[med-svn] [plastimatch] 01/03: Imported Upstream version 1.6.3+dfsg

Greg Sharp gregsharp-guest at moszumanska.debian.org
Thu Jun 30 19:37:24 UTC 2016


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

gregsharp-guest pushed a commit to branch master
in repository plastimatch.

commit daaa737755bc8e926bb3f8b2da9572b04a545180
Author: Gregory C. Sharp <gregsharp.geo at yahoo.com>
Date:   Thu Jun 30 13:33:03 2016 -0400

    Imported Upstream version 1.6.3+dfsg
---
 CMakeLists.txt                                     |   250 +-
 Testing/CMakeLists.txt                             |   415 +-
 Testing/CTestCustom.cmake.in                       |    16 +-
 Testing/Data/plm-bsp-regularize-numeric.txt        |    11 +-
 Testing/Data/plm-reg-itk-similarity.txt            |    33 +
 Testing/Data/plm-reg-multi-b.txt                   |    15 +
 Testing/Data/proton-dose-1.txt                     |    49 +-
 Testing/Data/proton-dose-2.txt                     |     6 +-
 Testing/Data/proton-dose-3.txt                     |    72 +-
 Testing/Data/proton-dose-4.txt                     |    46 +-
 Testing/Data/proton-dose-5a.txt                    |    18 +
 Testing/Data/proton-dose-5g.txt                    |    18 +
 Testing/Data/proton-dose-6a.txt                    |    18 +
 Testing/Data/speedtest-a.txt                       |    15 +
 Testing/Data/wed-a.txt                             |    19 +
 Testing/Data/wed-b.txt                             |    19 +
 Testing/Data/wed-c.txt                             |    18 +
 Testing/PlmSpeedTest.cmake                         |    17 +
 cmake/CheckEpsilon.cmake                           |     1 -
 cmake/CheckQt.cmake                                |    19 +
 cmake/FindCUDA_wrap.cmake                          |     6 +
 cmake/PlmMacros.cmake                              |    14 +-
 cmake/test_qt.cxx                                  |    17 +
 doc/NOTES.TXT                                      |  1447 ++
 doc/README.TXT                                     |     7 +
 doc/ROADMAP.TXT                                    |    52 +
 doc/STYLE_GUIDE_1.TXT                              |   172 +
 doc/STYLE_GUIDE_2.TXT                              |   174 +
 src/CMakeLists.txt                                 |    10 +-
 src/plastimatch/CHANGELOG.TXT                      |    17 +
 src/plastimatch/CMakeLists.txt                     |   193 +-
 src/plastimatch/base/CMakeLists.txt                |   229 +-
 src/plastimatch/base/aperture.cxx                  |   127 +-
 src/plastimatch/base/aperture.h                    |     6 +-
 src/plastimatch/base/bspline_macros.h              |    22 +-
 src/plastimatch/base/bspline_xform.cxx             |   296 +-
 src/plastimatch/base/bspline_xform.h               |    32 +-
 src/plastimatch/base/cxt_extract.cxx               |     6 +-
 src/plastimatch/base/dcmtk_file.cxx                |    13 +
 src/plastimatch/base/dcmtk_file.h                  |     2 +
 src/plastimatch/base/dcmtk_image.cxx               |   284 +-
 src/plastimatch/base/dcmtk_rt_study.cxx            |     7 +-
 src/plastimatch/base/dcmtk_rtdose.cxx              |    35 +-
 src/plastimatch/base/dcmtk_rtss.cxx                |     5 +-
 src/plastimatch/base/dcmtk_sro.cxx                 |    94 +-
 src/plastimatch/base/direction_cosines.cxx         |     4 -
 src/plastimatch/{util => base}/float_pair_list.cxx |     0
 src/plastimatch/{util => base}/float_pair_list.h   |     4 +-
 src/plastimatch/base/gdcm1_dose.cxx                |    27 +-
 src/plastimatch/base/hnd_io.cxx                    |     2 +-
 src/plastimatch/base/itk_dicom_save.h              |     1 -
 src/plastimatch/base/itk_image.cxx                 |    19 +-
 src/plastimatch/base/itk_image.h                   |    11 +-
 src/plastimatch/base/itk_image_create.cxx          |    21 +-
 src/plastimatch/base/itk_image_load.txx            |     1 +
 src/plastimatch/base/itk_image_load_char.cxx       |     2 -
 src/plastimatch/base/itk_image_load_double.cxx     |     2 -
 src/plastimatch/base/itk_image_load_float.cxx      |     2 -
 src/plastimatch/base/itk_image_load_int32.cxx      |     2 -
 src/plastimatch/base/itk_image_load_short.cxx      |     2 -
 src/plastimatch/base/itk_image_load_uchar.cxx      |     2 -
 src/plastimatch/base/itk_image_load_uint32.cxx     |     2 -
 src/plastimatch/base/itk_image_load_ushort.cxx     |     2 -
 src/plastimatch/base/itk_image_origin.cxx          |    53 +
 .../base/{plm_image_p.h => itk_image_origin.h}     |    14 +-
 src/plastimatch/base/itk_image_region.cxx          |    55 +
 .../base/{plm_image_p.h => itk_image_region.h}     |    14 +-
 src/plastimatch/base/itk_image_save.cxx            |     7 -
 src/plastimatch/base/itk_image_save.h              |    11 +-
 src/plastimatch/base/itk_image_type.h              |     3 -
 src/plastimatch/base/itk_resample.cxx              |    35 +-
 src/plastimatch/base/itk_volume_header.cxx         |    25 +-
 src/plastimatch/base/itk_volume_header.h           |     2 +-
 src/plastimatch/base/metadata.cxx                  |    17 +
 src/plastimatch/base/metadata.h                    |    14 +-
 src/plastimatch/base/nki_io.cxx                    |     6 +-
 src/plastimatch/base/plm_image.cxx                 |    38 +-
 src/plastimatch/base/plm_image.h                   |     3 +-
 src/plastimatch/base/plm_image_convert.cxx         |     6 +
 src/plastimatch/base/plm_image_header.cxx          |   381 +-
 src/plastimatch/base/plm_image_header.h            |   142 +-
 src/plastimatch/base/plm_image_p.h                 |     1 +
 src/plastimatch/base/plm_image_type.cxx            |    44 +-
 src/plastimatch/base/plm_image_type.h              |     3 +-
 src/plastimatch/base/{plm_image_p.h => plm_itk.h}  |    18 +-
 src/plastimatch/base/pointset.cxx                  |    10 +
 src/plastimatch/base/pointset.h                    |     4 +
 src/plastimatch/base/proj_image.cxx                |    35 +-
 src/plastimatch/base/proj_image.h                  |    25 +-
 src/plastimatch/base/proj_matrix.cxx               |   110 +-
 src/plastimatch/base/proj_matrix.h                 |    31 +-
 src/plastimatch/base/proj_volume.cxx               |   222 +-
 src/plastimatch/base/proj_volume.h                 |    15 +-
 src/plastimatch/base/pwlut.cxx                     |    90 +
 src/plastimatch/base/pwlut.h                       |    38 +
 src/plastimatch/base/ray_data.h                    |    24 +-
 src/plastimatch/base/ray_trace.h                   |     3 +-
 src/plastimatch/base/ray_trace_exact.cxx           |    26 +-
 src/plastimatch/base/ray_trace_uniform.cxx         |    21 +-
 src/plastimatch/base/rpl_volume.cxx                |  1260 +-
 src/plastimatch/base/rpl_volume.h                  |    54 +-
 src/plastimatch/base/rt_study.cxx                  |    99 +-
 src/plastimatch/base/rt_study.h                    |    24 +-
 src/plastimatch/base/rt_study_metadata.cxx         |    83 +-
 src/plastimatch/base/rt_study_metadata.h           |    19 +-
 src/plastimatch/base/rtog_io.cxx                   |    12 +-
 src/plastimatch/base/rtss.cxx                      |    43 +-
 src/plastimatch/base/rtss_contour.h                |     2 +-
 src/plastimatch/base/rtss_roi.cxx                  |    13 +
 src/plastimatch/base/rtss_roi.h                    |     1 +
 src/plastimatch/base/segmentation.cxx              |   238 +-
 src/plastimatch/base/segmentation.h                |     4 +-
 src/plastimatch/base/slice_list.cxx                |     6 +-
 src/plastimatch/base/volume.cxx                    |    19 +-
 src/plastimatch/base/volume.h                      |    16 +-
 src/plastimatch/base/volume_fill.cxx               |    23 +
 .../base/{plm_image_p.h => volume_fill.h}          |    33 +-
 src/plastimatch/base/volume_macros.h               |    16 +
 src/plastimatch/base/xform.cxx                     |   297 +-
 src/plastimatch/base/xform.h                       |    14 +-
 src/plastimatch/base/xio_ct.h                      |     1 -
 src/plastimatch/base/xio_ct_transform.cxx          |    16 +-
 src/plastimatch/base/xio_demographic.cxx           |    11 +-
 src/plastimatch/base/xio_demographic.h             |     1 +
 src/plastimatch/base/xio_studyset.cxx              |   297 +-
 src/plastimatch/base/xio_studyset.h                |     3 +
 src/plastimatch/cli/pcmd_dice.cxx                  |    17 -
 src/plastimatch/cli/pcmd_filter.cxx                |    11 +-
 src/plastimatch/cli/pcmd_filter.h                  |     1 +
 src/plastimatch/cli/pcmd_gamma.cxx                 |    22 +-
 src/plastimatch/cli/pcmd_gamma.h                   |     4 +-
 src/plastimatch/cli/pcmd_mabs.cxx                  |    19 +-
 src/plastimatch/cli/pcmd_resample.cxx              |    21 +-
 src/plastimatch/cli/pcmd_stats.cxx                 |     4 +-
 src/plastimatch/cli/pcmd_synth.cxx                 |    12 +-
 src/plastimatch/cli/pcmd_warp.cxx                  |    72 +-
 src/plastimatch/cli/pcmd_warp_dij.cxx              |   448 +-
 src/plastimatch/cli/pcmd_xf_convert.cxx            |    49 +-
 src/plastimatch/cli/plastimatch_main.cxx           |     2 +-
 ....cmake.in => PlastimatchConfig-Legacy.cmake.in} |     0
 src/plastimatch/cmake/PlastimatchConfig.cmake.in   |    18 +-
 src/plastimatch/cuda/CMakeLists.txt                |     1 +
 src/plastimatch/dose/CMakeLists.txt                |     5 +-
 src/plastimatch/dose/bragg_curve.cxx               |    14 +-
 src/plastimatch/dose/bragg_curve.h                 |     7 -
 src/plastimatch/dose/dose_volume_functions.cxx     |   103 +-
 src/plastimatch/dose/dose_volume_functions.h       |     6 +-
 src/plastimatch/dose/particle_type.cxx             |     5 +
 src/plastimatch/dose/particle_type.h               |     5 +-
 src/plastimatch/dose/rt_beam.cxx                   |   638 +-
 src/plastimatch/dose/rt_beam.h                     |   146 +-
 src/plastimatch/dose/rt_depth_dose.cxx             |   138 +-
 src/plastimatch/dose/rt_depth_dose.h               |    18 +-
 src/plastimatch/dose/rt_dose.cxx                   |  1142 +-
 src/plastimatch/dose/rt_dose.h                     |    70 +-
 src/plastimatch/dose/rt_lut.cxx                    |   848 +-
 src/plastimatch/dose/rt_lut.h                      |    39 +-
 src/plastimatch/dose/rt_mebs.cxx                   |  1693 +++
 src/plastimatch/dose/rt_mebs.h                     |   152 +
 src/plastimatch/dose/rt_parms.cxx                  |   216 +-
 src/plastimatch/dose/rt_parms.h                    |     1 -
 src/plastimatch/dose/rt_plan.cxx                   |  1343 +-
 src/plastimatch/dose/rt_plan.h                     |    35 +-
 src/plastimatch/dose/rt_sigma.cxx                  |   371 +-
 src/plastimatch/dose/rt_sigma.h                    |    19 +-
 src/plastimatch/dose/rt_sobp.cxx                   |   780 -
 src/plastimatch/dose/rt_sobp.h                     |   143 -
 src/plastimatch/dose/rt_sobp_optimize.cxx          |   429 -
 src/plastimatch/dose/rt_sobp_p.cxx                 |   131 -
 src/plastimatch/dose/rt_sobp_p.h                   |    65 -
 src/plastimatch/dose/wed_parms.cxx                 |   211 +-
 src/plastimatch/dose/wed_parms.h                   |    36 +-
 src/plastimatch/opencl/CMakeLists.txt              |     1 +
 src/plastimatch/plastimatch_version.txt            |     2 +-
 src/plastimatch/reconstruct/CMakeLists.txt         |     1 +
 src/plastimatch/reconstruct/cuda/CMakeLists.txt    |     1 +
 src/plastimatch/reconstruct/drr.h                  |    22 +-
 src/plastimatch/register/CMakeLists.txt            |     6 +-
 src/plastimatch/register/bspline.cxx               |   216 +-
 src/plastimatch/register/bspline.h                 |    15 +-
 src/plastimatch/register/bspline_gm.cxx            |    12 -
 src/plastimatch/register/bspline_gm.txx            |     2 +-
 src/plastimatch/register/bspline_landmarks.cxx     |     2 +-
 src/plastimatch/register/bspline_loop.txx          |    12 +-
 src/plastimatch/register/bspline_mi.cxx            |   115 +-
 src/plastimatch/register/bspline_mi.txx            |     4 +-
 src/plastimatch/register/bspline_mse.cxx           |   167 +-
 src/plastimatch/register/bspline_mse.txx           |     2 +-
 .../register/bspline_optimize_lbfgsb.cxx           |     2 +-
 .../register/bspline_optimize_liblbfgs.cxx         |     2 +-
 .../register/bspline_optimize_nlopt.cxx            |     2 +-
 .../register/bspline_optimize_steepest.cxx         |    32 +-
 src/plastimatch/register/bspline_parms.cxx         |    16 +-
 src/plastimatch/register/bspline_parms.h           |     4 +-
 src/plastimatch/register/bspline_regularize.cxx    |     6 +-
 src/plastimatch/register/bspline_regularize.h      |    20 +-
 .../register/bspline_regularize_analytic.cxx       |    22 +-
 .../register/bspline_regularize_numeric.cxx        |    44 +-
 .../register/bspline_regularize_semi_analytic.cxx  |    15 +-
 src/plastimatch/register/bspline_score.cxx         |   136 +-
 src/plastimatch/register/bspline_score.h           |    59 +-
 src/plastimatch/register/bspline_stage.cxx         |     5 +
 src/plastimatch/register/bspline_state.cxx         |    14 +-
 src/plastimatch/register/bspline_state.h           |    11 +-
 src/plastimatch/register/cuda/CMakeLists.txt       |     1 +
 src/plastimatch/register/cuda/bspline_cuda.cu      |     5 +-
 src/plastimatch/register/cuda/bspline_cuda.cxx     |    70 +-
 src/plastimatch/register/cuda/bspline_cuda.h       |     1 +
 src/plastimatch/register/itk_optimizer.cxx         |    39 +-
 src/plastimatch/register/itk_registration.cxx      |    20 +
 src/plastimatch/register/registration.cxx          |    37 +-
 src/plastimatch/register/registration.h            |     2 +
 .../register/registration_metric_type.cxx          |    28 +
 .../register/registration_metric_type.h            |     2 +
 src/plastimatch/register/registration_parms.cxx    |    42 +-
 src/plastimatch/register/registration_resample.cxx |     6 +
 src/plastimatch/register/shared_parms.h            |     1 +
 src/plastimatch/register/stage_parms.cxx           |    14 +
 src/plastimatch/register/stage_parms.h             |     5 +
 .../register/translation_grid_search.cxx           |     3 +
 src/plastimatch/segment/CMakeLists.txt             |     1 +
 src/plastimatch/segment/autolabel.cxx              |    12 +-
 src/plastimatch/segment/mabs.cxx                   |   334 +-
 src/plastimatch/segment/mabs.h                     |     9 +-
 src/plastimatch/segment/mabs_atlas_selection.cxx   |     6 +-
 src/plastimatch/segment/mabs_parms.cxx             |    10 +-
 src/plastimatch/segment/mabs_parms.h               |     1 +
 src/plastimatch/segment/plmsegment_config.h.in     |     4 -
 src/plastimatch/segment/segment_body.cxx           |   227 +-
 src/plastimatch/segment/segment_body.h             |     6 +-
 src/plastimatch/standalone/CMakeLists.txt          |   101 +-
 src/plastimatch/standalone/DlgGammaView.cxx        |    24 +
 src/plastimatch/standalone/DlgGammaView.h          |    33 +
 src/plastimatch/standalone/DlgGammaView.ui         |  1980 +++
 src/plastimatch/standalone/Launch_cmd_prompt.bat   |    24 +
 src/plastimatch/standalone/YK16GrayImage.cxx       |  2761 ++++
 src/plastimatch/standalone/YK16GrayImage.h         |   299 +
 src/plastimatch/standalone/YKThreadRegi.cpp        |    93 +
 src/plastimatch/standalone/YKThreadRegi.h          |    28 +
 src/plastimatch/standalone/bspline_main.cxx        |     5 +-
 src/plastimatch/standalone/bspline_opts.cxx        |     3 +-
 src/plastimatch/standalone/check_grad.cxx          |    51 +-
 src/plastimatch/standalone/cmd_prompt_launcher.cxx |    40 +-
 .../standalone/colormap_customgamma.txt            |   100 +
 src/plastimatch/standalone/colormap_jet.txt        |    64 +
 src/plastimatch/standalone/create_wed_volumes.cxx  |   107 -
 src/plastimatch/standalone/drr_main.cxx            |    17 +-
 src/plastimatch/standalone/drr_opts.cxx            |    35 +-
 src/plastimatch/standalone/extract_contour.cxx     |   114 -
 src/plastimatch/standalone/gamma_gui.cpp           |  3353 +++++
 src/plastimatch/standalone/gamma_gui.h             |   200 +
 src/plastimatch/standalone/gamma_gui.ui            |  1628 ++
 ...i2mha_converter_main.cpp => gamma_gui_main.cpp} |    20 +-
 src/plastimatch/standalone/hnd_to_pfm.cxx          |     2 +-
 src/plastimatch/standalone/nki2mha_converter.cpp   |    22 +-
 src/plastimatch/standalone/nki2mha_converter.h     |   114 +-
 src/plastimatch/standalone/nki2mha_converter.ui    |   363 +-
 .../standalone/nki2mha_converter_main.cpp          |    20 +-
 src/plastimatch/standalone/qcustomplot.cpp         | 15034 +++++++++++++++++++
 src/plastimatch/standalone/qcustomplot.h           |  2171 +++
 src/plastimatch/standalone/qt_util.cxx             |  1436 ++
 src/plastimatch/standalone/qt_util.h               |    74 +
 src/plastimatch/standalone/qyklabel.cpp            |   459 +
 src/plastimatch/standalone/qyklabel.h              |    91 +
 src/plastimatch/standalone/register_gui.cpp        |  2357 +++
 src/plastimatch/standalone/register_gui.h          |   201 +
 src/plastimatch/standalone/register_gui.ui         |  1209 ++
 ...ha_converter_main.cpp => register_gui_main.cpp} |    20 +-
 src/plastimatch/standalone/shuffle_mha_main.cxx    |     2 +-
 src/plastimatch/standalone/sobp_main.cxx           |   188 +-
 src/plastimatch/standalone/wed_main.cxx            |   556 +-
 src/plastimatch/standalone/xf_to_empirefmt.cxx     |   262 +-
 src/plastimatch/standalone/xvi_archive.cxx         |   350 +-
 src/plastimatch/standalone/yk_config.h             |   149 +
 src/plastimatch/sys/CMakeLists.txt                 |     7 +-
 src/plastimatch/sys/file_util.cxx                  |    46 +-
 src/plastimatch/sys/file_util.h                    |     7 +-
 src/plastimatch/sys/path_util.cxx                  |    10 +
 src/plastimatch/sys/path_util.h                    |     2 +
 src/plastimatch/sys/plm_config.h.in                |    46 +-
 src/plastimatch/sys/plm_math.h                     |     6 +
 src/plastimatch/sys/print_and_exit.cxx             |     2 +-
 src/plastimatch/sys/string_util.cxx                |    54 +-
 src/plastimatch/sys/string_util.h                  |     6 +
 src/plastimatch/test/itk_test.cxx                  |     9 +-
 src/plastimatch/test/itk_test_directions.cxx       |     1 +
 src/plastimatch/util/CMakeLists.txt                |     5 +-
 src/plastimatch/util/dice_statistics.cxx           |    11 +-
 src/plastimatch/util/dvh.cxx                       |    22 -
 src/plastimatch/util/gamma_dose_comparison.cxx     |   153 +-
 src/plastimatch/util/gamma_dose_comparison.h       |    38 +-
 src/plastimatch/util/geometry_chooser.cxx          |     3 +
 src/plastimatch/util/hausdorff_distance.cxx        |    10 +-
 src/plastimatch/util/image_boundary.cxx            |     5 +-
 src/plastimatch/util/image_center.cxx              |    99 +
 src/plastimatch/util/image_center.h                |    45 +
 src/plastimatch/util/itk_adjust.cxx                |    11 +
 src/plastimatch/util/itk_mask.cxx                  |     2 +
 src/plastimatch/util/rt_study_warp.cxx             |    29 +-
 src/plastimatch/util/synthetic_mha.cxx             |    10 +-
 src/plastimatch/util/warp_parms.h                  |    10 +-
 301 files changed, 47231 insertions(+), 9543 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7427bb1..b447126 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,12 +8,12 @@ project (plastimatch)
 ## The version here should be equal to the "next release"
 set (PLM_VERSION_MAJOR "1")
 set (PLM_VERSION_MINOR "6")
-set (PLM_VERSION_PATCH "2")
+set (PLM_VERSION_PATCH "3")
 
 ##-----------------------------------------------------------------------------
 ##  Set up CMake defaults
 ##-----------------------------------------------------------------------------
-cmake_minimum_required (VERSION 2.6.0)
+cmake_minimum_required (VERSION 2.8.12)
 
 # CMP0003: Libraries linked via full path no longer produce linker search
 #  paths.
@@ -28,9 +28,7 @@ endif ()
 # Note: it is ok to use CMake FindZLIB for 2.8.4.  Therefore setting 
 # policy CMP0017 to NEW is safe.  But we do want the OLD policy for 
 # older CMake versions.
-if (NOT ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} VERSION_LESS 2.8.4)
-  cmake_policy (SET CMP0017 NEW)
-endif ()
+cmake_policy (SET CMP0017 NEW)
 
 # Default to release
 if (NOT CMAKE_BUILD_TYPE)
@@ -40,6 +38,67 @@ if (NOT CMAKE_BUILD_TYPE)
 endif ()
 
 ##-----------------------------------------------------------------------------
+##  Define plastimatch configuration variables
+##-----------------------------------------------------------------------------
+option (PLM_CONFIG_DISABLE_CUDA "Set to ON to build without CUDA" OFF)
+option (PLM_CONFIG_DISABLE_DCMTK "Set to ON to build without DCMTK" OFF)
+option (PLM_CONFIG_DISABLE_FATM "Set to ON to build without FATM" ON)
+option (PLM_CONFIG_DISABLE_ISE "Build ISE fluoro program" ON)
+option (PLM_CONFIG_DISABLE_MONDOSHOT "Disable building mondoshot" ON)
+option (PLM_CONFIG_DISABLE_OPENCL "Set to ON to build without OpenCL" OFF)
+option (PLM_CONFIG_DISABLE_OPENMP "Set to ON to build without OpenMP" OFF)
+option (PLM_CONFIG_DISABLE_PLASTIMATCH "Disable building plastimatch" OFF)
+option (PLM_CONFIG_DISABLE_QT "Set to ON to build without QT" OFF)
+option (PLM_CONFIG_DISABLE_REG23 "Disable building REG-2-3" OFF)
+option (PLM_CONFIG_DISABLE_SSE2 "Set to ON to build without SSE" OFF)
+option (PLM_CONFIG_ENABLE_MATLAB "Set to ON to build Matlab plugins" OFF)
+set (PLM_CONFIG_VERSION_STRING "" CACHE STRING "Version string")
+option (PLM_CONFIG_LIBRARY_BUILD "Set to ON to build only libraries" OFF)
+option (PLM_CONFIG_DEBIAN_BUILD "Set to ON to configure build for debian" OFF)
+option (PLM_CONFIG_BUILD_QT_PLUGINS "Build QT4 Designer Plugins?" OFF)
+
+# Compile and link options
+option (PLM_SUPERBUILD "Download missing external libraries" OFF)
+option (PLM_PREFER_EXTERNAL_ITK "Prefer external ITK to local one" OFF)
+option (BUILD_SHARED_LIBS "Build plastimatch as shared library" OFF)
+
+# GCS 2012-03-15: We prefer f2c over fortran because bspline optimization
+# fails when compiling fortran in debug mode with gcc 4.5.0
+option (PLM_PREFER_F2C "Prefer using f2c over native fortran compiler" ON)
+option (PLM_PREFER_SYSTEM_F2C "Prefer the system f2c over the included f2c" ON)
+option (PLM_PREFER_NO_FORTRAN_NO_F2C "Prefer no fortran, no f2c" ON)
+
+# Testing
+option (PLM_BUILD_TESTING "Enable regression testing" ON)
+
+# Installer Options
+option (PLM_INSTALL_RPATH "Add full RPATH to install" OFF)
+option (PLM_CONFIG_INSTALL_LIBRARIES "Include libraries in install" ON)
+
+# Packaging
+option (PLM_PACKAGE_32BIT
+    "Set this when building 32-bit packages on a 64-bit machine" OFF)
+option (PLM_PACKAGE_NSIS "Set to ON when packaging binaries with NSIS" OFF)
+option (PLM_PACKAGE_WIX "Set to ON when packaging binaries with WIX" OFF)
+option (PLM_PACKAGE_LEGACY_CMAKE_CONFIG
+  "Use the old code for creating PlastimatchConfig.cmake and friends" OFF)
+
+# Use legacy packaging if cmake version is < 3.0
+if (CMAKE_VERSION VERSION_LESS "3.0")
+  set (PLM_PACKAGE_LEGACY_CMAKE_CONFIG ON CACHE BOOL)
+endif ()
+
+# Override some options if library build is selected
+if (PLM_CONFIG_LIBRARY_BUILD)
+  set (PLM_CONFIG_DISABLE_ISE ON)
+  set (PLM_CONFIG_DISABLE_FATM ON)
+  set (PLM_CONFIG_DISABLE_MONDOSHOT ON)
+  set (PLM_CONFIG_DISABLE_REG23 ON)
+  set (PLM_CONFIG_PREFER_F2C ON)
+  set (PLM_PREFER_SYSTEM_F2C OFF)
+endif ()
+  
+##-----------------------------------------------------------------------------
 ##  SETUP IMPORTANT LOCATIONS
 ##-----------------------------------------------------------------------------
 set (PLM_BUILD_ROOT "${CMAKE_CURRENT_BINARY_DIR}")
@@ -50,7 +109,7 @@ set (PLM_INSTALL_LIB_DIR lib CACHE PATH
   "Installation directory for libraries")
 set (PLM_INSTALL_BIN_DIR bin CACHE PATH 
   "Installation directory for executables")
-set (PLM_INSTALL_INCLUDE_DIR include CACHE PATH
+set (PLM_INSTALL_INCLUDE_DIR include/plastimatch CACHE PATH
   "Installation directory for header files")
 if (WIN32 AND NOT CYGWIN)
   set (DEF_INSTALL_CMAKE_DIR CMake)
@@ -75,12 +134,14 @@ mark_as_advanced (
 # Needs further investigation
 
 # Make relative install paths absolute (needed later on)
+if (PLM_PACKAGE_LEGACY_CMAKE_CONFIG)
 foreach (p LIB BIN INCLUDE CMAKE)
   set (var PLM_INSTALL_${p}_DIR)
   if (NOT IS_ABSOLUTE "${${var}}")
     set (${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
   endif ()
 endforeach ()
+endif ()
 
 ##-----------------------------------------------------------------------------
 ##  CMake include files
@@ -108,7 +169,7 @@ endif ()
 include (cmake/language_support.cmake)
 
 # Superbuild
-if (NOT ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 2.8)
+if (PLM_SUPERBUILD)
   include (ExternalProject)
 endif ()
 
@@ -119,68 +180,6 @@ include (cmake/CheckEpsilon)
 include (cmake/CheckCharSign)
 
 ##-----------------------------------------------------------------------------
-##  Plastimatch configuration variables
-##-----------------------------------------------------------------------------
-option (PLM_CONFIG_DISABLE_CUDA "Set to ON to build without CUDA" OFF)
-option (PLM_CONFIG_DISABLE_DCMTK "Set to ON to build without DCMTK" OFF)
-option (PLM_CONFIG_DISABLE_FATM "Set to ON to build without FATM" OFF)
-option (PLM_CONFIG_DISABLE_MONDOSHOT "Disable building mondoshot" ON)
-option (PLM_CONFIG_DISABLE_OPENCL "Set to ON to build without OpenCL" OFF)
-option (PLM_CONFIG_DISABLE_OPENMP "Set to ON to build without OpenMP" OFF)
-option (PLM_CONFIG_DISABLE_PLASTIMATCH "Disable building plastimatch" OFF)
-option (PLM_CONFIG_DISABLE_QT "Set to ON to build without QT" OFF)
-option (PLM_CONFIG_DISABLE_REG23 "Disable building REG-2-3" OFF)
-option (PLM_CONFIG_DISABLE_SSE2 "Set to ON to build without SSE" OFF)
-option (PLM_CONFIG_ENABLE_MATLAB "Set to ON to build Matlab plugins" OFF)
-set (PLM_CONFIG_VERSION_STRING "" CACHE STRING "Version string")
-option (PLM_CONFIG_LIBRARY_BUILD "Set to ON to build only libraries" OFF)
-option (PLM_CONFIG_DEBIAN_BUILD "Set to ON to configure build for debian" OFF)
-option (PLM_CONFIG_BUILD_QT_PLUGINS "Build QT4 Designer Plugins?" OFF)
-
-# Optional components
-option (PLM_BUILD_ISE "Build ISE fluoro program" OFF)
-
-# Compile and link options
-option (PLM_SUPERBUILD
-  "Download missing external libraries" OFF)
-option (PLM_PREFER_EXTERNAL_ITK
-  "Prefer external ITK to local one" OFF)
-option (BUILD_SHARED_LIBS 
-  "Build plastimatch as shared library" OFF)
-# GCS 2012-03-15: We prefer f2c over fortran because bspline optimization
-# fails when compiling fortran in debug mode with gcc 4.5.0
-option (PLM_PREFER_F2C 
-  "Prefer using f2c over native fortran compiler" ON)
-option (PLM_PREFER_SYSTEM_F2C 
-  "Prefer the system f2c over the included f2c" ON)
-option (PLM_PREFER_NO_FORTRAN_NO_F2C
-  "Prefer no fortran, no f2c" ON)
-
-# Testing
-option (PLM_BUILD_TESTING 
-  "Enable regression testing" ON)
-
-# Installer Options
-option (PLM_INSTALL_RPATH 
-  "Add full RPATH to install" OFF)
-option (PLM_CONFIG_INSTALL_LIBRARIES 
-  "Include libraries in install" ON)
-option (PLM_PACKAGE_32BIT 
-  "Set this when building 32-bit packages on a 64-bit machine" OFF)
-option (PLM_PACKAGE_NSIS 
-  "Set to ON when packaging binaries with NSIS" OFF)
-
-# Update options if library build is selected  
-if (PLM_CONFIG_LIBRARY_BUILD)
-  set (PLM_BUILD_ISE OFF)
-  set (PLM_CONFIG_DISABLE_FATM ON)
-  set (PLM_CONFIG_DISABLE_MONDOSHOT ON)
-  set (PLM_CONFIG_DISABLE_REG23 ON)
-  set (PLM_CONFIG_PREFER_F2C ON)
-  set (PLM_PREFER_SYSTEM_F2C OFF)
-endif ()
-  
-##-----------------------------------------------------------------------------
 ##  Figure out plastimatch version number
 ##-----------------------------------------------------------------------------
 # For ordinary versions from svn, use the version number at the 
@@ -743,13 +742,23 @@ if (PLM_CONFIG_DISABLE_QT)
     set (QT4_FOUND false)
 else ()
     #Edited by YPark, in VS2013 with win7, QtWebkit was not able to be compiled.(.lib file is not generated.) QtWebkit is only used by Reg23.
-   if (PLM_CONFIG_DISABLE_REG23)
-       message (STATUS "Due to the REG23 disabled, QtWebkit is no longer needed")
-       find_package (Qt4 4.6.3 COMPONENTS QtCore QtGui QtDesigner)
-   else ()
-       message (STATUS "In VS2013 w/ Qt4.8.6, QtWebkit couldn't be compiled. Check PLM_CONFIG_DISABLE_REG23 if you don't want REG23 and there is compiling error.")
-       find_package (Qt4 4.6.3 COMPONENTS QtCore QtGui QtWebKit QtDesigner)
-   endif ()	
+    if (PLM_CONFIG_DISABLE_REG23)
+	message (STATUS "Due to the REG23 disabled, QtWebkit is no longer needed")
+	find_package (Qt4 4.6.3 COMPONENTS QtCore QtGui QtDesigner)
+    else ()
+	message (STATUS "In VS2013 w/ Qt4.8.6, QtWebkit couldn't be compiled. Check PLM_CONFIG_DISABLE_REG23 if you don't want REG23 and there is compiling error.")
+	find_package (Qt4 4.6.3 COMPONENTS QtCore QtGui QtWebKit QtDesigner)
+    endif ()	
+endif ()
+
+if (QT4_FOUND)
+   # Test Qt install to make sure it can build and run a test program
+   include (cmake/CheckQt)
+   check_qt (QT_TEST_COMPILE_SUCCEEDED)
+   if (NOT QT_TEST_COMPILE_SUCCEEDED)
+       message (STATUS "Qt failed to compile a test program")
+       set (QT4_FOUND false)
+   endif ()
 endif ()
 
 if (QT4_FOUND)
@@ -817,9 +826,10 @@ add_subdirectory (src)
 ##  Additional install files
 ##-----------------------------------------------------------------------------
 if (ITK_FOUND)
-  if (EXISTS "${ITK_DIR}/bin/release/ITKCommon.dll")
-    install (FILES "${ITK_DIR}/bin/release/ITKCommon.dll" DESTINATION bin)
-  endif ()
+    file (GLOB DLLFILES "${ITK_DIR}/bin/release/*.dll")
+    if (DLLFILES)
+      install (FILES ${DLLFILES} DESTINATION bin)
+    endif ()
 endif ()
 
 if (CUDA_FOUND)
@@ -853,15 +863,25 @@ if (CUDA_FOUND)
       )
     endif ()
   else ()
-    set (CUDART_WIN32 "${CUDA_TOOLKIT_ROOT_DIR}/bin/cudart32_30_14.dll")
-    set (CUDART_WIN64 "${CUDA_TOOLKIT_ROOT_DIR}/bin/cudart64_30_14.dll")
+#    set (CUDART_WIN32 "${CUDA_TOOLKIT_ROOT_DIR}/bin/cudart32_30_14.dll")
+#    set (CUDART_WIN64 "${CUDA_TOOLKIT_ROOT_DIR}/bin/cudart64_30_14.dll")
+#05/27/2016 YKP (for window users only): will copy dll files to bin for packaging.
+    set (CUDART_WIN32 "${CUDA_TOOLKIT_ROOT_DIR}/bin/cudart32_65.dll")
+    set (CUDART_WIN64 "${CUDA_TOOLKIT_ROOT_DIR}/bin/cudart64_65.dll")
+    set (CUFFT_WIN32 "${CUDA_TOOLKIT_ROOT_DIR}/bin/cufft32_65.dll")
+    set (CUFFT_WIN64 "${CUDA_TOOLKIT_ROOT_DIR}/bin/cufft64_65.dll")
 
     if (MACHINE_IS_32_BIT AND EXISTS "${CUDART_WIN32}")
       set (CUDART_FILE "${CUDART_WIN32}")
     endif ()
-
-    if (MACHINE_IS_64_BIT AND EXISTS "${CUDART_WIN64}")
-      set (CUDART_FILE "${CUDART_WIN64}")
+    if (MACHINE_IS_32_BIT AND EXISTS "${CUFFT_WIN32}")
+      set (CUFFT_FILE "${CUFFT_WIN32}")
+    endif ()
+    if (MACHINE_IS_64_BIT AND EXISTS "${CUDART_WIN64}")	
+      set (CUDART_FILE "${CUDART_WIN64}")	  
+    endif ()
+    if (MACHINE_IS_64_BIT AND EXISTS "${CUFFT_WIN64}")		
+      set (CUFFT_FILE "${CUFFT_WIN64}")	  
     endif ()
 
     # Override for packagers building 32-bit packages on 64-bit machine
@@ -877,12 +897,47 @@ if (CUDA_FOUND)
       install (FILES "${CUDART_FILE}" DESTINATION bin)
     endif ()
   endif ()
+
+  if (EXISTS "${CUFFT_FILE}")
+    if (UNIX)
+      install (FILES "${CUFFT_FILE}" DESTINATION lib)
+    else ()
+      install (FILES "${CUFFT_FILE}" DESTINATION bin)
+    endif ()
+  endif ()
+endif ()
+
+#Add QT dlls to Install
+if (QT4_FOUND)
+  if (UNIX)
+    # YKP: should be implemented soon
+  else ()	
+    set (QT4_CORE_DLL_WIN "${QT_LIBRARY_DIR}/QtCore4.dll")
+    set (QT4_GUI_DLL_WIN "${QT_LIBRARY_DIR}/QtGui4.dll")
+    if (EXISTS "${QT4_CORE_DLL_WIN}")
+		install (FILES "${QT4_CORE_DLL_WIN}" DESTINATION bin)
+    endif ()
+	if (EXISTS "${QT4_GUI_DLL_WIN}")
+		install (FILES "${QT4_GUI_DLL_WIN}" DESTINATION bin)
+    endif ()
+  endif ()
 endif ()
 
+#Add FFT dlls to Install
 if (FFTW_FOUND)
   if (EXISTS "${FFTW_DIR}/libfftw3-3.dll")
-    install (FILES "${FFTW_DIR}/libfftw3-3.dll" DESTINATION bin)
+    install (FILES "${FFTW_DIR}/libfftw3-3.dll" DESTINATION bin)	
   endif ()
+    #YKP 05/27/2016: no need of libfftw3f-3.dll and libfftw3l-3.dll?
+endif ()
+
+# Add sample directory/files to Install Only for windows users
+if (WIN32 OR WIN64)
+  set (PLM_WINDOWS_INSTALL_DIR "${CMAKE_SOURCE_DIR}/extra/windows-install")
+  install (DIRECTORY "${PLM_WINDOWS_INSTALL_DIR}/sample" DESTINATION bin)
+  install (FILES "${PLM_WINDOWS_INSTALL_DIR}/colormap_customgamma.txt}" DESTINATION bin)
+  install (FILES "${PLM_WINDOWS_INSTALL_DIR}/colormap_jet.txt}" DESTINATION bin)
+  install (FILES "${PLM_WINDOWS_INSTALL_DIR}/launch_command_prompt.txt}" DESTINATION bin)
 endif ()
 
 # JAS 2011.01.24
@@ -934,6 +989,7 @@ endif ()
 ##    avoid this (i.e. no good suggestions on CMake/CTest email list.  
 ##    This is the purpose of the PATH_HACK code below.
 ##-----------------------------------------------------------------------------
+if (ITK_FOUND)
 if (WIN32 AND NOT CYGWIN AND NOT MINGW)
   set (PLM_PLASTIMATCH_PATH 
     ${CMAKE_CURRENT_BINARY_DIR}/Release)
@@ -964,6 +1020,7 @@ else ()
   set (PLM_PLASTIMATCH_PATH_HACK "")
   set (PLM_FFTW_PATH_HACK "")
 endif ()
+endif ()
 
 macro (PLM_ADD_TEST PLM_TEST_NAME PLM_TEST_COMMAND PARMS)
   # Optional extra parameters are passed through ${ARGN}
@@ -1024,16 +1081,24 @@ endif ()
 ##    ...from within your build directory
 ##-----------------------------------------------------------------------------
 # Choose generator
-if (PLM_PACKAGE_NSIS)
+if (PLM_PACKAGE_WIX)
+  set (CPACK_GENERATOR "WIX")
+elseif (PLM_PACKAGE_NSIS)
   set (CPACK_GENERATOR "NSIS")
 else ()
   set (CPACK_GENERATOR "ZIP")
 endif ()
 set (CPACK_SOURCE_GENERATOR "TBZ2")
 
+# For WiX install, and possibly for debian -dev targets,
+# we should properly set up the install components.
+# Until then, let's just kill it
+set (CPACK_MONOLITHIC_INSTALL 1)
+
 # General stuff
 set (CPACK_PACKAGE_CONTACT "plastimatch at googlegroups.com")
 set (CPACK_PACKAGE_NAME "plastimatch")
+set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Plastimatch - A Medical Imaging Application")
 set (CPACK_PACKAGE_VENDOR "Plastimatch Dev Team")
 set (CPACK_PACKAGE_VERSION_MAJOR "${PLM_VERSION_MAJOR}")
 set (CPACK_PACKAGE_VERSION_MINOR "${PLM_VERSION_MINOR}")
@@ -1046,12 +1111,15 @@ set (CPACK_SOURCE_IGNORE_FILES
 
 # NSIS stuff
 #set(CPACK_NSIS_INSTALLED_ICON_NAME "${APP_LOW_NAME}.ico")
-set(CPACK_NSIS_HELP_LINK "http://plastimatch.org")
-set(CPACK_NSIS_URL_INFO_ABOUT "http://plastimatch.org")
-set(CPACK_NSIS_CONTACT "plastimatch at googlegroups.com")
-
+set (CPACK_NSIS_HELP_LINK "http://plastimatch.org")
+set (CPACK_NSIS_URL_INFO_ABOUT "http://plastimatch.org")
+set (CPACK_NSIS_CONTACT "plastimatch at googlegroups.com")
 set (PLM_NSIS_VERSION_STRING "${PLM_VERSION_MAJOR}.${PLM_VERSION_MINOR}.${PLM_VERSION_PATCH} (${PLASTIMATCH_SVN_VERSION})")
 
+# WIX stuff
+set (CPACK_WIX_HELP_LINK "http://plastimatch.org")
+set (CPACK_WIX_UPGRADE_GUID "AA7C7964-14D7-4890-9CD1-EA1D80E4DC8C")
+
 # Write files to extra_stuff directory.  These is sets the release date 
 # and version number correctly into the changelog, without needing 
 # to commit these changes into the svn repository.
@@ -1080,7 +1148,7 @@ set (CPACK_SOURCE_INSTALLED_DIRECTORIES
 #set (CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
 #set (CPACK_PACKAGING_INSTALL_PREFIX "")
 
-if (NOT CPACK_GENERATOR STREQUAL "NSIS")
+if (CPACK_GENERATOR STREQUAL "ZIP")
   set (CPACK_SET_DESTDIR ON)
 endif ()
 set (CPACK_INSTALL_PREFIX "")
diff --git a/Testing/CMakeLists.txt b/Testing/CMakeLists.txt
index f693988..78a7210 100644
--- a/Testing/CMakeLists.txt
+++ b/Testing/CMakeLists.txt
@@ -48,6 +48,7 @@ set (CONFIG_FILE_LIST
   "plm-bsp-landmark-d.txt"
   "plm-bsp-landmark-e.txt"
   "plm-bsp-double.txt"
+  "plm-bsp-regularize-numeric.txt"
   "plm-reg-itk-translation.txt"
   "plm-reg-roi-a.txt"
   "plm-reg-roi-b.txt"
@@ -56,9 +57,11 @@ set (CONFIG_FILE_LIST
   "plm-reg-roi-e.txt"
   "plm-reg-stiffness-a.txt"
   "plm-reg-multi-a.txt"
+  "plm-reg-multi-b.txt"
   "plm-reg-dv-itk-translation.txt"
   "plm-reg-itk-rigid-a.txt"
   "plm-reg-itk-rigid-b.txt"
+  "plm-reg-itk-similarity.txt"
   "plm-reg-itk-bspline.txt"
   "plm-reg-process-a.txt"
   "plm-reg-trans-a.txt"
@@ -70,6 +73,13 @@ set (CONFIG_FILE_LIST
   "proton-dose-2.txt"
   "proton-dose-3.txt"
   "proton-dose-4.txt"
+  "proton-dose-5a.txt"
+  "proton-dose-5g.txt"
+  "proton-dose-6a.txt"
+  "speedtest-a.txt"
+  "wed-a.txt"
+  "wed-b.txt"
+  "wed-c.txt"
 )
 foreach (CONFIG_FILE ${CONFIG_FILE_LIST})
     string (REGEX REPLACE "\\.txt$" "" PLM_TEST_NAME ${CONFIG_FILE})
@@ -204,78 +214,19 @@ endif ()
 #     "${PLM_BUILD_TESTING_DIR}/20110509")
 # endif ()
 
-
 ## -------------------------------------------------------------------------
-## Copy plugin library
-##   GCS: N.b. I don't think this is necessary.  There is no need to 
-##   copy gpuit or plastimatch1 libraries.  Instead we set the extra path 
-##   as an argument to plm_add_test.
+## Custom test command
 ## -------------------------------------------------------------------------
-# JAS 2010.11.23
-# Tests will fail if the test binaries cannot find the cuda plugin.
-# So, we copy it into the testing directory.  This seems to work well.
-if (PLM_USE_GPU_PLUGINS)
-  # Because MSVC places stuff in Release/ or Debug/, etc
-  # CMAKE_CFG_INTDIR is "Release/" or "Debug/" for Windows or "/" for Linux
-  # In the Linux case, the extra "/" doesn't hurt anything.
-  set (GPU_PLUGIN_BIN_DIR
-    "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/")
-
-  if (CUDA_FOUND)
-    # CMAKE_SHARED_LIBRARY_PREFIX is "" under Windows and "lib" under Linux
-    # CMAKE_SHARE_LIBRARY_SUFFIX is ".dll" under Windows and ".so" under Linux
-    set (PLMREGISTERCUDA_FILE
-      "${CMAKE_SHARED_LIBRARY_PREFIX}plmregistercuda${CMAKE_SHARED_LIBRARY_SUFFIX}")
-
-    # This tells CMake how to handle a target depending on
-    # "Testing/[lib]plmcuda[.dll/.so]"
-    add_custom_command (
-      OUTPUT "${PLM_BUILD_TESTING_DIR}/${CUDA_PLUGIN_FILE}"
-      COMMAND
-      ${CMAKE_COMMAND} "-E" "copy"
-      "${GPU_PLUGIN_BIN_DIR}/${CUDA_PLUGIN_FILE}"
-      "${PLM_BUILD_TESTING_DIR}/${CUDA_PLUGIN_FILE}"
-      DEPENDS plmregistercuda
-      )
-
-    # The Phony target
-    # This is the Makefile equivalent of
-    # Testing/plmcuda.dll : plmcuda.dll      (Under Windows)  *OR*
-    # Testing/libplmcuda.so : libplmcuda.so  (Under Linux)
-    #
-    # The "rule" for handling this is the above custom command 
-    add_custom_target (plmregistercuda_tcpy
-      DEPENDS "${PLM_BUILD_TESTING_DIR}/${PLMREGISTER_FILE}"
-      )
-
-    # When using the plmregister cuda plugin, plmregister depends on it for GPU
-    # acceleration.  So, we add it as a dependency here so plmregistercuda will
-    # be copied to Testing whenever the user builds plmregister (or any target
-    # that depends on plmregister).
-    add_dependencies (plmregister plmcuda_tcpy)
-  endif ()
-
-#  if (OPENCL_FOUND)
-#    set (OPENCL_PLUGIN_FILE
-#      "${CMAKE_SHARED_LIBRARY_PREFIX}plmopencl${CMAKE_SHARED_LIBRARY_SUFFIX}")
-#
-#    add_custom_command (
-#      OUTPUT "${PLM_BUILD_TESTING_DIR}/${OPENCL_PLUGIN_FILE}"
-#      COMMAND
-#      ${CMAKE_COMMAND} "-E" "copy"
-#      "${GPU_PLUGIN_BIN_DIR}/${OPENCL_PLUGIN_FILE}"
-#      "${PLM_BUILD_TESTING_DIR}/${OPENCL_PLUGIN_FILE}"
-#      DEPENDS plmopencl
-#      )
-#
-#    add_custom_target (plmopencl_tcpy
-#      DEPENDS "${PLM_BUILD_TESTING_DIR}/${OPENCL_PLUGIN_FILE}"
-#      )
-#    add_dependencies (gpuit plmopencl_tcpy)
-#  endif ()
-endif ()
+add_custom_target (speedtest
+  ${CMAKE_COMMAND} 
+  -DPLM_PLASTIMATCH_PATH="${PLM_PLASTIMATCH_PATH}"
+  -DPLM_BUILD_TESTING_DIR="${PLM_BUILD_TESTING_DIR}"
+  -P "${PLM_TESTING_SOURCE_DIR}/PlmSpeedTest.cmake"
+  )
 
 ## -------------------------------------------------------------------------
+## Test cases
+## -------------------------------------------------------------------------
 ## Create synthetic images
 ##   black-1           All black, slightly different geometry
 ##   donut-1           Centered donut
@@ -292,6 +243,7 @@ endif ()
 ##   lung-1            Synthetic lung, tumor position 0
 ##   lung-2            Synthetic lung, tumor position -5
 ##   ptv-1             rect PTV target for dose calculation
+##   ptv-2             rect PTV target for dose calculation, smaller geometry
 ##   rect-1            Inverted rect, centered
 ##   rect-2            Standard rect, off-center
 ##   rect-3            Standard rect, centered
@@ -313,6 +265,8 @@ endif ()
 ##   rectarr-02        Two rectangles, lower rect offset
 ##   rectarr-m-01      Upper mask for rectarr-01 and rectarr-02
 ##   rectarr-m-02      Lower mask for rectarr-01 and rectarr-02
+##   rectarr-03        Two rectangles, both centered (for testing gamma)
+##   rectarr-04        Two rectangles, lower rect offset (for testing gamma)
 ##   roi-1             left = 0, right = 1
 ##   rgc-1             rect range compensator for dose calculation
 ##   sphere-1          Uchar sphere, bg=0, fg=255
@@ -395,12 +349,12 @@ plm_add_test (
 plm_add_test (
   "lung-1"
   ${PLM_PLASTIMATCH_PATH}/plastimatch
-  "synth;--output;${PLM_BUILD_TESTING_DIR}/lung-1.mha;--output-dicom;${PLM_BUILD_TESTING_DIR}/lung-1-dicom;--pattern;lung;--lung-tumor-pos;0"
+  "synth;--dicom-with-uids;false;--output;${PLM_BUILD_TESTING_DIR}/lung-1.mha;--output-prefix;${PLM_BUILD_TESTING_DIR}/lung-1-prefix;--output-dicom;${PLM_BUILD_TESTING_DIR}/lung-1-dicom;--pattern;lung;--lung-tumor-pos;0"
   )
 plm_add_test (
   "lung-2"
   ${PLM_PLASTIMATCH_PATH}/plastimatch
-  "synth;--output;${PLM_BUILD_TESTING_DIR}/lung-2.mha;--output-dicom;${PLM_BUILD_TESTING_DIR}/lung-1-dicom;--pattern;lung;--lung-tumor-pos;-5"
+  "synth;--dicom-with-uids;false;--output;${PLM_BUILD_TESTING_DIR}/lung-2.mha;--output-dicom;${PLM_BUILD_TESTING_DIR}/lung-2-dicom;--pattern;lung;--lung-tumor-pos;-5"
   )
 plm_add_test (
   "ptv-1"
@@ -408,6 +362,11 @@ plm_add_test (
   "synth;--output;${PLM_BUILD_TESTING_DIR}/ptv-1.mha;--pattern;rect;--dim;60 60 60;--background;0;--foreground;6;--rect-size;20 20 20;--origin;-150 -150 -150;--spacing;5 5 5"
   )
 plm_add_test (
+  "ptv-2"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "synth;--output;${PLM_BUILD_TESTING_DIR}/ptv-2.mha;--pattern;rect;--dim;40 40 40;--background;0;--foreground;6;--rect-size;20 20 20;--origin;-100 -100 -100;--spacing;5 5 5"
+  )
+plm_add_test (
   "rect-1"
   ${PLM_PLASTIMATCH_PATH}/plastimatch
   "synth;--dicom-with-uids;false;--output;${PLM_BUILD_TESTING_DIR}/rect-1.mha;--output-type;float;--output-dose-img;${PLM_BUILD_TESTING_DIR}/rect-1-dose-img.mha;--output-ss-img;${PLM_BUILD_TESTING_DIR}/rect-1-ss.mha;--output-ss-list;${PLM_BUILD_TESTING_DIR}/rect-1-ss-list.txt;--output-dicom;${PLM_BUILD_TESTING_DIR}/rect-1-dicom;--pattern;rect;--dim;${SYNTH_MHA_SIZE} ${SYNTH_MHA_SIZE} ${SYNTH_MHA_SIZE};--background;0;--foreground;-1000"
@@ -417,6 +376,13 @@ plm_add_test (
   ${PLM_PLASTIMATCH_PATH}/plastimatch
   "synth;--output;${PLM_BUILD_TESTING_DIR}/rect-2.mha;--output-type;float;--output-dicom;${PLM_BUILD_TESTING_DIR}/rect-2-dicom;--pattern;rect;--dim;${SYNTH_MHA_SIZE} ${SYNTH_MHA_SIZE} ${SYNTH_MHA_SIZE};--background;-1000;--foreground;0;--rect-size;-30 70 -50 50 -50 50"
   )
+
+plm_add_test (
+  "rect-2s"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "synth;--pattern;rect;--dim;${SYNTH_MHA_SIZE} ${SYNTH_MHA_SIZE} ${SYNTH_MHA_SIZE};--background;-1000;--foreground;0;--spacing;13.158 13.158 13.158;--rect-size; 200 200 200;--output;${PLM_BUILD_TESTING_DIR}/rect-2s.mha"
+  )
+
 plm_add_test (
   "rect-3"
   ${PLM_PLASTIMATCH_PATH}/plastimatch
@@ -547,6 +513,23 @@ plm_add_test (
 set_property (TEST rectarr-m-01 APPEND PROPERTY DEPENDS rectarr-01-a)
 set_property (TEST rectarr-m-02 APPEND PROPERTY DEPENDS rectarr-01-a)
 plm_add_test (
+  "rectarr-03-a"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "synth;--output;${PLM_BUILD_TESTING_DIR}/rectarr-03-a.mha;--output-type;float;--pattern;rect;--dim;30 50 30;--background;0;--foreground;70;--rect-size;-5 5 -15 -5 -5 5;--origin;-14.5 -24.5 -14.5;--spacing;1 1 1"
+  )
+plm_add_test (
+  "rectarr-03"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "synth;--input;${PLM_BUILD_TESTING_DIR}/rectarr-03-a.mha;--output;${PLM_BUILD_TESTING_DIR}/rectarr-03.mha;--output-type;float;--pattern;rect;--background;0;--foreground;50;--rect-size;-5 5 5 15 -5 5"
+  )
+plm_add_test (
+  "rectarr-04"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "synth;--input;${PLM_BUILD_TESTING_DIR}/rectarr-03-a.mha;--output;${PLM_BUILD_TESTING_DIR}/rectarr-04.mha;--output-type;float;--pattern;rect;--background;0;--foreground;50;--rect-size;-10 0 5 15 -5 5"
+  )
+set_property (TEST rectarr-03 APPEND PROPERTY DEPENDS rectarr-03-a)
+set_property (TEST rectarr-04 APPEND PROPERTY DEPENDS rectarr-03-a)
+plm_add_test (
   "rgc-1"
   ${PLM_PLASTIMATCH_PATH}/plastimatch
   "synth;--output;${PLM_BUILD_TESTING_DIR}/rgc-1.mha;--output-type;float;--pattern;rect;--dim;10 10 1;--background;0;--foreground;30;--rect-size;0 10 0 5 0 0;--origin;0 0 0;--spacing;1 1 1"
@@ -2135,6 +2118,8 @@ set_tests_properties (plm-mask-a-check PROPERTIES DEPENDS plm-mask-a-stats)
 ## -------------------------------------------------------------------------
 ## plm-gamma-a  centered and off-center rectangle, centered as reference
 ## plm-gamma-b  centered rectangles with different dosees
+## plm-gamma-c  analysis using dose threshold on both images
+## plm-gamma-d  analysis using dose threshold on ref only
 ## -------------------------------------------------------------------------
 plm_add_test (
   "plm-gamma-a"
@@ -2176,6 +2161,35 @@ set_tests_properties (plm-gamma-b PROPERTIES DEPENDS "rect-12;rect-13")
 set_tests_properties (plm-gamma-b-stats PROPERTIES DEPENDS plm-gamma-b)
 set_tests_properties (plm-gamma-b-check PROPERTIES DEPENDS plm-gamma-b-stats)
 
+plm_add_test (
+  "plm-gamma-c"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "gamma;--output;${PLM_BUILD_TESTING_DIR}/plm-gamma-c.mha;${PLM_BUILD_TESTING_DIR}/rectarr-03.mha;${PLM_BUILD_TESTING_DIR}/rectarr-04.mha"
+  )
+plmtest_check_interval ("plm-gamma-c-check"
+  "${PLM_BUILD_TESTING_DIR}/plm-gamma-c.stdout.txt"
+  "Pass rate = *([-0-9.]*)"
+  "90.6"
+  "90.8"
+  )
+set_tests_properties (plm-gamma-c PROPERTIES DEPENDS "rectarr-03;rectarr-04")
+set_tests_properties (plm-gamma-c-check PROPERTIES DEPENDS plm-gamma-c)
+
+plm_add_test (
+  "plm-gamma-d"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "gamma;--output;${PLM_BUILD_TESTING_DIR}/plm-gamma-d.mha;--ref-only-threshold;${PLM_BUILD_TESTING_DIR}/rectarr-03.mha;${PLM_BUILD_TESTING_DIR}/rectarr-04.mha"
+  )
+plmtest_check_interval ("plm-gamma-d-check"
+  "${PLM_BUILD_TESTING_DIR}/plm-gamma-d.stdout.txt"
+  "Pass rate = *([-0-9.]*)"
+  "89.9"
+  "90.1"
+  )
+set_tests_properties (plm-gamma-d PROPERTIES DEPENDS "rectarr-03;rectarr-04")
+set_tests_properties (plm-gamma-d-check PROPERTIES DEPENDS plm-gamma-d)
+
+
 ## -------------------------------------------------------------------------
 ## plastimatch usage
 ## -------------------------------------------------------------------------
@@ -2286,6 +2300,45 @@ set_tests_properties (plm-reg-itk-rigid-a-check-2 PROPERTIES
   DEPENDS plm-reg-itk-rigid-a-stats-2)
 
 plm_add_test (
+  "plm-reg-itk-similarity"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "${PLM_BUILD_TESTING_DIR}/plm-reg-itk-similarity.txt"
+  )
+plm_add_test (
+  "plm-reg-itk-similarity-stats-1"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/plm-reg-itk-similarity-intermediate-img.mha"
+  )
+plmtest_check_interval ("plm-reg-itk-similarity-check-1"
+  "${PLM_BUILD_TESTING_DIR}/plm-reg-itk-similarity-stats-1.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "-981.3"
+  "-981.1"
+  )
+plm_add_test (
+  "plm-reg-itk-similarity-stats-2"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/plm-reg-itk-similarity-img.mha"
+  )
+plmtest_check_interval ("plm-reg-itk-similarity-check-2"
+  "${PLM_BUILD_TESTING_DIR}/plm-reg-itk-similarity-stats-2.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "-940.8"
+  "-940.5"
+  )
+
+set_tests_properties (plm-reg-itk-similarity PROPERTIES
+  DEPENDS "rect-2s;rect-2")
+set_tests_properties (plm-reg-itk-similarity-stats-1 PROPERTIES
+  DEPENDS plm-reg-itk-similarity)
+set_tests_properties (plm-reg-itk-similarity-check-1 PROPERTIES
+  DEPENDS plm-reg-itk-similarity-stats-1)
+set_tests_properties (plm-reg-itk-similarity-stats-2 PROPERTIES
+  DEPENDS plm-reg-itk-similarity)
+set_tests_properties (plm-reg-itk-similarity-check-2 PROPERTIES
+  DEPENDS plm-reg-itk-similarity-stats-2)
+
+plm_add_test (
   "plm-reg-itk-rigid-b"
   ${PLM_PLASTIMATCH_PATH}/plastimatch
   "${PLM_BUILD_TESTING_DIR}/plm-reg-itk-rigid-b.txt"
@@ -2841,7 +2894,7 @@ set_tests_properties (plm-bsp-regularize-analytic-check PROPERTIES
 plm_add_test (
   "plm-bsp-regularize-numeric" 
   ${PLM_PLASTIMATCH_PATH}/plastimatch
-  "${PLM_TESTING_DATA_DIR}/plm-bsp-regularize-numeric.txt"
+  "${PLM_BUILD_TESTING_DIR}/plm-bsp-regularize-numeric.txt"
   )
 plm_add_test (
   "plm-bsp-regularize-numeric-stats"
@@ -3125,6 +3178,7 @@ set_tests_properties (plm-bsp-dcos-f-check PROPERTIES
 ## -------------------------------------------------------------------------
 ## plastimatch register (group 6)
 ##   plm-reg-multi-a      versor, then demons
+##   plm-reg-multi-b      two b-spline stages
 ## -------------------------------------------------------------------------
 plm_add_test (
   "plm-reg-multi-a"
@@ -3150,6 +3204,30 @@ set_tests_properties (plm-reg-multi-a-stats PROPERTIES
 set_tests_properties (plm-reg-multi-a-check PROPERTIES 
   DEPENDS plm-reg-multi-a-stats)
 
+plm_add_test (
+  "plm-reg-multi-b"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "${PLM_BUILD_TESTING_DIR}/plm-reg-multi-b.txt"
+  )
+plm_add_test (
+  "plm-reg-multi-b-stats"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/plm-reg-multi-b-img.nrrd"
+  )
+plmtest_check_interval (
+  "plm-reg-multi-b-check"
+  "${PLM_BUILD_TESTING_DIR}/plm-reg-multi-b-stats.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "-987"
+  "-984"
+  )
+set_tests_properties (plm-reg-multi-b PROPERTIES 
+  DEPENDS "rect-2;sphere-2")
+set_tests_properties (plm-reg-multi-b-stats PROPERTIES 
+  DEPENDS plm-reg-multi-b)
+set_tests_properties (plm-reg-multi-b-check PROPERTIES 
+  DEPENDS plm-reg-multi-b-stats)
+
 ## -------------------------------------------------------------------------
 ## plastimatch register (group 7) - check default value
 ##   plm-reg-dv-itk-translation
@@ -4067,7 +4145,7 @@ set_tests_properties (plm-xf-convert-d-warp-2-check PROPERTIES
 plm_add_test (
   "plm-xf-convert-e" 
   ${PLM_PLASTIMATCH_PATH}/plastimatch
-  "xf-convert;--input;${PLM_TESTING_DATA_DIR}/itk-rigid-a.tfm;--output-dicom;${PLM_BUILD_TESTING_DIR}/plm-xf-convert-e;--source-rcs;${PLM_BUILD_TESTING_DIR}/rect-2-dicom;--registered-rcs;${PLM_BUILD_TESTING_DIR}/rect-3-dicom"
+  "xf-convert;--input;${PLM_TESTING_DATA_DIR}/itk-rigid-a.tfm;--output-dicom;${PLM_BUILD_TESTING_DIR}/plm-xf-convert-e;--fixed-rcs;${PLM_BUILD_TESTING_DIR}/rect-2-dicom;--moving-rcs;${PLM_BUILD_TESTING_DIR}/rect-3-dicom"
   )
 set_tests_properties (plm-xf-convert-e PROPERTIES DEPENDS "rect-2;rect-3")
 
@@ -4135,6 +4213,11 @@ set_tests_properties (vf-invert-trans-1-check
 ## proton-dose-2    Flavor f, single bragg peak, target, autom. rgcomp
 ## proton-dose-3    Flavor g, multiple beam
 ## proton-dose-4    Flavor h, load range comp file
+## proton-dose-5a   Flavor a, sobp target with aperture and rgc
+## proton-dose-5f   Flavor f, sobp target with aperture and rgc
+## proton-dose-5g   Flavor g, sobp target with aperture and rgc
+## proton-dose-5h   Flavor h, sobp target with aperture and rgc
+## proton-dose-6a   Flavor a, target geometry differs from CT geometry
 ## -------------------------------------------------------------------------
 plm_add_test (
   "bragg-curve"
@@ -4151,30 +4234,174 @@ plm_add_test (
     ${PLM_PLASTIMATCH_PATH}/proton_dose
     "${PLM_BUILD_TESTING_DIR}/proton-dose-1.txt"
     )
+plm_add_test (
+  "proton-dose-1-stats"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/proton-dose-1.mha"
+  )
+plmtest_check_interval ("proton-dose-1-check"
+  "${PLM_BUILD_TESTING_DIR}/proton-dose-1-stats.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "0.140"
+  "0.145"
+  )
 set_tests_properties (proton-dose-1 PROPERTIES DEPENDS rect-17)
+set_tests_properties (proton-dose-1-stats
+    PROPERTIES DEPENDS proton-dose-1)
+set_tests_properties (proton-dose-1-check
+    PROPERTIES DEPENDS proton-dose-1-stats)
+
+plm_add_test (
+    "proton-dose-2"
+    ${PLM_PLASTIMATCH_PATH}/proton_dose
+    "${PLM_BUILD_TESTING_DIR}/proton-dose-2.txt"
+    )
+plm_add_test (
+  "proton-dose-2-stats"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/proton-dose-2.mha"
+  )
+plmtest_check_interval ("proton-dose-2-check"
+  "${PLM_BUILD_TESTING_DIR}/proton-dose-2-stats.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "0.10"
+  "0.12"
+  )
+set_tests_properties (proton-dose-2 PROPERTIES DEPENDS rect-17)
+set_tests_properties (proton-dose-2 PROPERTIES DEPENDS ptv-1)
+set_tests_properties (proton-dose-2-stats
+    PROPERTIES DEPENDS proton-dose-2)
+set_tests_properties (proton-dose-2-check
+    PROPERTIES DEPENDS proton-dose-2-stats)
+
+plm_add_test (
+    "proton-dose-3"
+    ${PLM_PLASTIMATCH_PATH}/proton_dose
+    "${PLM_BUILD_TESTING_DIR}/proton-dose-3.txt"
+    )
+plm_add_test (
+  "proton-dose-3-stats"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/proton-dose-3.mha"
+  )
+plmtest_check_interval ("proton-dose-3-check"
+  "${PLM_BUILD_TESTING_DIR}/proton-dose-3-stats.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "0.21"
+  "0.22"
+  )
+set_tests_properties (proton-dose-3 PROPERTIES DEPENDS rect-17)
+set_tests_properties (proton-dose-3 PROPERTIES DEPENDS ptv-1)
+set_tests_properties (proton-dose-3-stats
+    PROPERTIES DEPENDS proton-dose-3)
+set_tests_properties (proton-dose-3-check
+    PROPERTIES DEPENDS proton-dose-3-stats)
+
+## This seems to not work, gives zero dose.
+#plm_add_test (
+#    "proton-dose-4"
+#    ${PLM_PLASTIMATCH_PATH}/proton_dose
+#    "${PLM_BUILD_TESTING_DIR}/proton-dose-4.txt"
+#    )
+#set_tests_properties (proton-dose-4 PROPERTIES DEPENDS rect-18)
+#set_tests_properties (proton-dose-4 PROPERTIES DEPENDS rgc-1)
+
+plm_add_test (
+    "proton-dose-5a"
+    ${PLM_PLASTIMATCH_PATH}/proton_dose
+    "${PLM_BUILD_TESTING_DIR}/proton-dose-5a.txt"
+    )
+plm_add_test (
+  "proton-dose-5a-stats"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/proton-dose-5a.mha"
+  )
+plmtest_check_interval ("proton-dose-5a-check"
+  "${PLM_BUILD_TESTING_DIR}/proton-dose-5a-stats.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "0.26"
+  "0.27"
+  )
+set_tests_properties (proton-dose-5a PROPERTIES DEPENDS rect-17)
+set_tests_properties (proton-dose-5a PROPERTIES DEPENDS ptv-1)
+set_tests_properties (proton-dose-5a-stats
+    PROPERTIES DEPENDS proton-dose-5a)
+set_tests_properties (proton-dose-5a-check
+    PROPERTIES DEPENDS proton-dose-5a-stats)
 
- plm_add_test (
-     "proton-dose-2"
-     ${PLM_PLASTIMATCH_PATH}/proton_dose
-     "${PLM_BUILD_TESTING_DIR}/proton-dose-2.txt"
-     )
- set_tests_properties (proton-dose-2 PROPERTIES DEPENDS rect-17)
- set_tests_properties (proton-dose-2 PROPERTIES DEPENDS ptv-1)
-
- plm_add_test (
-     "proton-dose-3"
-     ${PLM_PLASTIMATCH_PATH}/proton_dose
-     "${PLM_BUILD_TESTING_DIR}/proton-dose-3.txt"
-     )
- set_tests_properties (proton-dose-3 PROPERTIES DEPENDS rect-17)
-
- plm_add_test (
-     "proton-dose-4"
-     ${PLM_PLASTIMATCH_PATH}/proton_dose
-     "${PLM_BUILD_TESTING_DIR}/proton-dose-4.txt"
-     )
- set_tests_properties (proton-dose-4 PROPERTIES DEPENDS rect-18)
- set_tests_properties (proton-dose-4 PROPERTIES DEPENDS rgc-1)
+plm_add_test (
+    "proton-dose-5g"
+    ${PLM_PLASTIMATCH_PATH}/proton_dose
+    "${PLM_BUILD_TESTING_DIR}/proton-dose-5g.txt"
+    )
+plm_add_test (
+  "proton-dose-5g-stats"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/proton-dose-5g.mha"
+  )
+plmtest_check_interval ("proton-dose-5g-check"
+  "${PLM_BUILD_TESTING_DIR}/proton-dose-5g-stats.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "0.365"
+  "0.375"
+  )
+set_tests_properties (proton-dose-5g PROPERTIES DEPENDS rect-17)
+set_tests_properties (proton-dose-5g PROPERTIES DEPENDS ptv-1)
+set_tests_properties (proton-dose-5g-stats
+    PROPERTIES DEPENDS proton-dose-5g)
+set_tests_properties (proton-dose-5g-check
+    PROPERTIES DEPENDS proton-dose-5g-stats)
+
+plm_add_test (
+    "proton-dose-6a"
+    ${PLM_PLASTIMATCH_PATH}/proton_dose
+    "${PLM_BUILD_TESTING_DIR}/proton-dose-6a.txt"
+    )
+plm_add_test (
+  "proton-dose-6a-stats"
+  ${PLM_PLASTIMATCH_PATH}/plastimatch
+  "stats;${PLM_BUILD_TESTING_DIR}/proton-dose-6a.mha"
+  )
+plmtest_check_interval ("proton-dose-6a-check"
+  "${PLM_BUILD_TESTING_DIR}/proton-dose-6a-stats.stdout.txt"
+  "AVE *([-0-9.]*)"
+  "0.26"
+  "0.27"
+  )
+set_tests_properties (proton-dose-6a PROPERTIES DEPENDS rect-17)
+set_tests_properties (proton-dose-6a PROPERTIES DEPENDS ptv-1)
+set_tests_properties (proton-dose-6a-stats
+    PROPERTIES DEPENDS proton-dose-6a)
+set_tests_properties (proton-dose-6a-check
+    PROPERTIES DEPENDS proton-dose-6a-stats)
+
+## -------------------------------------------------------------------------
+## wed-a           ct to proj-wed, proj-ct, and wed-ct
+## wed-b           load saved proj-wed, compute proj-ct and wed-ct
+## wed-c           proj-wed to dew-ct
+## -------------------------------------------------------------------------
+plm_add_test (
+  "wed-a"
+  ${PLM_PLASTIMATCH_PATH}/wed
+  "${PLM_BUILD_TESTING_DIR}/wed-a.txt"
+  )
+set_tests_properties (wed-a PROPERTIES DEPENDS lung-1)
+
+plm_add_test (
+  "wed-b"
+  ${PLM_PLASTIMATCH_PATH}/wed
+  "${PLM_BUILD_TESTING_DIR}/wed-b.txt"
+  )
+set_tests_properties (wed-b PROPERTIES DEPENDS lung-1)
+set_tests_properties (wed-b PROPERTIES DEPENDS wed-a)
+
+plm_add_test (
+  "wed-c"
+  ${PLM_PLASTIMATCH_PATH}/wed
+  "${PLM_BUILD_TESTING_DIR}/wed-c.txt"
+  )
+set_tests_properties (wed-b PROPERTIES DEPENDS lung-1)
+set_tests_properties (wed-b PROPERTIES DEPENDS wed-a)
 
 ## -------------------------------------------------------------------------
 ## i hate myself
diff --git a/Testing/CTestCustom.cmake.in b/Testing/CTestCustom.cmake.in
index 4457852..760959c 100644
--- a/Testing/CTestCustom.cmake.in
+++ b/Testing/CTestCustom.cmake.in
@@ -333,9 +333,23 @@ if (PLM_CONFIG_DEBIAN_BUILD)
   set (CTEST_CUSTOM_TESTS_IGNORE
     ${CTEST_CUSTOM_TESTS_IGNORE}
     "proton-dose-1"
+    "proton-dose-1-stats"
+    "proton-dose-1-check"
     "proton-dose-2"
+    "proton-dose-2-stats"
+    "proton-dose-2-check"
     "proton-dose-3"
-    "proton-dose-4"
+    "proton-dose-3-stats"
+    "proton-dose-3-check"
+    "proton-dose-5a"
+    "proton-dose-5a-stats"
+    "proton-dose-5a-check"
+    "proton-dose-5g"
+    "proton-dose-5g-stats"
+    "proton-dose-5g-check"
+    "proton-dose-6a"
+    "proton-dose-6a-stats"
+    "proton-dose-6a-check"
     )
 endif ()
 
diff --git a/Testing/Data/plm-bsp-regularize-numeric.txt b/Testing/Data/plm-bsp-regularize-numeric.txt
index 466c44a..c6ee8b8 100755
--- a/Testing/Data/plm-bsp-regularize-numeric.txt
+++ b/Testing/Data/plm-bsp-regularize-numeric.txt
@@ -1,9 +1,10 @@
 [GLOBAL]
-fixed=rect-3.mha
-moving=sphere-2.mha
-vf_out=plm-bsp-regularize-numeric-vf.mha
-xform_out=plm-bsp-regularize-numeric-xf.txt
-img_out=plm-bsp-regularize-numeric-img.mha
+fixed=@PLM_BUILD_TESTING_DIR@/rect-3.mha
+moving=@PLM_BUILD_TESTING_DIR@/sphere-2.mha
+
+vf_out=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -vf.mha
+xform_out=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -xf.txt
+img_out=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -img.mha
 
 [STAGE]
 xform=bspline
diff --git a/Testing/Data/plm-reg-itk-similarity.txt b/Testing/Data/plm-reg-itk-similarity.txt
new file mode 100755
index 0000000..8657aa5
--- /dev/null
+++ b/Testing/Data/plm-reg-itk-similarity.txt
@@ -0,0 +1,33 @@
+[GLOBAL]
+fixed=@PLM_BUILD_TESTING_DIR@/rect-2s.mha
+moving=@PLM_BUILD_TESTING_DIR@/rect-2.mha
+
+vf_out=@PLM_BUILD_TESTING_DIR@/plm-reg-itk-similarity-a-vf.mha
+xform_out=@PLM_BUILD_TESTING_DIR@/plm-reg-itk-similarity-a-xf.txt
+img_out=@PLM_BUILD_TESTING_DIR@/plm-reg-itk-similarity-a-img.mha
+
+[STAGE]
+xform=similarity
+optim=oneplusone
+impl=itk
+translation_scale_factor=1
+rotation_scale_factor=0.003
+scaling_scale_factor=0.1
+min_its=5
+max_its=10
+grad_tol=0.001
+res=2 2 2
+img_out=plm-reg-itk-similarity-intermediate-img.mha
+
+[STAGE]
+xform=similarity
+optim=oneplusone
+impl=itk
+rotation_scale_factor=0.005
+translation_scale_factor=1
+scaling_scale_factor=0.1
+min_its=5
+max_its=10
+grad_tol=0.001
+res=1 1 1
+img_out=plm-reg-itk-similarity-img.mha
diff --git a/Testing/Data/plm-reg-multi-b.txt b/Testing/Data/plm-reg-multi-b.txt
new file mode 100755
index 0000000..d66d016
--- /dev/null
+++ b/Testing/Data/plm-reg-multi-b.txt
@@ -0,0 +1,15 @@
+[GLOBAL]
+fixed=@PLM_BUILD_TESTING_DIR@/rect-2.mha
+moving=@PLM_BUILD_TESTING_DIR@/sphere-2.mha
+
+img_out=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -img.nrrd
+
+[STAGE]
+xform=bspline
+impl=plastimatch
+max_its=20
+res=4 4 4
+
+[STAGE]
+grid_spac=10 10 10
+res=1 1 1
diff --git a/Testing/Data/proton-dose-1.txt b/Testing/Data/proton-dose-1.txt
index 5d26caa..a9d0d23 100644
--- a/Testing/Data/proton-dose-1.txt
+++ b/Testing/Data/proton-dose-1.txt
@@ -1,25 +1,24 @@
-[PLAN]
-patient = @PLM_BUILD_TESTING_DIR@/rect-17.mha
-dose_out = @PLM_BUILD_TESTING_DIR@/dose-1.mha
-
-dose_prescription = 70
-
-[BEAM]
-flavor = a
-homo_approx = n
-
-source = 0 -2000 0
-isocenter = 0 0 0
-
-aperture_origin = -10 -10
-aperture_offset = 1500
-aperture_spacing = 1 1
-aperture_resolution = 21 21
-source_size = 0
-prescription_min = 90
-prescription_max = 110
-
-#[PEAK]
-#energy=100.0000
-#spread=1.000000
-#weight=1
+[PLAN]
+patient = @PLM_BUILD_TESTING_DIR@/rect-17.mha
+dose_out = @PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at .mha
+
+dose_prescription = 70
+
+[BEAM]
+flavor = a
+homo_approx = n
+
+source = 0 -2000 0
+isocenter = 0 0 0
+
+aperture_origin = -10 -10
+aperture_offset = 1500
+aperture_spacing = 1 1
+aperture_resolution = 21 21
+source_size = 0
+prescription_min_max = 70 90
+
+#[PEAK]
+#energy=100.0000
+#spread=1.000000
+#weight=1
diff --git a/Testing/Data/proton-dose-2.txt b/Testing/Data/proton-dose-2.txt
index 8eb6b69..cbc1b45 100644
--- a/Testing/Data/proton-dose-2.txt
+++ b/Testing/Data/proton-dose-2.txt
@@ -1,9 +1,7 @@
-#this file will create two beams (0� and 180�) of 100 MeV, automatically shaped to fit a target with 5 mm margins - if you don't specify the PEAKS, a SOBP will be automatically created to fit the target, you can also defined proximal and distal margins
-
 [PLAN]
 patient = @PLM_BUILD_TESTING_DIR@/rect-17.mha
-dose_out = @PLM_BUILD_TESTING_DIR@/dose-2.mha
 target = @PLM_BUILD_TESTING_DIR@/ptv-1.mha
+dose_out = @PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at .mha
 
 [BEAM]
 flavor = f
@@ -22,4 +20,4 @@ source_size = 0
 [PEAK]
 energy=100.00000
 spread=1.000000
-weight=1.0
\ No newline at end of file
+weight=1.0
diff --git a/Testing/Data/proton-dose-3.txt b/Testing/Data/proton-dose-3.txt
index f8b36d4..db4ed2a 100644
--- a/Testing/Data/proton-dose-3.txt
+++ b/Testing/Data/proton-dose-3.txt
@@ -1,36 +1,36 @@
-#this file will create two beams (0� and 180�), both made of two protons peaks
-
-[PLAN]
-patient = @PLM_BUILD_TESTING_DIR@/rect-17.mha
-dose_out = @PLM_BUILD_TESTING_DIR@/dose-3.mha
-
-[BEAM]
-flavor = g
-homo_approx = n
-
-source = 0 -2000 0
-isocenter = 0 0 0
-
-aperture_origin = -10 -10
-aperture_offset = 1500
-aperture_spacing = 2 2
-aperture_resolution = 11 11
-source_size = 0
-
-[PEAK]
-energy=100.00000
-spread=1.000000
-weight=.5
-
-[PEAK]
-energy=80.00000
-spread=1.000000
-weight=.5
-
-[BEAM]
-source = 0 2000 0
-
-[PEAK]
-energy=120.00000
-spread=1.000000
-weight=1
+#this file will create two beams (0� and 180�), both made of two protons peaks
+
+[PLAN]
+patient = @PLM_BUILD_TESTING_DIR@/rect-17.mha
+dose_out = @PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at .mha
+
+[BEAM]
+flavor = g
+homo_approx = n
+
+source = 0 -2000 0
+isocenter = 0 0 0
+
+aperture_origin = -10 -10
+aperture_offset = 1500
+aperture_spacing = 2 2
+aperture_resolution = 11 11
+source_size = 0
+
+[PEAK]
+energy=100.00000
+spread=1.000000
+weight=.5
+
+[PEAK]
+energy=80.00000
+spread=1.000000
+weight=.5
+
+[BEAM]
+source = 0 2000 0
+
+[PEAK]
+energy=120.00000
+spread=1.000000
+weight=1
diff --git a/Testing/Data/proton-dose-4.txt b/Testing/Data/proton-dose-4.txt
index f68439f..2736bd0 100644
--- a/Testing/Data/proton-dose-4.txt
+++ b/Testing/Data/proton-dose-4.txt
@@ -1,23 +1,23 @@
-[PLAN]
-patient = @PLM_BUILD_TESTING_DIR@/rect-18.mha
-dose_out = @PLM_BUILD_TESTING_DIR@/dose-4.mha
-
-[BEAM]
-flavor = h
-homo_approx = n
-depth_dose_z_max = 400
-
-source = 0 -2200 0
-isocenter = 0 0 0
-
-aperture_origin = -37.5 -37.5
-aperture_offset = 1500
-aperture_spacing = 7.5 7.5
-aperture_resolution = 10 10
-range_compensator_file_in = @PLM_BUILD_TESTING_DIR@/rgc-1.mha
-source_size = 0
-
-[PEAK]
-energy=100.0000
-spread=1.000000
-weight=1
+[PLAN]
+patient = @PLM_BUILD_TESTING_DIR@/rect-18.mha
+dose_out = @PLM_BUILD_TESTING_DIR@/proton-dose4.mha
+
+[BEAM]
+flavor = h
+homo_approx = n
+depth_dose_z_max = 400
+
+source = 0 -2200 0
+isocenter = 0 0 0
+
+aperture_origin = -37.5 -37.5
+aperture_offset = 1500
+aperture_spacing = 7.5 7.5
+aperture_resolution = 10 10
+range_compensator_file_in = @PLM_BUILD_TESTING_DIR@/rgc-1.mha
+source_size = 0
+
+[PEAK]
+energy=100.0000
+spread=1.000000
+weight=1
diff --git a/Testing/Data/proton-dose-5a.txt b/Testing/Data/proton-dose-5a.txt
new file mode 100644
index 0000000..190640e
--- /dev/null
+++ b/Testing/Data/proton-dose-5a.txt
@@ -0,0 +1,18 @@
+[PLAN]
+patient = @PLM_BUILD_TESTING_DIR@/rect-17.mha
+target = @PLM_BUILD_TESTING_DIR@/ptv-1.mha
+dose_out = @PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at .mha
+
+[BEAM]
+flavor = a
+homo_approx = n
+
+source = 0 -2000 0
+isocenter = 0 0 0
+
+aperture_offset = 1500
+aperture_origin = -75 -75
+aperture_spacing = 3.75 3.75
+aperture_resolution = 41 41
+aperture_smearing = 5
+source_size = 0
diff --git a/Testing/Data/proton-dose-5g.txt b/Testing/Data/proton-dose-5g.txt
new file mode 100644
index 0000000..4f6c815
--- /dev/null
+++ b/Testing/Data/proton-dose-5g.txt
@@ -0,0 +1,18 @@
+[PLAN]
+patient = @PLM_BUILD_TESTING_DIR@/rect-17.mha
+target = @PLM_BUILD_TESTING_DIR@/ptv-1.mha
+dose_out = @PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at .mha
+
+[BEAM]
+flavor = g
+homo_approx = n
+
+source = 0 -2000 0
+isocenter = 0 0 0
+
+aperture_offset = 1500
+aperture_origin = -75 -75
+aperture_spacing = 3.75 3.75
+aperture_resolution = 41 41
+aperture_smearing = 5
+source_size = 0
diff --git a/Testing/Data/proton-dose-6a.txt b/Testing/Data/proton-dose-6a.txt
new file mode 100644
index 0000000..ae565d0
--- /dev/null
+++ b/Testing/Data/proton-dose-6a.txt
@@ -0,0 +1,18 @@
+[PLAN]
+patient = @PLM_BUILD_TESTING_DIR@/rect-17.mha
+target = @PLM_BUILD_TESTING_DIR@/ptv-2.mha
+dose_out = @PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at .mha
+
+[BEAM]
+flavor = a
+homo_approx = n
+
+source = 0 -2000 0
+isocenter = 0 0 0
+
+aperture_offset = 1500
+aperture_origin = -75 -75
+aperture_spacing = 3.75 3.75
+aperture_resolution = 41 41
+aperture_smearing = 5
+source_size = 0
diff --git a/Testing/Data/speedtest-a.txt b/Testing/Data/speedtest-a.txt
new file mode 100755
index 0000000..51a7f0e
--- /dev/null
+++ b/Testing/Data/speedtest-a.txt
@@ -0,0 +1,15 @@
+[GLOBAL]
+fixed=@PLM_BUILD_TESTING_DIR@/gauss-1.mha
+moving=@PLM_BUILD_TESTING_DIR@/gauss-2.mha
+
+[STAGE]
+xform=bspline
+optim=lbfgsb
+impl=plastimatch
+threading=single
+alg_flavor=c
+max_its=5
+convergence_tol=3
+grad_tol=0.1
+grid_spac=30 30 30
+res=2 2 2
diff --git a/Testing/Data/wed-a.txt b/Testing/Data/wed-a.txt
new file mode 100755
index 0000000..d1539b5
--- /dev/null
+++ b/Testing/Data/wed-a.txt
@@ -0,0 +1,19 @@
+[INPUT SETTINGS]
+ct=@PLM_BUILD_TESTING_DIR@/lung-1.mha
+target=@PLM_BUILD_TESTING_DIR@/lung-1-prefix/Tumor.mha
+skin=@PLM_BUILD_TESTING_DIR@/lung-1-prefix/Body.mha
+
+[OUTPUT SETTINGS]
+proj_ct=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -proj-ct.rpl
+proj_wed=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -proj-wed.rpl
+wed_ct=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -wed-ct.mha
+
+[BEAM]
+pos=-2270.5 0 0
+isocenter=0 0 0
+res=1
+
+[APERTURE]
+offset=1700
+center=49.5 49.5
+resolution=100 100
diff --git a/Testing/Data/wed-b.txt b/Testing/Data/wed-b.txt
new file mode 100755
index 0000000..03c730f
--- /dev/null
+++ b/Testing/Data/wed-b.txt
@@ -0,0 +1,19 @@
+[INPUT SETTINGS]
+ct=@PLM_BUILD_TESTING_DIR@/lung-1.mha
+proj_wed=@PLM_BUILD_TESTING_DIR@/wed-a-proj-wed.rpl
+target=@PLM_BUILD_TESTING_DIR@/lung-1-prefix/Tumor.mha
+skin=@PLM_BUILD_TESTING_DIR@/lung-1-prefix/Body.mha
+
+[OUTPUT SETTINGS]
+proj_ct=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -proj-ct.rpl
+wed_ct=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -wed-ct.mha
+
+[BEAM]
+pos=-2270.5 0 0
+isocenter=0 0 0
+res=1
+
+[APERTURE]
+offset=1700
+center=49.5 49.5
+resolution=100 100
diff --git a/Testing/Data/wed-c.txt b/Testing/Data/wed-c.txt
new file mode 100755
index 0000000..ab6dc39
--- /dev/null
+++ b/Testing/Data/wed-c.txt
@@ -0,0 +1,18 @@
+[INPUT SETTINGS]
+ct=@PLM_BUILD_TESTING_DIR@/lung-1.mha
+proj_wed=@PLM_BUILD_TESTING_DIR@/wed-a-proj-wed.mha
+target=@PLM_BUILD_TESTING_DIR@/lung-1-prefix/Tumor.mha
+skin=@PLM_BUILD_TESTING_DIR@/lung-1-prefix/Body.mha
+
+[OUTPUT SETTINGS]
+dew_ct=@PLM_BUILD_TESTING_DIR@/@PLM_TEST_NAME at -dew-ct.mha
+
+[BEAM]
+pos=-2270.5 0 0
+isocenter=0 0 0
+res=1
+
+[APERTURE]
+offset=1700
+center=49.5 49.5
+resolution=100 100
diff --git a/Testing/PlmSpeedTest.cmake b/Testing/PlmSpeedTest.cmake
new file mode 100755
index 0000000..1129cd9
--- /dev/null
+++ b/Testing/PlmSpeedTest.cmake
@@ -0,0 +1,17 @@
+## This script runs the speed tests
+
+execute_process (
+  COMMAND "${PLM_PLASTIMATCH_PATH}/plastimatch"
+#  OUTPUT_FILE ${PLM_BUILD_TESTING_DIR}/hello.txt
+  OUTPUT_VARIABLE STDOUT
+  ERROR_VARIABLE STDOUT
+  )
+message (STATUS "${PLM_PLASTIMATCH_PATH}/plastimatch")
+message (STATUS "${STDOUT}")
+
+file (WRITE "${PLM_BUILD_TESTING_DIR}/hello.txt"
+  "${PLM_PLASTIMATCH_PATH}/plastimatch\n")
+file (APPEND "${PLM_BUILD_TESTING_DIR}/hello.txt"
+  "STDOUT = ${STDOUT}\n")
+file (APPEND "${PLM_BUILD_TESTING_DIR}/hello.txt"
+  "STDERR = ${STDERR}\n")
diff --git a/cmake/CheckEpsilon.cmake b/cmake/CheckEpsilon.cmake
index b10ad56..0ded097 100755
--- a/cmake/CheckEpsilon.cmake
+++ b/cmake/CheckEpsilon.cmake
@@ -7,5 +7,4 @@ macro (CHECK_EPSILON OUT_VAR)
     ${CMAKE_BINARY_DIR}
     ${CMAKE_SOURCE_DIR}/cmake/test_eps.cxx
     RUN_OUTPUT_VARIABLE ${OUT_VAR})
-  #message (STATUS "Checking epsilon: ${OUT_VAR}")
 endmacro ()
diff --git a/cmake/CheckQt.cmake b/cmake/CheckQt.cmake
new file mode 100644
index 0000000..516793d
--- /dev/null
+++ b/cmake/CheckQt.cmake
@@ -0,0 +1,19 @@
+##---------------------------------------------------------------------------
+## See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+##---------------------------------------------------------------------------
+
+include (CheckCXXSourceCompiles)
+
+macro (CHECK_QT QT_TEST_COMPILE_SUCCEEDED)
+    # It took forever to get the quoting correct on this.
+    # Thanks to sakra @ http://stackoverflow.com/questions/25726853
+    try_compile (COMPILE_RESULT_VAR
+	${CMAKE_BINARY_DIR}/helpme
+	${CMAKE_SOURCE_DIR}/cmake/test_qt.cxx
+	CMAKE_FLAGS 
+	"-DINCLUDE_DIRECTORIES:STRING=${QT_INCLUDES}"
+	"-DLINK_LIBRARIES:STRING=${QT_QTCORE_LIBRARIES}"
+	OUTPUT_VARIABLE OUT_VAR
+	)
+    set (${QT_TEST_COMPILE_SUCCEEDED} ${COMPILE_RESULT_VAR})
+endmacro ()
diff --git a/cmake/FindCUDA_wrap.cmake b/cmake/FindCUDA_wrap.cmake
index bcc1466..ee63f8f 100755
--- a/cmake/FindCUDA_wrap.cmake
+++ b/cmake/FindCUDA_wrap.cmake
@@ -26,6 +26,12 @@ else ()
   find_package (CUDA QUIET)
 endif ()
 
+# 14-5-2016 PAOLO: WORKAROUND GCC 6.1 AND CUDA 7.5 INCOMPATIBILITY
+if (CMAKE_COMPILER_IS_GNUCC
+      AND (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0))
+    set (CUDA_CXX_FLAGS "${CUDA_CXX_FLAGS},-std=c++98")
+endif ()
+
 # ITK headers cannot be processed by nvcc, so we define
 # PLM_CUDA_COMPILE for the purpose of guarding
 # (see base/plmbase.h)
diff --git a/cmake/PlmMacros.cmake b/cmake/PlmMacros.cmake
index 2e41a12..68d214c 100644
--- a/cmake/PlmMacros.cmake
+++ b/cmake/PlmMacros.cmake
@@ -12,15 +12,25 @@
 ## GCS 2012-06-10
 ##  Installed libraries need added to export set for external applications.
 macro (PLM_ADD_LIBRARY 
-    TARGET_NAME TARGET_SRC TARGET_LIBS TARGET_LDFLAGS TARGET_INCLUDES)
+    TARGET_NAME
+    TARGET_SRC
+    TARGET_LIBS
+    TARGET_LDFLAGS
+    TARGET_INCLUDE_DIRECTORIES
+    TARGET_INCLUDE_FILES
+    )
 
   add_library (${TARGET_NAME} ${TARGET_SRC})
   set_target_properties (${TARGET_NAME} PROPERTIES 
     ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
     LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
     RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
-    PUBLIC_HEADER "${TARGET_INCLUDES}")
+    PUBLIC_HEADER "${TARGET_INCLUDE_FILES}")
   if (PLM_CONFIG_INSTALL_LIBRARIES)
+    if (NOT PLM_PACKAGE_LEGACY_CMAKE_CONFIG)
+      target_include_directories(${TARGET_NAME} INTERFACE
+	${TARGET_INCLUDE_DIRECTORIES})
+    endif ()
     install (TARGETS ${TARGET_NAME}
       EXPORT PlastimatchLibraryDepends
       RUNTIME DESTINATION "${PLM_INSTALL_BIN_DIR}" 
diff --git a/cmake/test_qt.cxx b/cmake/test_qt.cxx
new file mode 100644
index 0000000..7d45870
--- /dev/null
+++ b/cmake/test_qt.cxx
@@ -0,0 +1,17 @@
+#include <QString>
+
+QString foo ()
+{
+    return QString ("a") + QString ("b");
+}
+
+int 
+main (int argc, char* argv[])
+{
+    QString q1 ("Hello world");
+
+    /* The below test fails on VS 2013 when attempting to link against 
+       QT built with VS 2008 */
+    QString q2;
+    q2 = foo ();
+}
diff --git a/doc/NOTES.TXT b/doc/NOTES.TXT
new file mode 100644
index 0000000..3a7845b
--- /dev/null
+++ b/doc/NOTES.TXT
@@ -0,0 +1,1447 @@
+******************************************************************************
+cmake upgrade
+2016-05-16
+
+The cmake infrastructure will benefit from using the new tools for
+creating packages.  New tools are available with CMake 2.8.11,
+available since May 2013.  Here is my understanding of how this stuff
+is supposed to work.
+
+1) Create target 
+
+add_library (libr src.cxx)
+
+2) Assign property to library target
+
+target_include_directories (libr INTERFACE
+    $<INSTALL_INTERFACE:include/libr>)
+
+I think you can also set target_link_libraries, which
+would track library dependencies.
+
+3) Install target with EXPORT option
+
+install (TARGETS libr
+    EXPORT LibrTargets
+    )
+
+It seems step 2 can be omitted by using the INCLUDES DESTINATION
+option in install(TARGETS).  
+
+4) Install export set
+
+install (EXPORT LibrTargets
+  DESTINATION "${PLM_INSTALL_CMAKE_DIR}"
+  )
+
+Do I need to specify the DESTINATION?  Will it do the right thing regardless?
+
+5) Using the library
+
+No need to do anything.  By creating an executable which depends on a
+library target, the include directories are set appropriately.
+
+
+******************************************************************************
+xvi archive
+2016-04-13
+
+1) Handle multiple Rx (done)
+2) Handle HFP/FFS/FFP
+3) Handle rotations
+4) Some form of series description and study description (done)
+5) Correct dates (done)
+6) Mark as CBCT (done)
+7) Some form of window/level (not supported by MIM?)
+
+
+******************************************************************************
+WED
+2016-03-09
+
+Temporarily (?) removed functionality
+- Computing aperture and range compensator (mode 2)
+- Looping through angles (mode 3)
+
+
+******************************************************************************
+Proj_volume coordinates
+2016-02-08
+
+The main issue with the proj volume is that its coordinates are 
+both difficult to understand and poorly documented.
+
+What makes it difficult?
+- ires vs image_dim vs dim
+-- use image_dim
+- is first index row or column?
+-- dim[0] = cols, dim[1] = rows
+-- loop (j=0;j<dim[1]-1;j++) {
+--   loop (i=0;i<dim[0]-1;i++) { } }
+- image_center vs image_origin
+-- both are potentially useful
+-- either should be specified, but internal storage as image origin
+- location of first data point (at clipping plane or inside volume?)
+-- ?
+- different code for drr and wed
+-- need to unify
+
+What does ray_data contain?
+- ray_data is computed from reference volume
+- Includes test if ray intersects volume, front and back intersection 
+  points, aperture intersection point, and ray norm
+- compute_ray_data also sets the clipping planes
+
+What files might the wed program create?
+- proj_ct = HU in proj coordinates
+- proj_wed = WED in proj coordinates
+- proj_dose = dose in proj coordinates
+- wed_ct = HU in WED coordinates
+- wed_dose = dose in WED coordinates
+- dew_ct = HU in proj coordinates
+- dew_dose = dose in proj coordinates
+
+What conversions are important?
+- Convert ct to proj_wed
+-- rpl.compute_rpl_PrSTRP_no_rgc
+- Convert ct to proj_ct
+- Convert proj_wed to ct
+
+This thing needs removal
+- compute_target_distance_limits_slicerRt
+
+******************************************************************************
+Rpl_volume
+2015-12-21
+
+An rpl_volume is a proj_volume.  The x,y dimensions are defined by 
+the aperture, and the z dimension is along rays through the aperture.
+
+The contents of the rpl_volume may be intensity (e.g. ct or dose) or 
+depth (wepl).  
+
+The units of the steps are either geometric or wepl.
+
+Main functions:
+  get_rgdepth - lookup values in volume
+  compute_ray_data - this is called internally, but also by dose code;
+       
+
+  compute_farthest_penetrating_ray_on_nrm
+
+  compute_proj_wed_volume - this seems to compute a 2D image
+  compute_wed_volume - resample a Proj_vol into a Wed_vol
+  compute_dew_volume - 
+
+  apply_smearing_to_target
+  compute_volume_aperture
+  apply_beam_modifiers
+
+  compute_beam_modifiers_passive_scattering
+  compute_beam_modifiers_active_scanning
+
+  rpl_ray_trace - call ray_trace_uniform on a single ray
+
+  Why are there so many of these
+  ------------------------------
+  compute_rpl_void 
+    -> No ray tracing performed
+    -> WTF ?
+  compute_rpl_ct_density
+    -> rpl_ray_trace_callback_ct_density
+    -> Schneider density, not accumulated
+  compute_rpl_HU 
+    -> rpl_ray_trace_callback_ct_HU
+    -> HU, not accumulated
+  compute_rpl_PrSTRP_no_rgc
+    -> rpl_ray_trace_callback_PrSTPR
+    -> Schneider stopping power, not accumulated
+  compute_rpl_range_length_rgc
+    -> rpl_ray_trace_callback_range_length
+    -> Schneider stopping power, accumulated
+  rpl_ray_trace_callback_PrSTPR_XiO_MGH (not called)
+
+  Answer.  There are limitations in the ray tracing design which limit 
+  reusability.
+    -> May accumulate or not
+    -> No real world coordinates / li_values
+
+******************************************************************************
+ITK Origin and Index
+2015-08-18
+
+It seems that ITK allows images for which the first pixel is a 
+non-zero (but non-negative) index.  When using such images, care 
+must be used.
+
+Either, use the region and the (ITK) origin:
+
+    const typename ImageType::PointType& og = input->GetOrigin();
+    const typename ImageType::SpacingType& sp = input->GetSpacing();
+
+Or, use the dimension and the (plastimatch) origin:
+
+
+******************************************************************************
+Image orientation
+2015-08-13
+
+DICOM is simple.  Coordinate system is always LPS.
+
+a) (0020,0032) Image Position Patient: the world coordinate location of 
+the center of the first voxel in the file.
+b) (0020,0037) Image Orientation Patient: the real world direction cosines,
+mapping an increment in voxel index to an increment in real-world location.
+c) (0018,5100) Patient Position: does not affect mapping from voxel 
+index to world coordinate.  But it can be used to display correct 
+orientation on the screen (e.g. putting patient left on screen right 
+for HFS).
+
+ITK is simple.  However, not yet clear to me if coordinate system 
+is always LPS.
+
+a) Offset: the world coordinate location of the center of the first 
+voxel in the file.
+b) TransformMatrix: the real world direction cosines,
+mapping an increment in voxel index to an increment in real-world location.
+c) AnatomicOrientation: does not affect mapping from voxel index 
+to world coordinate.
+
+******************************************************************************
+Bspline_parms
+2015-06-10
+
+1. Historically, it is not possible to include ITK include files 
+   by the nvcc compiler.
+2. Also historically, the itk include file is in plm_config.h
+3. As a result, bspline_parms is needed, instead of accessing 
+   stage_parms.
+
+******************************************************************************
+Proj_volume, Proj_image, Rpl_volume, and dose calculation
+2015-05-06, 2016-03-16
+
+:: Proj_volume
+
+    Volume *vol;
+    Proj_matrix *pmat;
+      double ic[2];
+      double matrix[12];
+      double sad;
+      double sid;
+      double cam[3];
+      double nrm[3];
+      double extrinsic[16];
+      double intrinsic[12];
+    int num_steps;
+    double step_length;
+    int image_dim[2];
+    double image_spacing[2];
+    double clipping_dist[2];
+    double nrm[3];
+    double src[3];
+    double iso[3];
+    double ul_room[3];
+    double incr_r[3];
+    double incr_c[3];
+
+* Proj_volume is defined in documentation with a projective geometry.
+  But it seems to be implemented as a spherical geometry.
+* It is used by rpl_volume, dose
+* It is not used by drr, fdk
+* Regarding Proj_matrix:
+   - Can be used by Siddon approach, does not depend on uniform steps
+   - Has no knowledge of projection plane dimension, spacing
+
+:: Proj_image
+    int dim[2];              /* dim[0] = cols, dim[1] = rows */
+    double xy_offset[2];     /* Offset of center pixel */
+    Proj_matrix *pmat;
+      double ic[2];
+      double matrix[12];
+      double sad;
+      double sid;
+      double cam[3];
+      double nrm[3];
+      double extrinsic[16];
+      double intrinsic[12];
+    float* img;		     /* Pixel data */
+
+* Note use of raw pixel data
+* Spacing gets encoded in pmat
+* Proj_image could be a 1D Proj_vol
+
+:: Aperture
+   Private:
+    Plm_image::Pointer aperture_image;
+    Plm_image::Pointer range_compensator_image;
+    double distance;
+    int dim[2];
+    double center[2];
+    double spacing[2];
+   Public:
+    double vup[3];        /* orientation */
+    double ic_room[3];    /* loc of center (room coords) */
+    double ul_room[3];    /* loc of upper left corder (room coords) */
+    double incr_r[3];     /* row increment vector */
+    double incr_c[3];     /* col increment vector */
+    double nrm[3];        /* unit vec: normal */
+    double pdn[3];        /* unit vec: down */
+    double prt[3];        /* unit vec: right */
+    double tmp[3];
+
+* Projection plane geometry and proton hardware
+* Maybe two separate distances would be helpful
+* Reimplements concepts of Proj_matrix
+* Why not two separate Proj_image with same Proj_matrix?
+* Why pdn instead of pup?
+
+:: Projection
+    Canonical
+      double src[3];
+      double iso[3];
+      double sid;
+      int image_dim[2];            // [0] = columns, [1] = rows
+      double image_center[2];      // in pixels
+      double image_spacing[2];     // spacing on detector
+      double vup[3];
+    Optional
+      double clipping_dist[2];
+    Derivative
+      double matrix[12];
+      double extrinsic[16];
+      double intrinsic[12];
+      double sad;            // || src - iso ||
+      double nrm[3];         // src - iso / || src - iso ||
+      double plt[3];         // nrm x vup / || nrm x vup ||
+      double pup[3];         // plt x nrm
+      double ic_room[3];     // src - sad * nrm
+      double incr_c[3];      // ic_room - image_center[0] * isp[0] * pup
+      double incr_r[3];      // ic_room + image_center[1] * isp[1] * plt
+      double ul_room[3];     // ic_room - ic[0] * incr_c - ic[1] * incr_r
+
+* Replaces Proj_matrix and Aperture
+
+:: IEC
+      double gantry_angle;
+      double couch_angle;
+      double detector_angle;
+      double I_f;       // SAD (?)
+      double I_r;       // detector distance
+      double I_p[3];    // patient offset
+      double Rx, Ry;    // image center
+      N.b. IEC does not describe image resolution or spacing
+
+:: DICOM RT Image
+      General Image (C.7.6.1)
+      Image Pixel (C.7.6.3)
+        Rows
+	Columns
+      RT Image (C.8.8.2)
+	X-Ray Image Receptor Translation       // IEC
+	X-Ray Image Receptor Angle             // IEC
+        RT Image Plane, RT Image Orientation   // out-of-plane rotations
+	Image Plane Pixel Spacing
+	RT Image Position                      // 2D coords of first pixel
+	Radiation Machine SAD
+	RT Image SID
+	Source to Reference ObjectDistance
+	Gantry Angle
+	Gantry Pitch Angle
+	Patient Support Angle
+	Table Top Eccentric AxisDistance
+	Table Top Eccentric Angle
+	Table Top Pitch Angle
+	Table Top Roll Angle
+	Table Top Vertical Position
+	Table Top LongitudinalPosition
+	Table Top Lateral Position
+	Isocenter Position
+	Patient Position
+
+* Strangely, no Image Plane module seems required (or even allowed?), 
+  which would define Image Position Patient, Image Orientation Patient
+
+:: Other thoughts
+
+* The Aperture class demonstrates that you may have multiple 
+proj_image with same proj_matrix.  A few mild assumptions are used:
+image pixels are matched and clipping plane is same.
+Backprojection will map voxels correctly in both proj_image.
+
+Alternative implementation as master-slave, using Proj_image 
+for aperture_image and Volume for range_compensator_image could also 
+be considered.
+
+******************************************************************************
+Older stuff
+
+***** Metadata hookup *****
+
+Current status:
+Rt_study_metadata constructor hooks up internally created metadata
+Segmentation allows external hookup of parent in constructor.  
+
+DCMTK loading puts items into Rt_study_metadata.  It puts the info 
+directly into the right section.  Ditto for GDCM1.
+
+Synthetic_mha creates images and inserts images into an Rt_study.
+
+XiO loader (Rt_study::load_xio) inserts items directly into Rt_study_metadata.
+
+Metadata within Plm_image is never actually used.
+You must have an Rt_study to have metadata.
+The Rt_study_metadata is passed into Plm_image::save()
+
+
+***** Metadata ownership *****
+
+Current ownership organization
+
+Rt_study -> Rt_study_metadata
+Rt_study_metadata -> shared metadata
+Rt_study_metadata -> image metadata
+Rt_study_metadata -> dose metadata
+Rt_study_metadata -> rtss metadata
+
+Plm_image -> image metadata
+Plm_image -> dose metadata
+Segmentation -> rtss metadata
+
+image metadata -> shared metadata
+rtss metadata -> shared metadata
+dose metadata -> shared metadata
+
+***** Bspline methods *****
+
+Method mi-i
+Tile loop to create histogram (hist_add omp_v2)
+Condense loop to create gradient
+
+Method mi-h 
+Serial loop to create histogram
+Condense loop to create gradient
+
+Method mi-g == method mi-h
+
+Method mi-f
+Tile loop to create histogram (hist_add omp_crit)
+Condense loop to create gradient
+
+Method mi-e
+Tile loop to create histogram (hist_add omp_v1)
+Condense loop to create gradient
+
+Method d == method h
+
+Method mi-c
+Serial loop to create histogram
+Serial loop to create gradient
+
+Method mse-i
+Parallel tile
+
+Method mse-h
+Serial tile
+
+Method mse-g == mse-i
+
+Method mse-c
+Serial voxel
+
+Method k
+Serial loop
+
+Method l
+Serial tile loop, condense with sets
+
+
+***** Overlap fraction *****
+
+The overlap fraction should (?) be tied to score at initial iteration.
+This requires stashing the initial score somewhere.  But this needs to 
+be done in a general way for various optimizers.
+
+Furthermore, the "best score" concept is probably not implemented for 
+the non-Nocedal optimizers.
+
+This is a TODO.
+
+***** Programmability redux *****
+
+Option 1) Internal programmability, with lua
+Effort: medium
+Pro: Convenient
+Con: Lua not popular
+
+Option 2) Internal programmability, with internal language
+Effort: medium-high
+Pro: Convenient for existing users, not so difficult to implement
+Con: Custom languages are lame
+
+Option 3) External wrapping, a la VTK
+Effort: medium
+Pro: Python popular
+Con: 
+
+Option 4) External wrapping with Swig
+Effort: medium
+Pro: Python popular, other languages supported
+Con: 
+
+***** Restartable registration (second try) *****
+
+After a lot of effort, I learned that you can't simply code up 
+something like suggested on the ITK web site to create a semaphore
+from a condition variable [1].  Doing so does not work, because the 
+condition variable does not guarantee that the signalee gets the 
+resource before the signaller can re-grab.  N.b. that the DLIB 
+implementation has the same problem.
+
+Therefore instead, let's try a producer-consumer style solution, 
+with two locks.
+
+master_grab:
+  mutex.lock
+    slave_waits = true
+    master_waits = true
+    while master_waits == true
+      master_sema.wait       // wait for slave to finish
+  mutex.unlock
+
+master_release:
+  mutex.lock
+    slave_waits = false
+    slave_sema.signal      // wake up slave
+  mutex.unlock
+
+slave_grab:
+  mutex.lock
+    while slave_waits == true
+      slave_sema.wait      // slave waits to get woken up
+  mutex.unlock
+
+slave_release:
+  mutex.lock
+    master_waits = false;
+    master_sema.wait      // slave waits to get woken up
+  mutex.unlock
+
+http://itk.org/migrationv4/index.php?action=artikel&cat=3&id=108&artlang=en
+
+***** Restartable registration *****
+
+Here is how it looks using ITK methodology
+
+reg_func:
+  halt_sem.Down();
+  // do registration
+  halt_sem.Up ();
+
+start_registration:
+  halt_sem.Initialize (1);
+  id = spawn thread(reg_func);
+  return;
+
+halt_registration:
+  halt_sem.Down();
+  return;
+
+wait_for_complete:
+  threader.TerminateThread (id)
+
+
+***** ITK and plastimatch direction cosines *****
+
+There are two ways to have a rotated volume.  The direction cosines 
+of the volume might be inherently rotated, or there might be a rigid 
+transform associated with it.
+
+ITK direction cosines are stored in the columns of the array.
+In other words, if the (DICOM) orientations are A, B, and C 
+for the i (fastest), j, and k (slowest) indices, then ITK stores 
+them like this:
+
+  itk_dc[0][0] = A[0];
+  itk_dc[0][1] = B[0];
+  itk_dc[0][2] = C[0];
+  itk_dc[1][0] = A[1];
+  itk_dc[1][1] = B[1];
+  itk_dc[1][2] = C[1];
+  itk_dc[2][0] = A[2];
+  itk_dc[2][1] = B[2];
+  itk_dc[2][2] = C[2];
+
+The plastimatch direction cosines are stored in a one-dimensional
+array, also by columns.  
+
+  dc[0] = A[0];
+  dc[1] = B[0];
+  dc[2] = C[0];
+  dc[3] = A[1];
+  dc[4] = B[1];
+  dc[5] = C[1];
+  dc[6] = A[2];
+  dc[7] = B[2];
+  dc[8] = C[2];
+
+These are then converted into the "step" and "proj" matrices.
+
+  step[0] <- spacing[0] * dc[0] == spacing[0] * itk_dc[0][0]
+  step[1] <- spacing[1] * dc[1] == spacing[1] * itk_dc[0][1]
+  step[2] <- spacing[2] * dc[2] == spacing[2] * itk_dc[0][2]
+  step[3] <- spacing[0] * dc[3] == spacing[0] * itk_dc[1][0]
+  ...
+  step[8] <- spacing[2] * dc[8] == spacing[0] * itk_dc[2][2]
+
+To convert an index into a position, you do this:
+
+  pos[0] = origin[0] + idx[0]*step[0] + idx[1]*step[1] + idx[2]*step[2]
+  pos[1] = origin[1] + idx[0]*step[3] + idx[1]*step[4] + idx[2]*step[5]
+  pos[2] = origin[2] + idx[0]*step[6] + idx[1]*step[7] + idx[2]*step[8]
+
+***** How to delete items from conquest *****
+
+./dgate --deletepatient:patid
+
+Note: supports wildcards, such as:
+
+./dgate --deletepatient:0522*
+
+***** Subsampling framework *****
+
+legacy vox subsampling      Voxel binning, new voxel center at bin center
+new vox subsampling         Convert into standard resampling, allow fraction
+mm resampling               Choose by mm
+pct resample                Choose by pct of original voxels
+dim resample                Choose by number of voxels
+
+res,res_vox,ss
+res_mm
+res_pct
+res_dim
+
+***** When is itk vs native resample used? *****
+
+native:
+xform.cxx                       resample gpuit vfs
+demons,bspline,transl           registration subsampling
+
+itk:
+everywhere else
+
+
+***** How to detect a "Null" smart pointer *****
+
+Just like a regular pointer.
+
+Plm_image::Pointer p;
+if (p) {
+   /* Will not be executed because p is null */
+}
+
+You can return a null pointer like this:
+
+if (error) {
+  return Plm_image::Pointer();
+}
+
+***** Plastimatch 2.0 *****
+
+Proposal #1, extend existing language.  The below is an example,
+but many improvements could be made.
+
+[GLOBAL]
+fixed[0]=image_1a.mha           // support for multi-planar registration
+fixed[1]=image_1b.mha
+moving[0]=image_2a.mha
+moving[1]=image_2b.mha
+$aux=image_1a_mask.mha          // load image into a variable
+img_out[0]=warped_1a.mha
+
+[FILTER]
+action=dmap                     // could be lua script here...
+input_1=$fixed[0]               // set input from variable
+input_2=$aux                    // set input from variable
+input_3=image_1c.mha            // load image into a variable
+$f1=$fixed[0]                   // save a copy of the old fixed[0]
+fixed[0]=$output                // overwrite fixed[0] with filter output
+
+[STAGE]
+xform=translation
+optim=rsg
+max_its=30
+res=4 4 2
+
+[RESUME_STAGE]                  // continue existing stage (yuck)
+max_its=50
+
+[STAGE]
+fixed[0]=$f1                    // replace fixed image for this stage
+xform=bspline
+
+Proposal #2, extend lua scripting.  Something like this:
+
+-- Global (inputs)
+r = Registration()
+r:fixed[0] = Image.load("image_1a.mha")
+r:fixed[1] = Image.load("image_1b.mha")
+r:moving[0] = Image.load("image_2a.mha")
+r:moving[1] = Image.load("image_2b.mha")
+aux = Image.load("image_1a_mask.mha")
+
+-- Filter
+f1 = fixed[0]
+r:fixed[0] = Dmap:get_output (r:fixed[0], aux, Image.load("Image_1c.mha"))
+
+-- Stage
+s = r:Stage ()
+s:xform = 'translation'
+s:optim = 'rsg'
+s:max_its = 30
+s:res = '4 4 2'
+s:run()
+
+-- Stage (continuation)
+s:max_its = 30
+s:run()
+
+-- Stage
+s = r:Stage(s)
+s:fixed[0] = f1
+s:xform = 'bspline'
+s:run()
+
+-- Global (outputs)
+r:get_output():save("warped_1a.mha")
+
+
+***** What to do about const smart pointers *****
+
+(Your answer here)
+
+
+***** A trick for forward declaration of nested classes *****
+
+http://stackoverflow.com/questions/2600385/c-nested-class-forward-declaration-issue
+
+(Not the first answer that was accepted though.  The second one 
+is the one you want.)
+
+
+***** Rt_study API example *****
+
+/* Example #1, simple save using images */
+itk::Image<short,3>::Pointer itk_image;
+itk::Image<float,3>::Pointer itk_dose;
+itk::Image<unsigned char,3>::Pointer itk_structure_1;
+itk::Image<unsigned char,3>::Pointer itk_structure_2;
+
+Rt_study rt_study;
+rt_study.set_image (itk_image);
+rt_study.set_dose (itk_dose);
+rt_study.add_structure (itk_structure_1, "Body", "255\\0\\0");
+rt_study.add_structure (itk_structure_2, "Tumor", "0\\255\\0");
+rt_study.save ("output_directory");
+
+
+/* Example #2, save structure set that references existing CT image */
+Rt_study rt_study;
+rt_study.add_structure (itk_structure_1, "Body", "255\\0\\0");
+rt_study.add_structure (itk_structure_2, "Tumor", "0\\255\\0");
+
+const char *study_uid, *ct_series_uid, *for_uid;
+Rt_study_metadata::Pointer = rt_study.get_study_metadata();
+rt_study_metadata.set_study_uid (study_uid);
+rt_study_metadata.set_ct_series_uid (ct_series_uid);
+rt_study_metadata.set_frame_of_reference_uid (for_uid);
+rt_study_metadata.set_study_metadata (0x0010, 0x0010, "PATIENT^NAME");
+rt_study_metadata.set_study_metadata (0x0010, 0x0020, "PATIENT^ID");
+
+itk::Image<short,3>::Pointer itk_image;
+rt_study_metadata.set_image_header (itk_reference_image);
+for (int slice = 0; slice < num_slices; slice++) {
+    rt_study_metadata.set_slice_uid (slice, uid_string[slice]);
+}
+
+rt_study.save ("output_directory");
+
+
+***** Which metadata goes where? *****
+
+PatientPosition ?
+StudyID ?
+
+***** Slice list, slice index *****
+
+Slice_list
+    bool m_loaded;
+    Plm_image_header m_pih;
+    std::vector<Pstring> m_ct_slice_uids;
+
+Slice_index
+    bool m_loaded;
+    Plm_image_header m_pih;
+    Metadata m_demographics;
+    Pstring m_study_id;
+    Pstring m_ct_study_uid;
+    Pstring m_ct_series_uid;
+    Pstring m_ct_fref_uid;
+    std::vector<Pstring> m_ct_slice_uids;
+
+Dicom_rt_study --> Rename to Rt_study_metadata
+    bool m_loaded;
+    Plm_image_header m_pih;
+
+    std::string date_string;
+    std::string time_string;
+    std::string ct_series_uid;
+    std::string dose_instance_uid;
+    std::string dose_series_uid;
+    std::string for_uid;
+    std::string rtss_instance_uid;
+    std::string rtss_series_uid;
+    std::string study_uid;
+    Slice_list slice_list;
+
+    Metadata study_metadata;
+    Metadata image_metadata;
+    Metadata rtss_metadata;
+    Metadata dose_metadata;
+
+-- References
+
+Slice_index
+    cxt_io
+        UIDs are loaded and saved
+    dcmtk_rdd
+    dicom_util
+    gdcm1_dose
+    gdcm1_rdd
+    gdcm1_rtss
+        UIDs, image header, demographics are loaded
+    itk_dicom_save
+    itk_image_save (cxx only)
+    plm_image
+    rtss
+    xio_ct (h only)
+    xio_ct_transform
+    rt_study
+    segmentation
+
+Slice_list
+    dicom_rt_study
+    rtss
+
+
+***** Rtss, rtss_structure_set, ... *****
+
+These names are lousy.  Let's get some new names.
+
+DICOM officially refers to the set of all structures 
+as a "RT Structure Set".  The image associated with the structures 
+is called a "Contour Image Sequence".  A single structure is 
+called an "ROI".  A single polyline is called an "ROI Contour".
+
+Proposed names (polyline types)
+
+  - Rtss_contour          replaces Rtss_polyline
+  - Rtss_roi              replaces Rtss_structure
+  - Rtss                  replaces Rtss_structure_set
+
+Proposed names (image & polymorphic types)
+
+  - Segmentation          replaces Rtss
+  - Segmentation_image    replaces Ss_img
+
+
+***** Smart pointers part 3 *****
+
+Things to fix
+
+- Plm_image::steal_volume () should be removed
+- Rtds::load_dcmtk (const char *dicom_path) - need to be fixed
+
+
+***** Smart pointers redux *****
+
+Prefer to have "internal" smart pointers
+
+Dlib has 3 kinds
+
+#include "smart_pointers/scoped_ptr.h"
+#include "smart_pointers/shared_ptr.h"
+#include "smart_pointers/weak_ptr.h"
+
+- shared_ptr is a standard smart pointer
+- weak_ptr is a shared_ptr that can go away; you make a special call to 
+  see if it's still there
+- scoped_ptr doesn't have reference counting
+
+***** DICOM re-org 2013-03-24 *****
+
+Future plan:
+
+Dicom_rt_study
+  -> Various metadata
+  -> Dcmtk_rt_study
+    -> Backpointer to Dicom_rt_study
+    -> More metadata
+    -> Dcmtk_series (dose) // currently in dcmtk_loader
+    -> Dcmtk_series (rtss) // currently in dcmtk_loader
+    -> Dcmtk_series (img)  //??
+  -> cxt    // currently in dcmtk_loader
+  -> dose   // currently in dcmtk_loader
+  -> img    // currently in dcmtk_loader
+
+***** MABS re-org *****
+
+Prep/training steps:
+(1) convert file format
+(2) do registrations, compute dice, possibly optimize registration
+(3) warp structures and get distance maps
+(4) optimize voting parameters (rho, sigma, thresh)
+
+Workflow -- optimizing registrations
+------------------------------------
+convert file format
+for each image pair
+  for each registration parameter
+    register
+    warp structures
+    compute distance maps
+    compute dice
+    tabulate results
+  choose best registration parameter
+
+Workflow -- optimizing voting parameters
+----------------------------------------
+convert file format
+for each image pair
+  register
+  warp structures
+  compute distance maps
+for each structure
+  for each voting parameter
+    for each image pair
+      load dmap
+      vote
+    save weights
+    for each thresh
+      threshold image
+      compute dice
+      save results
+
+Workflow -- standard usage
+--------------------------
+for each image pair
+  register
+  warp structures
+  for each structure
+    compute distance maps
+    vote
+    threshold image
+  
+
+Other thoughts:
+(1) Make dmap, dice faster
+
+***** Geometry chooser *****
+
+Two use cases.
+
+Use case one:
+- Manual overrides
+- Fixed image
+- Input image
+
+Use case two:
+- Manual overrides
+- Fixed image
+- Reference image
+- Compare image
+
+***** DICOM re-org *****
+
+Need patient, study, series hierarchy.
+
+------ Plm_patient
+  ---- Plm_study
+    -- Plm_series
+
+This should be allowed to be created via dicom.  It might therefore 
+be required to move rtss/rtds code into base.  These files would move:
+
+Idea (1).  Plm_series has a "type" field, which tells us if the 
+item is an image, rtss, etc.
+
+Idea (2).  Plm_series is a base type, subclassed by image, rtss, etc.
+
+***** Viscous *****
+
+It seems that only certain versions of thrust work with certain versions 
+of cuda.
+
+Thrust 1.6.0 does not work with CUDA 3.0
+Thrust 1.4.0 does work with CUDA 3.0
+
+***** Metadata Mark II *****
+
+Do we need separate metadata items for image, dose, and rtstruct?
+Not sure.  Here are the considerations:
+
+- ITK dicom write (image) requires setting metadata into itk image
+- CXT, xio formats need somewhere to store the metadata
+- RDD has its own metadata
+
+Where does RTSS metadata get used (version 1.5.9)
+
+- cxt_io.cxx
+  - name, id, sex (rtss meta)
+  - study id, ct study id, ct series id, ct for id (rdd)
+- gdcm1_rtss.cxx
+  - name, id, sex, series description
+  - study id, ct study id, ct series id, ct for, ct slice ids (rdd)
+
+Where RDD metadata get used (version 1.5.9)
+
+- rtds.cxx
+  - name, id, sex, patient position
+  - (xio ct transform -- this should be part of metadata??)
+- gdcm1_rdd.cxx
+  - name, id, sex, patient position
+
+Options: 
+
+(a) All in rtds.
+    PRO: simple, heterogenous data structure makes sense
+    CON: registration code (and other code which doesn't use rtds) 
+    	 still needs somewhere to store metadata
+(b) Separate metadata for each item
+    PRO: more similar to dicom, works well with registration code
+    CON: difficult to synchronize
+
+Final choice:
+
+- Separate metadata for image, struct, dose
+- Special structure for ct uids, etc.  These go in rtds instead of image
+  (as they are now), because we might have a referenced dicom dir, without 
+  an actual image.
+
+***** Debian notes *****
+
+In order to install ITK 3.20, you need to use unstable repository.
+Here is what you need:
+
+  sudo apt-get -V install -t unstable \
+    libgdcm2-dev libinsighttoolkit3-dev libvtk5-dev
+
+It seems that Debian ITK was built with GDCM 2.X instead of GDCM 1.X.
+Too bad.
+
+***** Organization of learning code *****
+
+Use cases:
+
+image: can be 2d (slice) or 3d (vol)
+pos: can be 1d (slice loc), 2d (in-plane), or 3d (position)
+binary (in/out, male/female)
+tri-state (above, near, below)
+continuous tri-state (above (+1), near (-1 to +1), below (-1))
+conditional position (inside (with position), outside (without position))
+
+- T-spine
+
+slice -> slice loc
+slice -> in-plane
+
+- Lung apex
+
+slice -> continuous tri-state
+
+- T-spine
+
+slice + continuous tri-state -> slice loc
+
+Finding training data:
+
+T-spine: Choose only slice or vol centered at fiducial
+T-spine: Interpolate fiducials, use all slices
+Lung apex: Choose only slice at apex
+Lung apex: Choose all slices, compute distance to apex
+Lung volume: Choose all slices intersecting mask
+
+Transition plan:
+
+Use hard-coded training routines for different use cases
+
+plastimatch autolabel-train \
+    --task t-spine-v1 \
+    --input dir \
+    --output net
+
+The data needs a hierarchy, to allow for cross-validation on a 
+patient-by-patient basis.  Call the data for a single patient 
+an Autolabel_data_item
+
+Autolabel_data_item {
+  dlib::matrix inputs;
+  dlib::matrix outputs;
+}
+
+Autolabel_data {
+  std::list<Autolabel_data_item>
+  choose_subset ()
+}
+
+Autolabel_trainer {
+  Autolabel_data ad;
+  train (parameter_range) {
+    for (num_trials) {
+      dlib::matrix training_data = ad.choose_subset();
+    }
+  }
+}
+
+
+***** Organization for irregular volume (transition plan) *****
+
+(1) Use native, not ITK
+
+(2) Only support volumes with irregular slice spacing.  No support 
+for things like changing pixel size or direction cosines
+
+(3) Add irregular volume as a member of Volume
+
+class Volume {
+      /* Regular volume stuff */
+      int npix;
+      void *data;
+
+      /* Irregular volume stuff */
+      float *irr_spacing;
+      void **irr_data;
+};
+
+***** Writing a Slicer4 loadable module *****
+
+(1) Use the wizard to make a template
+
+export SD=$HOME/build/slicer-4/Slicer4
+python ${SD}/Scripts/ModuleWizard.py \
+       --template ${SD}/QTModules/ModuleTemplate \
+       --target MY_MODULE_NAME \
+       MY_MODULE_NAME
+
+(2) Modify CMakeLists.txt
+
+find_package (Slicer QUIET)
+if (SLICER_FOUND)
+  include ("${Slicer_USE_FILE}")
+  if (SLICER_IS_SLICER4)
+    add_subdirectory (QTModules/MY_MODULE_NAME)
+  endif ()
+endif ()
+
+(3) Copy over TestingMacros.h
+
+cp ${SD}/TestingMacros.h MY_MODULE_NAME
+
+At this point you can compile, and it runs.  But it builds directly 
+into the slicer directory.  This can be (partly) defeated using 
+the following strategy, but it is a moot point because you can't yet 
+set the module search path.  Here is the strategy:
+
+(4) Defeat Slicer overwriting CMAKE_* variables
+
+  if (Slicer_USE_FILE)
+    set (OLD_CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+    set (OLD_CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
+    set (OLD_CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY})
+
+    include ("${Slicer_USE_FILE}")
+
+    set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OLD_CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+    set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OLD_CMAKE_LIBRARY_OUTPUT_DIRECTORY})
+    set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OLD_CMAKE_ARCHIVE_OUTPUT_DIRECTORY})
+  endif ()
+
+(5) Edit CMakeLists.txt files generated by the python script.  
+There are two files to edit: MY_MODULE_NAME/CMakeLists.txt, 
+and MY_MODULE_NAME/Logic/CMakeLists.txt.
+
+(5a) For MY_MODULE_NAME/CMakeLists.txt, do the following:
+
+set (lib_name qSlicer${qt_module_name}Module)
+set_target_properties (${lib_name} PROPERTIES
+  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${Slicer_INSTALL_QTLOADABLEMODULES_BIN_DIR}"
+  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${Slicer_INSTALL_QTLOADABLEMODULES_LIB_DIR}"
+  ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${Slicer_INSTALL_QTLOADABLEMODULES_LIB_DIR}"
+  )
+
+(5b) For MY_MODULE_NAME/Logic/CMakeLists.txt, do the following:
+
+SlicerMacroBuildModuleLogic(
+  NAME ${module_logic_name}
+  DISABLE_WRAP_PYTHON
+  EXPORT_DIRECTIVE ${module_logic_export_directive}
+  INCLUDE_DIRECTORIES ${module_logic_include_directories}
+  SRCS ${module_logic_SRCS}
+  TARGET_LIBRARIES ${module_logic_target_libraries}
+  )
+
+set_target_properties (${module_logic_name} PROPERTIES
+  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${Slicer_INSTALL_QTLOADABLEMODULES_BIN_DIR}"
+  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${Slicer_INSTALL_QTLOADABLEMODULES_LIB_DIR}"
+  ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${Slicer_INSTALL_QTLOADABLEMODULES_LIB_DIR}"
+  )
+
+***** Proton dose ideas *****
+
+Stage 1(a): Compute pencil beam in standard grid
+(z direction is pdd in water)
+(x-y direction is scatter in water, or just 1-d with distance)
+
+Stage 1(b): Compute RPL in interpolated coordinate system
+(z axis is beam axis)
+(x-y perpendicular to beam, arbitrary v-up vector)
+
+Stage 2: For each voxel
+  a) Look up primary in RPL grid
+  b) Convolve to find scatter within x-y axis of primary grid (ignoring tilt)
+
+***** File browser design *****
+
+http://www.xvsxp.com/files/file_browsing.php
+http://rixstep.com/4/0/xfile/ss.shtml
+http://www.ragesw.com/products/explorer/screenshots/1/
+
+***** Writing to stdout in Qt *****
+
+    QTextStream(stdout) << QString("foo") << "\n";
+
+***** dgate quite ref (for wormwood) *****
+
+cd ~/build/conquest-1.4.15
+./dgate &
+
+***** dcmtk quick ref *****
+
+Run the dicom server like this:
+
+  $ dcmqrscp
+
+It will read a file "dcmqrscp.cfg" in the current directory, which is used 
+to set user/group, port, AET, storage directory.  NOTE: Be very careful 
+about extra spaces in the list of remote AETs.  Here the file I used 
+for these tests:
+
+--- begin here ---
+NetworkType     = "tcp"
+NetworkTCPPort  = 9885
+MaxPDUSize      = 16384
+MaxAssociations = 16
+Display         = "no"
+UserName        = "gsharp"
+GroupName       = "gsharp"
+HostTable BEGIN
+entry1           = (MOVESCU, localhost, 19530)
+entry2           = (STORESCP, localhost, 19335)
+HostTable END
+VendorTable BEGIN
+VendorTable END
+AETable BEGIN
+READWRITE     /home/gsharp/projects/dicom-test/junk RW (10, 1024mb)  ANY
+AETable END
+--- end here ---
+
+Send files to database like this:
+
+  $ storescu -aec READWRITE localhost 9885 *.dcm
+
+Query the database like this:
+
+  $ findscu -P -k 0010,0010 -k 0008,0052=PATIENT -aec READWRITE localhost 9885
+
+Retrieve from the database like this:
+
+  $ movescu -v --patient -aet MOVESCU -aem MOVESCU -aec READWRITE --port 19530 -k 0008,0052=PATIENT -k 0010,0020=PL811332912032439 localhost 9885
+
+Or like this:
+
+  $ storescp 19335 &
+  $ movescu -v --patient -aet MOVESCU -aem STORESCP -aec READWRITE -k 0008,0052=PATIENT -k 0010,0020=PL811332912032439 localhost 9885
+
+
+***** Proposed engineering changes: Aug 1, 2009 *****
+
+1) Reduce number of executables
+
+   Old:
+     plastimatch [options]
+     dicom_to_mha [options]
+     warp_mha [options]
+   New:
+     plastimatch register [options]
+     plastimatch convert [options]
+     plastimatch warp [options]
+
+   "Simple" executables such as bspline.exe, drr_c.exe etc. will not 
+   be merged.
+
+2) Add threading options to plastimatch
+
+   Old:
+     implementation=gpuit_cpu
+   New:
+     implementation=plastimatch
+     threading=openmp
+     max_threads=2
+
+3) Remove gpuit sub-library, merge into plastimatch1.lib
+
+4) Move source code to src/ subdirectory
+
+***** Consistency of indices *****
+
+By convention:
+
+   k, z = slowest moving index (usually IS)
+   j, y = middle moving index (usually AP)
+   i, x = fastest moving index (usually RL)
+
+Arrays which hold things like the dimensions are indexed as follows:
+
+   dim[0] = dimensions of fastest moving index
+   dim[1] = dimensions of middle moving index
+   dim[2] = dimensions of slowest moving index
+
+Loops should be nested from slowest index to fastest index.  
+Therefore, the correct nesting is:
+
+    for (k = 0; k < fixed->dim[2]; k++) {
+	for (j = 0; j < fixed->dim[1]; j++) {
+	    for (i = 0; i < fixed->dim[0]; i++) { ... } } }
+
+Embedded indices, such as (x,y,z) coordinates of the vector 
+field at a voxel, should be in the order of x, then y, then z.
+
+When you pass indices into a function it should be 
+in order x, then y, then z.  For example:
+
+    int volume_index (int* dims, int i, int j, int k);
+
+***** How to compile libf2c *****
+
+Edit libf2c/makefile.vc, and change:
+  CFLAGS = -DUSE_CLOCK -DMSDOS -DNO_ONEXIT -Ot1
+
+To:
+  CFLAGS = -DUSE_CLOCK -DMSDOS -DNO_ONEXIT -Ot1 -MD
+
+Edit libf2c/fio.h, and comment out:
+  extern int isatty(int);
+
+***** How to compile the .br into .cpp *****
+
+The default compile is simply: 
+   brcc -o outfile.cpp infile.br
+
+The FDK code doesn't work for PS20 & ARB targets.
+
+We can use -p flag to set the platform.
+   brcc -o outfile.cpp
+
+***** How to compile brook on cygwin/g++ *****
+
+1) The config/DetectOS thing always gets Windows_NT because 
+the $OS environment variable is standard in Windows.
+
+2) Need to create a new *.mk file  (To be done)
+
+3) It seems to build OK, but doesn't completely solve the 
+problem.  fxc still requires windows style paths, maybe cgc does too.
+
+***** Threads vs OpenMP *****
+
+http://www.intel.com/cd/ids/developer/asmo-na/eng/technologies/threading/hyperthreading/53797.htm
+
+https://computing.llnl.gov/tutorials/openMP/
+
+On GCC:
+  gcc -fopenmp openmp_test.c
+
+Visual studio 2005 supports OpenMP 2.0
+  cl /openmp
+
+Express version does not support OpenMP (except as described below)
+  http://blog.codekills.net/archives/25-OpenMP-and-Visual-C++-the-free-way-sorta.html
+MinGW gcc OpenMP is still not fully supported
+  http://www.nabble.com/OpenMP-and-shared-libgcc-td17516165.html
+
+***** What is the deal with ITK's oriented images? *****
+
+http://www.itk.org/pipermail/insight-users/2008-August/027102.html
+
+Now, ITK 3.10.2 has two flags (earlier version are similar).
+The use of these flags are not well described.
+
+ITK_USE_ORIENTED_IMAGE_DIRECTION
+ITK_IMAGE_BEHAVES_AS_ORIENTED_IMAGE
+
+***** Logging *****
+
+For "C" logging, I found 2 projects:
+
+- log4c (LGPL license)
+- pantheios (BSD license)
+
+***** Timing *****
+
+Options:
+1) clock()
+2) time()
+3) gettimeofday()
+4) QueryPerformanceCounter  // windows only
+5) OpenMP timer
+6a) clock_gettime(CLOCK_MONOTONIC)
+6b) clock_gettime(CLOCK_REALTIME)
+6c) clock_gettime(CLOCK_HIGHRES) // solaris only?
+
+http://en.wikipedia.org/wiki/Real-time_clock
+http://en.wikipedia.org/wiki/High_Precision_Event_Timer
+
+http://cboard.cprogramming.com/c-programming/106025-clock-vs-gettimeofday.html
+http://fixunix.com/linux/6645-negative-response-time-gettimeofday.html
+http://code.google.com/p/high-resolution-timer/source/browse/trunk/highrestimer/c%2B%2B_library/wraper_and_library/timer_library.c
+
+***** SVN eol-goop *****
+
+Put the following in your ~/.subversion/config
+
+CMakeLists.txt = svn:eol-style=native;svn:mime-type=text/plain
+Makefile = svn:eol-style=native;svn:mime-type=text/plain
+README* = svn:mime-type=text/plain;svn:eol-style=native
+readme* = svn:mime-type=text/plain;svn:eol-style=native
+
+*.tga = svn:mime-type=image/tga
+*.bat = svn:mime-type=text/plain;svn:eol-style=CRLF
+*.br = svn:eol-style=native;svn:mime-type=text/plain
+*.c = svn:eol-style=native;svn:mime-type=text/plain
+*.cmake = svn:mime-type=text/plain;svn:eol-style=native
+*.cmd = svn:mime-type=text/plain;svn:eol-style=CRLF
+*.cpp = svn:eol-style=native;svn:mime-type=text/plain
+*.cu = svn:eol-style=native;svn:mime-type=text/plain
+*.cxx = svn:eol-style=native;svn:mime-type=text/plain
+*.dsp = svn:eol-style=CRLF;svn:mime-type=text/plain
+*.dsw = svn:eol-style=CRLF;svn:mime-type=text/plain
+*.f = svn:eol-style=native;svn:mime-type=text/plain
+*.h = svn:eol-style=native;svn:mime-type=text/plain
+*.jpg = svn:mime-type=image/jpeg
+*.m = svn:eol-style=native;svn:mime-type=text/plain
+*.pl = svn:eol-style=native;svn:mime-type=text/plain;svn:executable
+*.png = svn:mime-type=image/png
+*.pm = svn:eol-style=native;svn:mime-type=text/plain
+*.sh = svn:mime-type=text/plain;svn:eol-style=LF;svn:executable
+*.txt = svn:mime-type=text/plain;svn:eol-style=native
+*.xml = svn:mime-type=text/xml;svn:eol-style=native
diff --git a/doc/README.TXT b/doc/README.TXT
new file mode 100755
index 0000000..0fb4db9
--- /dev/null
+++ b/doc/README.TXT
@@ -0,0 +1,7 @@
+To build the sphinx documentation, do this:
+
+sphinx-build -b html source_dir build_dir
+
+For example:
+
+sphinx-build -b html ~/work/plastimatch/doc/sphinx/ ~/work/web-plastimatch/
diff --git a/doc/ROADMAP.TXT b/doc/ROADMAP.TXT
new file mode 100644
index 0000000..0e32029
--- /dev/null
+++ b/doc/ROADMAP.TXT
@@ -0,0 +1,52 @@
+2.0
+
+Features
+*  All registration parameters have auto defaults
+*  Long option alias
+*  Multi-planar registration
+*  Multi-similarity registration
+*  Overlap fraction penalty for native registration methods
+*  Full support for irregular slice spacing
+*  Superbuild
+*  Non-aligned B-spline LUT
+
+Backward incompatible changes
+*  Remove short options
+*  DRR/FDK/dose calc use IEC coordinates
+*  GDCM becomes deprecated
+*  Standalone executables will no longer be available, instead 
+   they are merged into plastimatch executable:
+   vf-invert, landmark-warp, drr, fdk, wed
+*  Metric mi will default to Mattes for ITK methods (done)
+*  Automatic tuning of regularization lambda as default (?)
+*  xform=align_center -> xform=translation, optim=align_center
+
+Documentation
+*  Registration tutorial based on NA-MIC case library
+*  MABS tutorial
+
+Cleanup
+*  Remove Pstring/bstring (done)
+*  Remove unnecessary third party libraries (f2c, nocedal, sqlite?, lua?)
+*  Evaluate if duplication between Stage_parms and Bspline_parms 
+   can be removed
+*  Re-evaluate B-spline loop template
+*  DICOM uses module system
+
+Wishlist
+
+*  Autolabel (la,tsv1,tsv2)
+*  Copy-free conversion between itk and native volumes
+*  RTK wrapping
+*  DRR/FDK dicom
+*  Evaluate native vs itk jacobian
+*  MSVC express multicore strategy (done, MSVC 2013 and higher)
+*  Native rigid registration
+*  Normalized MI
+*  patient mask to support MRI
+*  plastimatch gradient magnitude (done)
+*  Proton/photon/electron dose calculation
+** Add multi-beam to command file (done)
+** Remove duplication of code for photons
+*  RTPLAN support
+*  Standalone GUI
diff --git a/doc/STYLE_GUIDE_1.TXT b/doc/STYLE_GUIDE_1.TXT
new file mode 100644
index 0000000..ee4e5c4
--- /dev/null
+++ b/doc/STYLE_GUIDE_1.TXT
@@ -0,0 +1,172 @@
+Introduction
+------------
+This is part 1 of the style guide.  It concerns how the C/C++ syntax 
+should be formated within a file.
+
+Unified coding style is important because it eases software review 
+and maintenance.  This file specifies guidelines which can be broken 
+for specific cases, but should be adhered to as closely as possible.
+
+Plastimatch can't be properly formatted automatically by GNU indent 
+or astyle.  Uncrustify will do a decent job; an uncrustify configuration 
+file is included in the distribution.
+
+Files
+-----
+Source code files should use Unix-style end-of-line characters.
+All source code should be written in 7-bit clean ASCII.
+
+Third-party code
+----------------
+Third-party code should be placed in separate files, and should be 
+clearly identified, so that no mistake in licensing occurs.
+
+Indentation
+-----------
+Indentation should be 4 spaces.  Tabs are allowed, but you must set 
+your tab stop to 8 spaces.  Note that this is not the default 
+for Microsoft Visual Studio compilers, so you should adjust your setting.
+
+Line breaking
+-------------
+Code should be limited to 80 columns when possible.  Use typedefs to 
+assist in this process.
+
+Curly braces
+------------
+Use GNU curly brace style (curly on its own line) for function 
+definitions, and K&R curly brace style (curly on same line as conditional)
+everywhere else.
+
+Always use curly braces for if/do/while/for statements, even if there 
+is only one statement in the block.
+
+For a simple else clause, cozy it up with the previous right curly.  
+For testing multiple cases, move the else keyword to a new line.
+
+Examples:
+
+    int
+    foo (int bar)
+    {
+        if (bar == 3) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    int
+    foo (int bar)
+    {
+        if (bar == 3) {
+            return 1;
+        }
+        else if (bar == 5) {
+            return 2;
+        }
+	else {
+            return 0;
+	}
+    }
+
+Switch statement
+----------------
+Case labels are not indented in the switch statement.  A default label 
+is required (even if there is no code).
+
+    switch (x) {
+    case a:
+        code;
+    case b:
+        code;
+    case c:
+    default:
+        code;
+    }
+
+Function definitions
+--------------------
+Declaritors and return values go on a separate line (GNU style):
+
+    static int
+    foo (int bar)
+    {
+
+Use explicit "void" in the argument list if there are no arguments
+is optional:
+
+    void
+    foo (void)
+    {
+
+Horizontal whitespace
+---------------------
+Horizontal whitespace is used between operators and operands.  
+Conditionals should be laid out like this:
+
+    if (bar == 3 && baz > 4) {
+
+Function calls like this:
+
+    foo (a, b);
+
+When there are no arguments, you may omit the space:
+
+    foo();
+
+Pointers and references
+-----------------------
+As per linux kernel style, put the '*' adjacent to the data name or 
+function name and not adjacent to the type name.
+
+    char *foo;
+    char *bar (char *baz, char **boo);
+
+The reason for this rule is it allows the declaration of multiple 
+variables of the same type in a single statement.
+
+    char *p, *q, *pv, *pq;
+
+However, references go together with the type rather than the name.
+
+    char& linux_banner = s1;
+    char& bar (char& baz, char*& boo);
+
+Vertical whitespace
+-------------------
+A single empty line is used between functions or between 
+two groups of code.
+
+Identifier naming
+-----------------
+Prefer English words to abbreviations.  Words are separated by 
+underscores.  Use lowercase for function names, variable names, 
+and structure names.  Use all upper case for constants.
+
+    struct long_list {
+        int list_length;
+        void *list_items;
+    }
+
+    int
+    my_function (struct long_list employee_list)
+    {
+        const int DESIRED_LENGTH = 3;
+        employee_list->list_length = DESIRED_LENGTH;
+    }
+
+Names for typedefs and C++ classes should capitalize only the first letter.
+Member names are lower case.
+
+    class Volume_resizer {
+    public:
+        int foo;
+	void bar (void);
+    }
+
+    void
+    Volume_resizer::bar (void)
+    {
+        code;
+    }
diff --git a/doc/STYLE_GUIDE_2.TXT b/doc/STYLE_GUIDE_2.TXT
new file mode 100644
index 0000000..4d65f25
--- /dev/null
+++ b/doc/STYLE_GUIDE_2.TXT
@@ -0,0 +1,174 @@
+Introduction
+------------
+This is part 2 of the style guide.  It concerns how variables 
+and functions should be named to maintain a uniform look and feel.
+
+Include files
+-------------
+Include files should be in the following order:
+
+ 1) plm_config.h
+ 2) Standard C++ include files (sorted alphabetically)
+ 3) Standard C include files (sorted alphabetically)
+ 4) Third party library include files (grouped, sorted alphabetically 
+    within each group if possible)
+ 5) Plastimatch include files (sorted alphabetically)
+
+File names and function names
+-----------------------------
+Generally, files will come in pairs, one h file and one cxx file.  
+These pair of files will normally define a single public
+C++ class, and optionally will define a second private C++ class.
+
+The public class name will have the same name as the h and cxx file. 
+For example, the class Plm_image is defined in plm_image.h.
+
+The private class will include the suffix "_private", and is defined 
+in the cxx file.  For example, class Plm_image_private is defined in 
+plm_image.cxx.
+
+Layout of a public class definition
+-----------------------------------
+The first entries of the class definition specify the API 
+linkage, smart pointer support, and private class data.
+Next are the constructors and destructors.  For example:
+
+class PLMBASE_API Plm_image {
+public:
+    SMART_POINTER_SUPPORT (Plm_image);
+    Plm_image_private *d_ptr;
+public:
+    Plm_image ();
+
+The token PLMBASE_API is defined in plmbase_config.h, and is 
+reqired for linking with the library on windows.  The macro 
+SMART_POINTER_SUPPORT specifies that this class allows the use 
+of smart pointers, and is defined in smart_pointer.h.
+
+Native layer, itk layer, wrapper layer [Proposed, not implemented]
+------------------------------------------------------------------
+The following prefixes should be used for these three layers:
+
+  itk_xxxx            ## ITK layer, makes itk function calls.
+  xxxx                ## Native layer
+  plm_xxxx            ## Wrapper layer
+
+Alternatives:
+
+  plc_xxx             ## Native layer
+  plm_core_xxx        ## Native layer
+  plw_xxx             ## Wrapper layer
+
+For images, we currently use this following:
+
+  itk_image, itk      ## ITK layer
+  volume              ## Native layer
+  plm_image, pli      ## Wrapper layer
+
+For complex images (multiple irregular spacings), consider the following:
+
+  itk_volume, itk      ## ITK layer
+  volume               ## Native layer
+  plm_volume, plv      ## Wrapper layer
+
+  plm_volset, plv      ## Wrapper layer (wrapper layer sufficient?)
+
+The image header would become:
+
+  itk_volume_header    ## ITK layer
+  volume_header        ## Native layer
+  plm_volume_header    ## Wrapper layer
+
+  plm_volset_header    ## Wrapper layer (wrapper layer sufficient?)
+
+Command line args, Function options [Proposed, not implemented]
+---------------------------------------------------------------
+Command line arguments are digested, and then placed in a structure 
+called Xxx_args.  Example:
+
+class Warp_args {
+    ...
+};
+
+Complicated functions which are controlled by a structure full of 
+options will use a structure called Xxx_opts.  Example:
+
+typedef struct bspline_opts Bspline_opts;
+struct bspline_opts {
+    ...
+};
+
+N.b. The old way is to use the term "parms" for both types of structures.  
+
+N.b. Does this even make sense?  Cxt_to_mha can use the same structure 
+for both uses.
+
+Dim, dims, offset, origin, spacing
+----------------------------------
+To describe the position of an image in mm, use origin, not offset.  
+The term offset is used to describe an ROI in voxels.
+
+To describe the resolution of an image in voxels, use dim, not dims.
+
+The order of parameters should be alphabetic:
+
+  calling_a_function (dim, origin, spacing);
+
+Except that direction_cosines is last:
+
+  calling_a_function (dim, origin, spacing, direction_cosines);
+
+For itk routines, it should follow the same semantic order, 
+but it becomes non-alphabetic:
+
+  calling_a_function (region, origin, spacing, direction_cosines);
+
+Direction_cosines, direction_matrix, Itk_direction
+--------------------------------------------------
+A "direction matrix" is a raw matrix of nine numbers.
+The ITK version is called an Itk_direction.
+A Direction_cosine is a container that also holds the inverse.
+
+UChar, Char, ...
+----------------
+The ordering is: 
+    itk, then gpuit,
+    integers, then float, then vectors,
+    smaller, then larger,
+    unsigned, then signed.
+
+Plm_image, ITK image, Volume
+----------------------------
+Overloaded functions which return specific types should specify 
+them as follows:
+
+     object.get_image ();        // Returns Plm_image::Pointer
+     object.get_image_itk ()     // Returns most appropriate itk type
+                                 // (usually FloatImageType::Pointer)
+     object.get_volume ();       // Returns Volume::Pointer of most 
+                                 // appropriate type (usually float) 
+     object.get_vol ();          // Returns Volume*
+
+When appropriate, the functions may be suffixed to specify the 
+return type:
+
+     object.get_image_itk_float ();
+     object.get_volume_float ();
+
+
+Variable naming for indexing  [Proposed, not implemented]
+---------------------------------------------------------
+idx	index		one dimensional index
+ijk	coords		three dimensional index
+xyz	position	three dimensional position
+
+Fixed image voxel                   fijk[3], fidx <currently (fi,fj,fk),fv>
+Fixed world coord                   fxyz[3]
+Moving image voxel                  mijk[3], midx < ditto >
+Moving world voxel                  mxyz[3]
+Tile                                p[3], pidx
+Offset within tile                  q[3], qidx
+Control point                       (c[3]), cidx <currently (i,j,k), m>
+Coefficient array                   ?                <currently cidx>
+Multiplier LUT                      qidx
+Index LUT                           pidx
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bfcbded..416f887 100755
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -84,13 +84,13 @@ endif ()
 ##  Add subdirectories
 ##-----------------------------------------------------------------------------
 if (PLM_CONFIG_DEBIAN_BUILD)
-  set (PLM_BUILD_ISE OFF)
+  set (PLM_CONFIG_DISABLE_ISE ON)
   set (PLM_CONFIG_DISABLE_FATM ON)
   set (PLM_CONFIG_DISABLE_MONDOSHOT ON)
   set (PLM_CONFIG_DISABLE_REG23 ON)
 endif ()
 
-if (PLM_BUILD_ISE)
+if (NOT PLM_CONFIG_DISABLE_ISE)
   add_subdirectory (ise)
 endif ()
 
@@ -105,7 +105,11 @@ if (WIN32 AND NOT CYGWIN AND wxWidgets_FOUND AND DCMTK_FOUND
 endif ()
 
 if (NOT PLM_CONFIG_DISABLE_PLASTIMATCH)
-  add_subdirectory (plastimatch)
+  if (NOT ITK_FOUND)
+    message (STATUS "Plastimatch will not be built (ITK not found)")
+  else ()
+    add_subdirectory (plastimatch)
+  endif ()
 endif ()
 
 set (PLM_BUILD_ORAIFUTILS 1)
diff --git a/src/plastimatch/CHANGELOG.TXT b/src/plastimatch/CHANGELOG.TXT
index 4e3383c..03d0af6 100644
--- a/src/plastimatch/CHANGELOG.TXT
+++ b/src/plastimatch/CHANGELOG.TXT
@@ -1,3 +1,20 @@
+Version 1.6.3 (build 5341)
+Wed Jun 29 17:08:12 EDT 2016
+* WiX based windows install
+* Many proton dose enhancements
+* Many MABS enhancements
+* Gamma_gui and register_gui programs
+* DICOM metadata enhancements
+* Default MI metric for ITK implementations is now Mattes
+* Vector fields can now be saved as any ITK format
+* Multi-DRR options can now use any starting angle and can save details file
+* Dij matrix warping works again
+* You can now select gpuid in registration command file
+* Fix image convert failure for when resampling without supplied xform
+* Fix image resize crash
+* Fix for loading ITK volumes with non-zero index attributes
+* Fix DICOM loading RTDOSE with non-identity direction cosines
+
 Version 1.6.2 (build 5130)
 Tue Jul 28 14:38:04 EDT 2015
 * New MABS post-processing feature to fill holes and remove islands
diff --git a/src/plastimatch/CMakeLists.txt b/src/plastimatch/CMakeLists.txt
index fa6244c..6e68510 100644
--- a/src/plastimatch/CMakeLists.txt
+++ b/src/plastimatch/CMakeLists.txt
@@ -10,16 +10,27 @@ project (src_plastimatch)
 ##-----------------------------------------------------------------------------
 
 # Plastimatch software configuration options
-option (PLM_CONFIG_ALT_DCOS "Use alternative direction cosines rules" OFF)
-option (PLM_CONFIG_KEYHOLIZE "Enable RT structure keyholization" OFF)
-option (PLM_CONFIG_USE_SS_IMAGE_VEC
-  "Save structure sets as ITK UCHAR VEC images (in development)" ON)
-option (PLM_CONFIG_CLANG_COMPLETE "Generate .clang_complete for hipster Vim-ers" OFF)
-option (PLM_CONFIG_DISABLE_VISCOUS "Disable experimental viscous fluid registration algorithm" ON)
+option (PLM_CONFIG_CLANG_COMPLETE
+    "Generate .clang_complete for hipster Vim-ers" OFF)
+option (PLM_CONFIG_DISABLE_VISCOUS
+    "Disable experimental viscous fluid registration algorithm" ON)
 option (PLM_CONFIG_ENABLE_PLASTIMATCH_QT
-  "Enable experimental plastimatch_qt executable" OFF)
+    "Enable experimental plastimatch_qt executable" OFF)
+option (PLM_CONFIG_KEYHOLIZE "Enable RT structure keyholization" OFF)
 option (PLM_CONFIG_PREFER_PATCHED_ITK
-  "Prefer to use the patched version of certain ITK files" ON)
+    "Prefer to use the patched version of certain ITK files" ON)
+option (PLM_CONFIG_VOL_LIST
+    "Native support for volumes with irregular slice thicknesses" OFF)
+
+# Plastimatch legacy options
+option (PLM_CONFIG_LEGACY_BSPLINE_EXTEND
+  "Use legacy code for extending b-spline domain" OFF)
+option (PLM_CONFIG_LEGACY_BSPLINE_XFORM_IO
+  "Use legacy code for reading and writing b-spline xform files" ON)
+option (PLM_CONFIG_LEGACY_MI_METRIC
+  "For ITK metrics, the legacy implementation of the mi metric is Viola-Wells to Mattes" OFF)
+option (PLM_CONFIG_LEGACY_PROJ_GEO
+  "Use legacy method for specifying projection geometry" ON)
 
 ##-----------------------------------------------------------------------------
 ##  Give a warning for obsolete dicom libraries
@@ -37,7 +48,7 @@ endif ()
 ##-----------------------------------------------------------------------------
 set (PLM_CONFIG_USE_PATCHED_ITK 0)
 if (${ITK_VERSION} VERSION_LESS "4.1" AND PLM_CONFIG_PREFER_PATCHED_ITK)
-    set (PLM_CONFIG_USE_PATCHED_ITK 1)
+  set (PLM_CONFIG_USE_PATCHED_ITK 1)
 endif ()
 
 
@@ -154,27 +165,7 @@ if (NOT PLM_CONFIG_DISABLE_VISCOUS
 endif ()
 
 
-##-----------------------------------------------------------------------------
-##  OLD BUILD SYSTEM REMNANTS
-##-----------------------------------------------------------------------------
-# JAS 2012.05.15 -- needs to be merged then removed for new build system
-#if (FULL_PLASTIMATCH_BUILD)
-#  # Ugh.  ITK is stupid.  They quietly dropped support for cygwin. 
-#  # However, you can still get 3.20.0 to work.  You need to run 
-#  # "make ITKFEM" after running "make" to workaround ITK's broken CMake 
-#  # script:
-#  #  http://old.nabble.com/compile-error-using-cygwin-td30879187.html
-#  # See also:
-#  #  http://public.kitware.com/Bug/view.php?id=11659
-#  if (CYGWIN)
-#    set (PLASTIMATCH1_LIBRARY_DEPENDENCIES 
-#      ${PLASTIMATCH1_LIBRARY_DEPENDENCIES} gdi32)
-#  endif ()
-#endif ()
-
-##-----------------------------------------------------------------------------
-##  PLASTIMATCH MODULES
-##-----------------------------------------------------------------------------
+##  Specify which libraries of plastimatch should be built
 if (PLM_CONFIG_LIBRARY_BUILD)
 else ()
   set (PLMLIB_CONFIG_ENABLE_CLI true)
@@ -194,6 +185,39 @@ if (PLMLIB_CONFIG_ENABLE_REGISTER OR PLMLIB_CONFIG_ENABLE_RECONSTRUCT)
   set (PLMLIB_CONFIG_ENABLE_OPENCL true)
 endif ()
 
+## Specify which include directories are needed when
+## compiling code that links to the libraries
+set (PLASTIMATCH_INCLUDE_DIRECTORIES "")
+if (NOT PLM_PACKAGE_LEGACY_CMAKE_CONFIG)
+  list (APPEND PLASTIMATCH_INCLUDE_DIRECTORIES
+    $<INSTALL_INTERFACE:${PLM_INSTALL_INCLUDE_DIR}>
+    )
+  list (APPEND PLASTIMATCH_INCLUDE_DIRECTORIES
+    $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>
+    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/plastimatch/sys>
+    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/plastimatch/base>
+    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/plastimatch/util>
+    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/libs/devillard>
+    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/libs/dlib-18.7>
+    )
+  if (PLMLIB_CONFIG_ENABLE_REGISTER)
+    list (APPEND PLASTIMATCH_INCLUDE_DIRECTORIES
+      $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/plastimatch/register>
+      $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/libs/liblbfgs-1.9/include>
+      )
+  endif ()
+  if (PLMLIB_CONFIG_ENABLE_SEGMENT)
+    list (APPEND PLASTIMATCH_INCLUDE_DIRECTORIES
+      $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/plastimatch/segment>
+      )
+  endif ()
+  if (PLMLIB_CONFIG_ENABLE_DOSE)
+    list (APPEND PLASTIMATCH_INCLUDE_DIRECTORIES
+      $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src/plastimatch/dose>
+      )
+  endif ()
+endif ()
+
 # Core
 add_subdirectory(base)
 add_subdirectory(sys)
@@ -237,10 +261,8 @@ if (PLMLIB_CONFIG_ENABLE_TEST)
   add_subdirectory(test)
 endif ()
 
-##-----------------------------------------------------------------------------
-##  Set up configuration for external projects using plastimatch API
-##-----------------------------------------------------------------------------
-# Add targets to build tree export set
+# Create a list of targets to be exported.  These are used by applications
+# which link to plastimatch libraries.
 set (EXPORT_TARGET_LIST plmsys plmbase devillard nkidecompress plmutil)
 if (PLMLIB_CONFIG_ENABLE_DOSE)
   set (EXPORT_TARGET_LIST ${EXPORT_TARGET_LIST} specfun plmdose)
@@ -281,38 +303,77 @@ endif ()
 #if (PLMLIB_CONFIG_ENABLE_SCRIPT)
 #  set (EXPORT_TARGET_LIST ${EXPORT_TARGET_LIST} lua plmscript)
 #endif ()
-export (TARGETS 
-  ${EXPORT_TARGET_LIST}
-  FILE "${CMAKE_BINARY_DIR}/PlastimatchLibraryDepends.cmake")
-
-# Help cmake find the PlastimatchConfig.cmake in the build directory
-if (NOT ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 2.8)
-  export (PACKAGE Plastimatch)
-endif ()
-
-# Create PlastimatchBuildTreeSettings.cmake for the use from the build tree
-file (RELATIVE_PATH CONF_REL_INCLUDE_DIR "${PLM_INSTALL_CMAKE_DIR}"
-  "${PLM_INSTALL_INCLUDE_DIR}")
-configure_file (
-  "${PROJECT_SOURCE_DIR}/cmake/PlastimatchConfig.cmake.in" 
-  "${CMAKE_BINARY_DIR}/PlastimatchConfig.cmake" 
-  @ONLY)
-configure_file (
-  "${PROJECT_SOURCE_DIR}/cmake/PlastimatchBuildTreeSettings.cmake.in"
-  "${CMAKE_BINARY_DIR}/PlastimatchBuildTreeSettings.cmake" @ONLY)
-
-# Install the PlastimatchConfig.cmake file
-install (FILES 
-  "${CMAKE_BINARY_DIR}/PlastimatchConfig.cmake" 
-  DESTINATION "${PLM_INSTALL_CMAKE_DIR}"
-  # COMPONENT dev
-  )
-
-# Install the export set for use with the install-tree
-install (EXPORT PlastimatchLibraryDepends 
-  DESTINATION "${PLM_INSTALL_CMAKE_DIR}"
-  # COMPONENT dev
-  )
+
+# Create the configuration files used by client applications
+if (PLM_PACKAGE_LEGACY_CMAKE_CONFIG)
+
+  export (TARGETS 
+    ${EXPORT_TARGET_LIST}
+    FILE "${CMAKE_BINARY_DIR}/PlastimatchLibraryDepends.cmake")
+
+  # Help cmake find the PlastimatchConfig.cmake in the build directory
+  if (NOT ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 2.8)
+    export (PACKAGE Plastimatch)
+  endif ()
+
+  # Create PlastimatchBuildTreeSettings.cmake for the use from the build tree
+  file (RELATIVE_PATH CONF_REL_INCLUDE_DIR "${PLM_INSTALL_CMAKE_DIR}"
+    "${PLM_INSTALL_INCLUDE_DIR}")
+  configure_file (
+    "${PROJECT_SOURCE_DIR}/cmake/PlastimatchConfig-Legacy.cmake.in" 
+    "${CMAKE_BINARY_DIR}/PlastimatchConfig.cmake" 
+    @ONLY)
+  configure_file (
+    "${PROJECT_SOURCE_DIR}/cmake/PlastimatchBuildTreeSettings.cmake.in"
+    "${CMAKE_BINARY_DIR}/PlastimatchBuildTreeSettings.cmake" @ONLY)
+
+  # Install the PlastimatchConfig.cmake file
+  install (FILES 
+    "${CMAKE_BINARY_DIR}/PlastimatchConfig.cmake" 
+    DESTINATION "${PLM_INSTALL_CMAKE_DIR}"
+    # COMPONENT dev
+    )
+
+  # Install the export set for use with the install-tree
+  install (EXPORT PlastimatchLibraryDepends 
+    DESTINATION "${PLM_INSTALL_CMAKE_DIR}"
+    # COMPONENT dev
+    )
+
+else (PLM_PACKAGE_LEGACY_CMAKE_CONFIG)
+
+  include (CMakePackageConfigHelpers)
+
+  # Make the version file
+  write_basic_package_version_file (
+    "${CMAKE_CURRENT_BINARY_DIR}/PlastimatchConfigVersion.cmake"
+    VERSION ${PLM_DEFAULT_VERSION_STRING}
+    COMPATIBILITY AnyNewerVersion
+    )
+
+  # Make the targets file
+  export (EXPORT PlastimatchLibraryDepends
+    FILE "${CMAKE_BINARY_DIR}/PlastimatchLibraryDepends.cmake"
+    )
+  
+  # Make the config file
+  configure_file (
+    "${PROJECT_SOURCE_DIR}/cmake/PlastimatchConfig.cmake.in" 
+    "${CMAKE_BINARY_DIR}/PlastimatchConfig.cmake" 
+    @ONLY)
+  
+  # Install the files
+  install (EXPORT PlastimatchLibraryDepends
+    DESTINATION "${PLM_INSTALL_CMAKE_DIR}"
+    )
+  install (FILES 
+    "${CMAKE_BINARY_DIR}/PlastimatchConfig.cmake"
+    "${CMAKE_CURRENT_BINARY_DIR}/PlastimatchConfigVersion.cmake"
+    DESTINATION "${PLM_INSTALL_CMAKE_DIR}"
+    COMPONENT Devel
+    )
+
+endif (PLM_PACKAGE_LEGACY_CMAKE_CONFIG)
 
 ##-----------------------------------------------------------------------------
 ##  DOXYGEN
diff --git a/src/plastimatch/base/CMakeLists.txt b/src/plastimatch/base/CMakeLists.txt
index 89d3d76..c2e81e2 100644
--- a/src/plastimatch/base/CMakeLists.txt
+++ b/src/plastimatch/base/CMakeLists.txt
@@ -16,32 +16,37 @@ include_directories (AFTER ${NKIDECOMPRESS_INCLUDE_DIR})
 ##-----------------------------------------------------------------------------
 set (PLMBASE_LIBRARY_SRC
   aperture.cxx aperture.h 
-  astroid_dose.cxx
-  bspline_interpolate.cxx
-  bspline_warp.cxx
-  bspline_xform.cxx
-  cxt_extract.cxx
-  cxt_io.cxx
-  dicom_probe.cxx
-  dicom_util.cxx
-  dicom.dic
-  direction_cosines.cxx 
-  direction_matrices.cxx 
+  astroid_dose.cxx astroid_dose.h
+  bspline_interpolate.cxx bspline_interpolate.h
+  bspline_warp.cxx bspline_warp.h
+  bspline_xform.cxx bspline_xform.h
+  clamp.h
+  cxt_extract.cxx cxt_extract.h
+  cxt_io.cxx cxt_io.h
+  dicom_probe.cxx dicom_probe.h
+  dicom_util.cxx dicom_util.h
+  dicom.dic   # How to make sure this gets installed?
+  direction_cosines.cxx direction_cosines.h
+  direction_matrices.cxx direction_matrices.h
   exchkeys.cxx
+  float_pair_list.cxx float_pair_list.h
   gaussian.cxx gaussian.h 
-  hnd_io.cxx
-  interpolate.cxx
-  itkClampCastImageFilter.txx
-  itk_dicom_load.cxx
-  itk_dicom_save.cxx
-  itk_directions.cxx
-  itk_image.cxx
-  itk_image_accumulate.cxx
-  itk_image_cast.cxx
-  itk_image_clone.cxx
+  hnd_io.cxx hnd_io.h
+  hnd_io_p.h
+  interpolate.cxx interpolate.h
+  interpolate_macros.h
+  itkAndConstantToImageFilter.h
+  itkClampCastImageFilter.txx itkClampCastImageFilter.h
+  itk_dicom_load.cxx itk_dicom_load.h
+  itk_dicom_save.cxx itk_dicom_save.h
+  itk_directions.cxx itk_directions.h
+  itk_image.cxx itk_image.h
+  itk_image_accumulate.cxx itk_image_accumulate.h
+  itk_image_cast.cxx itk_image_cast.h
+  itk_image_clone.cxx itk_image_clone.h
 #  itk_image_conv.cxx (broken)
-  itk_image_create.cxx
-  itk_image_load.txx
+  itk_image_create.cxx itk_image_create.h
+  itk_image_load.txx itk_image_load.h
   itk_image_load_char.cxx
   itk_image_load_double.cxx
   itk_image_load_float.cxx
@@ -51,122 +56,138 @@ set (PLMBASE_LIBRARY_SRC
   itk_image_load_uint32.cxx
   itk_image_load_ushort.cxx
   itk_image_load_vec.cxx
-  itk_image_save.cxx
+  itk_image_origin.cxx itk_image_origin.h 
+  itk_image_region.cxx itk_image_region.h 
+  itk_image_save.cxx itk_image_save.h
   itk_image_scale.cxx itk_image_scale.h 
-  itk_image_stats.cxx
+  itk_image_stats.cxx itk_image_stats.h
   itk_image_type.h
-  itk_metadata.cxx
+  itk_metadata.cxx itk_metadata.h
   itk_point.h
-  itk_pointset.cxx
-  itk_resample.cxx
-  itk_volume_header.cxx
-  itk_warp.cxx
-  mc_dose.cxx
-  metadata.cxx
-  mha_io.cxx
-  nki_io.cxx
-  parameter_parser.cxx
-  plm_file_format.cxx
-  plm_image.cxx
+  itk_pointset.cxx itk_pointset.h
+  itk_resample.cxx itk_resample.h
+  itk_volume_header.cxx itk_volume_header.h
+  itk_warp.cxx itk_warp.h
+  make_string.h
+  mc_dose.cxx mc_dose.h
+  metadata.cxx metadata.h
+  mha_io.cxx mha_io.h
+  nki_io.cxx nki_io.h
+  parameter_parser.cxx parameter_parser.h
+  plm_file_format.cxx plm_file_format.h
+  plm_image.cxx plm_image.h 
+  plm_image_p.h
   plm_image_convert.cxx
-  plm_image_header.cxx
-  plm_image_set.cxx
-  plm_image_type.cxx
-  plm_warp.cxx
-  pointset.cxx
-  pointset_warp.cxx
-  proj_image.cxx
-  proj_image_dir.cxx
-  proj_matrix.cxx
+  plm_image_header.cxx plm_image_header.h
+  plm_image_set.cxx plm_image_set.h
+  plm_image_type.cxx plm_image_type.h
+  plm_itk.h
+  plm_uid_prefix.h
+  plm_warp.cxx plm_warp.h
+  pointset.cxx pointset.h
+  pointset_warp.cxx pointset_warp.h
+  proj_image.cxx proj_image.h
+  proj_image_dir.cxx proj_image_dir.h
+  proj_matrix.cxx proj_matrix.h
   proj_volume.cxx proj_volume.h
-  rasterize_slice.cxx
-  rasterizer.cxx 
-  raw_pointset.cxx
+  pwlut.cxx pwlut.h 
+  rasterize_slice.cxx rasterize_slice.h
+  rasterizer.cxx rasterizer.h
+  raw_pointset.cxx raw_pointset.h
+  ray_data.h
   ray_trace_exact.cxx
+  ray_trace_callback.h
   ray_trace_probe.cxx
+  ray_trace_probe.h
   ray_trace_uniform.cxx
-  rpl_volume.cxx
+  rpl_volume.cxx rpl_volume.h
   rtds_dcmtk.cxx
   rtds_gdcm.cxx
   rt_study.cxx rt_study.h 
+  rt_study_p.h
   rt_study_metadata.cxx rt_study_metadata.h 
   rtog_io.cxx
   rtss.cxx rtss.h 
   rtss_contour.cxx rtss_contour.h 
   rtss_roi.cxx rtss_roi.h 
   segmentation.cxx segmentation.h 
-  slice_extract.cxx
+  slice_extract.cxx slice_extract.h
   slice_list.cxx slice_list.h
-  ss_img_extract.cxx
-  ss_list_io.cxx
-  thumbnail.cxx
-  vf_convolve.cxx
-  vf_jacobian.cxx
-  vf_stats.cxx
-  vf.cxx
-  volume_conv.cxx
-  volume_gaussian.cxx volume_gaussian.h 
-  volume_grad.cxx volume_grad.h 
-  volume_header.cxx
-  volume_limit.cxx
-  volume_resample.cxx
-  volume_stats.cxx
-  volume.cxx
-  xform.cxx
-  xform_convert.cxx
-  xform_legacy.cxx
-  xform_point.cxx
-  xio_ct.cxx
-  xio_ct_transform.cxx
-  xio_demographic.cxx
-  xio_dir.cxx
-  xio_dose.cxx
-  xio_patient.cxx
-  xio_plan.cxx
-  xio_structures.cxx
-  xio_studyset.cxx
-  xpm.cxx
+  ss_img_extract.cxx ss_img_extract.h
+  ss_list_io.cxx ss_list_io.h
+  threading.h
+  thumbnail.cxx thumbnail.h
+  vf.cxx vf.h
+  vf_convolve.cxx vf_convolve.h
+  vf_jacobian.cxx vf_jacobian.h
+  vf_stats.cxx vf_stats.h
+  volume.cxx volume.h
+  volume_conv.cxx volume_conv.h
+  volume_fill.cxx volume_fill.h
+  volume_gaussian.cxx volume_gaussian.h
+  volume_grad.cxx volume_grad.h
+  volume_header.cxx volume_header.h
+  volume_limit.cxx volume_limit.h
+  volume_macros.h
+  volume_resample.cxx volume_resample.h
+  volume_stats.cxx volume_stats.h
+  xform.cxx xform.h
+  xform_convert.cxx xform_convert.h
+  xform_legacy.cxx xform_legacy.h
+  xform_point.cxx xform_point.h
+  xio_ct.cxx xio_ct.h
+  xio_ct_transform.cxx xio_ct_transform.h
+  xio_demographic.cxx xio_demographic.h
+  xio_dir.cxx xio_dir.h
+  xio_dose.cxx xio_dose.h
+  xio_patient.cxx xio_patient.h
+  xio_plan.cxx xio_plan.h
+  xio_structures.cxx xio_structures.h
+  xio_studyset.cxx xio_studyset.h
+  xpm.cxx xpm.h xpm_p.h
   )
 
 if (PLM_DCM_USE_GDCM1)
   set (PLMBASE_LIBRARY_SRC
     ${PLMBASE_LIBRARY_SRC}
-    gdcm1_dose.cxx
-    gdcm1_file.cxx
-    gdcm1_rdd.cxx
-    gdcm1_rtss.cxx
-    gdcm1_series.cxx
-    gdcm1_series_helper_2.cxx
-    gdcm1_util.cxx
+    gdcm1_dose.cxx gdcm1_dose.h
+    gdcm1_file.cxx gdcm1_file.h
+    gdcm1_rdd.cxx gdcm1_rdd.h
+    gdcm1_rtss.cxx gdcm1_rtss.h
+    gdcm1_series.cxx gdcm1_series.h
+    gdcm1_series_helper_2.cxx gdcm1_series_helper_2.h
+    gdcm1_util.cxx gdcm1_util.h
     )
 endif ()
 
 if (PLM_DCM_USE_GDCM2)
   set (PLMBASE_LIBRARY_SRC
     ${PLMBASE_LIBRARY_SRC}
-    gdcm2_util.cxx
+    gdcm2_util.cxx gdcm2_util.h
     )
 endif ()
 
 if (PLM_DCM_USE_DCMTK)
   set (PLMBASE_LIBRARY_SRC
     ${PLMBASE_LIBRARY_SRC}
-    dcmtk_file.cxx
-    dcmtk_image.cxx
-    dcmtk_loader.cxx
-    dcmtk_metadata.cxx
-    dcmtk_module_general_series.cxx
-    dcmtk_module_general_study.cxx
-    dcmtk_module_patient.cxx
-    dcmtk_rdd.cxx
-    dcmtk_rtdose.cxx 
-    dcmtk_rtss.cxx
-    dcmtk_series.cxx
-    dcmtk_sro.cxx
+    dcmtk_config.h
+    dcmtk_file.cxx dcmtk_file.h
+    dcmtk_image.cxx 
+    dcmtk_loader.cxx dcmtk_loader.h
+    dcmtk_loader_p.h
+    dcmtk_metadata.cxx dcmtk_metadata.h
+    dcmtk_module_general_series.cxx dcmtk_module_general_series.h
+    dcmtk_module_general_study.cxx dcmtk_module_general_study.h
+    dcmtk_module_patient.cxx dcmtk_module_patient.h
+    dcmtk_rdd.cxx dcmtk_rdd.h
+    dcmtk_rtdose.cxx dcmtk_rtdose.h
+    dcmtk_rtss.cxx dcmtk_rtss.h
     dcmtk_rt_study.cxx dcmtk_rt_study.h
     dcmtk_rt_study_p.cxx dcmtk_rt_study_p.h
-    dcmtk_uid.cxx
-    dcmtk_util.cxx
+    dcmtk_series.cxx dcmtk_series.h
+    dcmtk_sro.cxx dcmtk_sro.h
+    dcmtk_uid.cxx dcmtk_uid.h
+    dcmtk_util.cxx dcmtk_util.h
     )
 endif ()
 
@@ -217,4 +238,6 @@ plm_add_library (
   "${PLMBASE_LIBRARY_SRC}" 
   "${PLMBASE_LIBRARY_DEPENDENCIES}"
   "${PLMBASE_LIBRARY_LDFLAGS}"
-  "${PLMBASE_LIBRARY_HEADERS}")
+  "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
+  "${PLMBASE_LIBRARY_HEADERS}"
+  )
diff --git a/src/plastimatch/base/aperture.cxx b/src/plastimatch/base/aperture.cxx
index 05cc3ce..5b08f6e 100644
--- a/src/plastimatch/base/aperture.cxx
+++ b/src/plastimatch/base/aperture.cxx
@@ -30,10 +30,6 @@ public:
         center[1] = app->center[1];
         spacing[0] = app->spacing[0];
         spacing[1] = app->spacing[1];
-
-        /* Is this a good idea?? */
-        aperture_image = app->aperture_image;
-        range_compensator_image = app->range_compensator_image;
     }
 public:
     Plm_image::Pointer aperture_image;
@@ -49,6 +45,9 @@ Aperture::Aperture ()
 {
     this->d_ptr = new Aperture_private;
 
+	/* The aperture and the range compensator are inizialized */
+	this->allocate_aperture_images();
+
     this->vup[0] = 0.0;
     this->vup[1] = 0.0;
     this->vup[2] = 1.0;
@@ -72,6 +71,9 @@ Aperture::Aperture (const Aperture::Pointer& ap)
 {
     this->d_ptr = new Aperture_private (ap->d_ptr);
 
+	/* The aperture and the range compensator are initialized */
+	this->allocate_aperture_images();
+
     vec3_copy (this->vup, ap->vup);
     vec3_copy (this->pdn, ap->pdn);
     vec3_copy (this->prt, ap->prt);
@@ -80,6 +82,20 @@ Aperture::Aperture (const Aperture::Pointer& ap)
     vec3_copy (this->ul_room, ap->ul_room);
     vec3_copy (this->incr_r, ap->incr_r);
     vec3_copy (this->incr_c, ap->incr_c);
+
+	Volume::Pointer ap_tmp = ap->get_aperture_volume();
+	unsigned char* ap_tmp_img = (unsigned char*) ap_tmp->img;
+	Volume::Pointer rc_tmp = ap->get_range_compensator_volume();
+	float* rc_tmp_img = (float*) rc_tmp->img;
+
+	unsigned char* ap_img = (unsigned char*) this->get_aperture_volume()->img;
+	float* rc_img = (float*) this->get_range_compensator_volume()->img;
+
+	for (int i = 0; i < d_ptr->dim[0] * d_ptr->dim[1]; i++)
+	{
+		ap_img[i] = ap_tmp_img[i];
+		rc_img[i] = rc_tmp_img[i];
+	}
 }
 
 Aperture::~Aperture ()
@@ -118,6 +134,8 @@ Aperture::set_dim (const int* dim)
     d_ptr->dim[1] = dim[1];
     d_ptr->center[0] = (dim[0]-1) / 2;
     d_ptr->center[1] = (dim[1]-1) / 2;
+
+	this->allocate_aperture_images();
 }
 
 const double*
@@ -221,6 +239,16 @@ Aperture::allocate_aperture_images ()
     Volume *ap_vol = new Volume (dim, origin, spacing, NULL, PT_UCHAR, 1);
     Volume *rc_vol = new Volume (dim, origin, spacing, NULL, PT_FLOAT, 1);
 
+	/* Set the volume to values = 0 for range compensator and 1 for aperture */
+	unsigned char* ap_img = (unsigned char*) ap_vol->img;
+	float* rc_img = (float*) rc_vol->img;
+
+	for (int i = 0; i < d_ptr->dim[0] * d_ptr->dim[1]; i++)
+	{
+		ap_img[i] = 1;
+		rc_img[i] = 0;
+	}
+
     d_ptr->aperture_image = Plm_image::New (ap_vol);
     d_ptr->range_compensator_image = Plm_image::New (rc_vol);
 }
@@ -286,13 +314,15 @@ Aperture::set_range_compensator_volume (Volume::Pointer rc)
 }
 
 void 
-Aperture::apply_smearing (float smearing)
+Aperture::apply_smearing_to_aperture (float smearing, float reference_depth)
 {
     /* Create a structured element of the right size */
     int strel_half_size[2];
     int strel_size[2];
-    strel_half_size[0] = ROUND_INT(smearing / d_ptr->spacing[0]);
-    strel_half_size[1] = ROUND_INT(smearing / d_ptr->spacing[1]);
+
+	strel_half_size[0] = ROUND_INT(smearing * d_ptr->distance / (reference_depth * d_ptr->spacing[0]));
+	strel_half_size[1] = ROUND_INT(smearing * d_ptr->distance / (reference_depth * d_ptr->spacing[1]));
+
     strel_size[0] = 1 + 2 * strel_half_size[0];
     strel_size[1] = 1 + 2 * strel_half_size[1];
     unsigned char *strel = new unsigned char[strel_size[0]*strel_size[1]];
@@ -320,18 +350,14 @@ Aperture::apply_smearing (float smearing)
 
     /* Apply smear */
     Volume::Pointer& ap_vol = this->get_aperture_volume ();
-    Volume::Pointer& rc_vol = this->get_range_compensator_volume ();
     unsigned char* ap_img = (unsigned char*) ap_vol->img;
-    float* rc_img = (float*) rc_vol->img;
     Volume::Pointer ap_vol_new = ap_vol->clone ();
-    Volume::Pointer rc_vol_new = rc_vol->clone ();
     unsigned char* ap_img_new = (unsigned char*) ap_vol_new->img;
-    float* rc_img_new = (float*) rc_vol_new->img;
+
     for (int ar = 0; ar < d_ptr->dim[1]; ar++) {
         for (int ac = 0; ac < d_ptr->dim[0]; ac++) {
             int aidx = ar * d_ptr->dim[0] + ac;
             unsigned char ap_acc = 0;
-            float rc_acc = FLT_MAX;
             for (int sr = 0; sr < strel_size[1]; sr++) {
                 int pr = ar + sr - strel_half_size[1];
                 if (pr < 0 || pr >= d_ptr->dim[1]) {
@@ -352,18 +378,91 @@ Aperture::apply_smearing (float smearing)
                     if (ap_img[pidx]) {
                         ap_acc = 1;
                     }
+                }
+            }
+            ap_img_new[aidx] = ap_acc;
+        }
+    }
+
+    /* Fixate updated aperture and rc into this object */
+    d_ptr->aperture_image->set_volume (ap_vol_new);
+
+    /* Clean up */
+    delete[] strel;
+}
+
+void 
+Aperture::apply_smearing_to_range_compensator (float smearing, float reference_depth)
+{
+    /* Create a structured element of the right size */
+    int strel_half_size[2];
+    int strel_size[2];
+
+	strel_half_size[0] = ROUND_INT(smearing * d_ptr->distance / (reference_depth * d_ptr->spacing[0]));
+	strel_half_size[1] = ROUND_INT(smearing * d_ptr->distance / (reference_depth * d_ptr->spacing[1]));
+
+    strel_size[0] = 1 + 2 * strel_half_size[0];
+    strel_size[1] = 1 + 2 * strel_half_size[1];
+    unsigned char *strel = new unsigned char[strel_size[0]*strel_size[1]];
+    for (int r = 0; r < strel_size[1]; r++) {
+        float rf = (float) (r - strel_half_size[1]) * d_ptr->spacing[1];
+        for (int c = 0; c < strel_size[0]; c++) {
+            float cf = (float) (c - strel_half_size[0]) * d_ptr->spacing[0];
+            int idx = r*strel_size[0] + c;
+
+            strel[idx] = 0;
+            if ((rf*rf + cf*cf) <= smearing*smearing) {
+                strel[idx] = 1;
+            }
+        }
+    }
+
+    /* Debugging information */
+    for (int r = 0; r < strel_size[1]; r++) {
+        for (int c = 0; c < strel_size[0]; c++) {
+            int idx = r*strel_size[0] + c;
+            printf ("%d ", strel[idx]);
+        }
+        printf ("\n");
+    }
+
+    /* Apply smear */
+    Volume::Pointer& rc_vol = this->get_range_compensator_volume ();
+    float* rc_img = (float*) rc_vol->img;
+    Volume::Pointer rc_vol_new = rc_vol->clone ();
+    float* rc_img_new = (float*) rc_vol_new->img;
+
+    for (int ar = 0; ar < d_ptr->dim[1]; ar++) {
+        for (int ac = 0; ac < d_ptr->dim[0]; ac++) {
+            int aidx = ar * d_ptr->dim[0] + ac;
+            float rc_acc = FLT_MAX;
+            for (int sr = 0; sr < strel_size[1]; sr++) {
+                int pr = ar + sr - strel_half_size[1];
+                if (pr < 0 || pr >= d_ptr->dim[1]) {
+                    continue;
+                }
+                for (int sc = 0; sc < strel_size[0]; sc++) {
+                    int pc = ac + sc - strel_half_size[0];
+                    if (pc < 0 || pc >= d_ptr->dim[0]) {
+                        continue;
+                    }
+
+                    int sidx = sr * strel_size[0] + sc;
+                    if (strel[sidx] == 0) {
+                        continue;
+                    }
+
+                    int pidx = pr * d_ptr->dim[0] + pc;
                     if (rc_img[pidx] < rc_acc) {
                         rc_acc = rc_img[pidx];
                     }
                 }
             }
-            ap_img_new[aidx] = ap_acc;
             rc_img_new[aidx] = rc_acc;
         }
     }
 
     /* Fixate updated aperture and rc into this object */
-    d_ptr->aperture_image->set_volume (ap_vol_new);
     d_ptr->range_compensator_image->set_volume (rc_vol_new);
 
     /* Clean up */
diff --git a/src/plastimatch/base/aperture.h b/src/plastimatch/base/aperture.h
index fd50003..acab887 100644
--- a/src/plastimatch/base/aperture.h
+++ b/src/plastimatch/base/aperture.h
@@ -95,8 +95,10 @@ public:
     void set_range_compensator_volume (Volume::Pointer ap);
 
     /*! \brief Expand aperture and smear compensator.  The smearing 
-      parameters is defined as mm in aperture plane. */
-    void apply_smearing (float smearing);
+      parameters is defined as mm around the target in the beam eye view frame
+	  at the target minimal depth. */
+    void apply_smearing_to_aperture (float smearing, float target_depth);
+	void apply_smearing_to_range_compensator (float smearing, float target_depth);
     ///@}
 
 public:
diff --git a/src/plastimatch/base/bspline_macros.h b/src/plastimatch/base/bspline_macros.h
index 84d0e0f..05b7fa0 100644
--- a/src/plastimatch/base/bspline_macros.h
+++ b/src/plastimatch/base/bspline_macros.h
@@ -172,7 +172,7 @@ get_region_offset (plm_long i, plm_long j, plm_long k, const Bspline_xform *bxf)
     } while (0);
 
 
-/* Get real-space coordinates from a set of volume coordinates */
+/* Get real-space coordinates from a set of volume indices */
 #define GET_REAL_SPACE_COORDS(xyz_vol, ijk_vol, bxf)			\
     do {								\
 	xyz_vol[0] = bxf->img_origin[0] + bxf->img_spacing[0] * ijk_vol[0]; \
@@ -180,22 +180,10 @@ get_region_offset (plm_long i, plm_long j, plm_long k, const Bspline_xform *bxf)
 	xyz_vol[2] = bxf->img_origin[2] + bxf->img_spacing[2] * ijk_vol[2]; \
     } while (0);
 
-
-/* Direction cosines - IJK to XYZ */
+/* Get real-space coordinates from a set of volume indices */
+#if defined (commentout)
 #define GET_WORLD_COORDS(xyz_vol, ijk_vol, vol, bxf)                    \
-    do {                                                                \
-        xyz_vol[0] = bxf->img_origin[0]                                 \
-            + ijk_vol[0]*vol->step[3*0+0]                               \
-            + ijk_vol[1]*vol->step[3*0+1]                               \
-            + ijk_vol[2]*vol->step[3*0+2];                              \
-        xyz_vol[1] = bxf->img_origin[1]                                 \
-            + ijk_vol[0]*vol->step[3*1+0]                               \
-            + ijk_vol[1]*vol->step[3*1+1]                               \
-            + ijk_vol[2]*vol->step[3*1+2];                              \
-        xyz_vol[2] = bxf->img_origin[2]                                 \
-            + ijk_vol[0]*vol->step[3*2+0]                               \
-            + ijk_vol[1]*vol->step[3*2+1]                               \
-            + ijk_vol[2]*vol->step[3*2+2];                              \
-    } while (0);
+    VOXEL_COORDS (xyz_vol, ijk_vol, bxf->img_origin, vol->step)
+#endif
 
 #endif /* _bspline_macros_h_ */
diff --git a/src/plastimatch/base/bspline_xform.cxx b/src/plastimatch/base/bspline_xform.cxx
index 7a84e7f..2d0207b 100644
--- a/src/plastimatch/base/bspline_xform.cxx
+++ b/src/plastimatch/base/bspline_xform.cxx
@@ -2,6 +2,7 @@
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
 #include "plmbase_config.h"
+#include <fstream>
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
@@ -19,6 +20,7 @@
 #include "logfile.h"
 #include "plm_math.h"
 #include "print_and_exit.h"
+#include "string_util.h"
 #include "volume_header.h"
 #include "volume_macros.h"
 
@@ -40,6 +42,8 @@ Bspline_xform::Bspline_xform ()
     this->num_coeff = 0;
     this->coeff = 0;
 
+    this->lut_type = LUT_ALIGNED;
+
     this->cidx_lut = 0;
     this->c_lut = 0;
     this->qidx_lut = 0;
@@ -48,6 +52,10 @@ Bspline_xform::Bspline_xform ()
     this->bx_lut = 0;
     this->by_lut = 0;
     this->bz_lut = 0;
+
+    this->ux_lut = 0;
+    this->uy_lut = 0;
+    this->uz_lut = 0;
 }
 
 Bspline_xform::~Bspline_xform ()
@@ -70,6 +78,9 @@ Bspline_xform::~Bspline_xform ()
     if (this->bz_lut) {
         free (this->bz_lut);
     }
+    delete[] ux_lut;
+    delete[] uy_lut;
+    delete[] uz_lut;
 }
 
 static float
@@ -101,7 +112,7 @@ bspline_basis_eval (
 }
 
 void
-bspline_xform_save (Bspline_xform* bxf, const char* filename)
+Bspline_xform::save (const char* filename)
 {
     FILE* fp;
 
@@ -111,23 +122,23 @@ bspline_xform_save (Bspline_xform* bxf, const char* filename)
 
     fprintf (fp, "MGH_GPUIT_BSP <experimental>\n");
     fprintf (fp, "img_origin = %f %f %f\n", 
-        bxf->img_origin[0], bxf->img_origin[1], bxf->img_origin[2]);
+        this->img_origin[0], this->img_origin[1], this->img_origin[2]);
     fprintf (fp, "img_spacing = %f %f %f\n", 
-        bxf->img_spacing[0], bxf->img_spacing[1], bxf->img_spacing[2]);
+        this->img_spacing[0], this->img_spacing[1], this->img_spacing[2]);
     fprintf (fp, "img_dim = %u %u %u\n", 
-        (unsigned int) bxf->img_dim[0], (unsigned int) bxf->img_dim[1], 
-        (unsigned int) bxf->img_dim[2]);
+        (unsigned int) this->img_dim[0], (unsigned int) this->img_dim[1], 
+        (unsigned int) this->img_dim[2]);
     fprintf (fp, "roi_offset = %d %d %d\n", 
-        (unsigned int) bxf->roi_offset[0], (unsigned int) bxf->roi_offset[1], 
-        (unsigned int) bxf->roi_offset[2]);
+        (unsigned int) this->roi_offset[0], (unsigned int) this->roi_offset[1], 
+        (unsigned int) this->roi_offset[2]);
     fprintf (fp, "roi_dim = %d %d %d\n", 
-        (unsigned int) bxf->roi_dim[0], (unsigned int) bxf->roi_dim[1], 
-        (unsigned int) bxf->roi_dim[2]);
+        (unsigned int) this->roi_dim[0], (unsigned int) this->roi_dim[1], 
+        (unsigned int) this->roi_dim[2]);
     fprintf (fp, "vox_per_rgn = %d %d %d\n", 
-        (unsigned int) bxf->vox_per_rgn[0], 
-        (unsigned int) bxf->vox_per_rgn[1], 
-        (unsigned int) bxf->vox_per_rgn[2]);
-    float *direction_cosines = bxf->dc.get_matrix ();
+        (unsigned int) this->vox_per_rgn[0], 
+        (unsigned int) this->vox_per_rgn[1], 
+        (unsigned int) this->vox_per_rgn[2]);
+    float *direction_cosines = this->dc.get_matrix ();
     fprintf (fp, "direction_cosines = %f %f %f %f %f %f %f %f %f\n", 
         direction_cosines[0], 
         direction_cosines[1], 
@@ -142,15 +153,16 @@ bspline_xform_save (Bspline_xform* bxf, const char* filename)
 
     /* This dumps in itk-like planar format */
     for (int i = 0; i < 3; i++) {
-        for (int j = 0; j < bxf->num_coeff / 3; j++) {
-            //fprintf (fp, "%6.3f\n", bxf->coeff[j*3 + i]);
-            fprintf (fp, "%.20f\n", bxf->coeff[j*3 + i]);
+        for (int j = 0; j < this->num_coeff / 3; j++) {
+            fprintf (fp, "%.20f\n", this->coeff[j*3 + i]);
         }
     }           
 
     fclose (fp);
 }
 
+#if PLM_CONFIG_LEGACY_BSPLINE_XFORM_IO
+
 Bspline_xform* 
 bspline_xform_load (const char* filename)
 {
@@ -242,7 +254,7 @@ bspline_xform_load (const char* filename)
     
 
     /* Allocate memory and build LUTs */
-    bspline_xform_initialize (bxf, img_origin, img_spacing, img_dim,
+    bxf->initialize (img_origin, img_spacing, img_dim,
         roi_offset, roi_dim, vox_per_rgn, dc);
 
     /* This loads from itk-like planar format */
@@ -265,6 +277,141 @@ free_exit:
     return 0;
 }
 
+#else /* PLM_CONFIG_LEGACY_BSPLINE_XFORM_IO is off */
+
+Bspline_xform* 
+bspline_xform_load (const char* filename)
+{
+    int rc;
+    float img_origin[3] = {      /* Image origin (in mm) */
+        0., 0., 0. };
+    float img_spacing[3] = {     /* Image spacing (in mm) */
+        1., 1., 1. };
+    unsigned int a, b, c;        /* For fscanf */
+    plm_long img_dim[3] = {      /* Image size (in vox) */
+        0, 0, 0 };
+    plm_long roi_offset[3] = {   /* Position of first vox in ROI (in vox) */
+        0, 0, 0 };
+    plm_long roi_dim[3] = {      /* Dimension of ROI (in vox) */
+        0, 0, 0 };
+    plm_long vox_per_rgn[3] = {  /* Knot spacing (in vox) */
+        0, 0, 0 };
+    float dc[9] = {              /* Direction cosines */
+        1., 0., 0., 0., 1., 0., 0., 0., 1. };
+
+    std::ifstream ifs (filename);
+    if (!ifs.is_open()) {
+        return 0;
+    }
+
+    /* Check magic number */
+    std::string line;
+    getline (ifs, line);
+    if (!string_starts_with (line, "MGH_GPUIT_BSP")) {
+        return 0;
+    }
+
+    /* Parse header */
+    while (true) {
+        getline (ifs, line);
+        if (!ifs.good()) {
+            logfile_printf ("Error parsing bspline xform\n");
+            return 0;
+        }
+
+        std::string tag, val;
+        if (!split_tag_val (line, tag, val)) {
+            /* No "=" found.  Better be the first coefficient. */
+            break;
+        }
+        
+        rc = sscanf (line.c_str(), "img_origin = %f %f %f\n", 
+            &img_origin[0], &img_origin[1], &img_origin[2]);
+        if (rc == 3) continue;
+
+        rc = sscanf (line.c_str(), "img_spacing = %f %f %f\n", 
+            &img_spacing[0], &img_spacing[1], &img_spacing[2]);
+        if (rc == 3) continue;
+
+        rc = sscanf (line.c_str(), "img_dim = %d %d %d\n", &a, &b, &c);
+        if (rc == 3) {
+            img_dim[0] = a;
+            img_dim[1] = b;
+            img_dim[2] = c;
+            continue;
+        }
+
+        rc = sscanf (line.c_str(), "roi_offset = %d %d %d\n", &a, &b, &c);
+        if (rc == 3) {
+            roi_offset[0] = a;
+            roi_offset[1] = b;
+            roi_offset[2] = c;
+            continue;
+        }
+
+        rc = sscanf (line.c_str(), "roi_dim = %d %d %d\n", &a, &b, &c);
+        if (rc == 3) {
+            roi_dim[0] = a;
+            roi_dim[1] = b;
+            roi_dim[2] = c;
+            continue;
+        }
+
+        rc = sscanf (line.c_str(), "vox_per_rgn = %d %d %d\n", &a, &b, &c);
+        if (rc == 3) {
+            vox_per_rgn[0] = a;
+            vox_per_rgn[1] = b;
+            vox_per_rgn[2] = c;
+            continue;
+        }
+
+        rc = sscanf (line.c_str(), "direction_cosines = %f %f %f %f %f %f %f %f %f\n",
+            &dc[0], &dc[1], &dc[2], &dc[3], &dc[4],
+            &dc[5], &dc[6], &dc[7], &dc[8]);
+        if (rc == 9) continue;
+
+        logfile_printf ("Error loading bxf file\n%s\n", line.c_str());
+        return 0;
+    }
+
+    logfile_printf ("1\n");
+
+    /* Allocate memory and build LUTs */
+    Bspline_xform* bxf = new Bspline_xform;
+    bxf->initialize (img_origin, img_spacing, img_dim,
+        roi_offset, roi_dim, vox_per_rgn, dc);
+
+    if (bxf->num_coeff < 1) {
+        logfile_printf ("Error loading bxf file, no coefficients\n");
+        delete bxf;
+        return 0;
+    }
+
+    /* Load from itk-like planar format */
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < bxf->num_coeff / 3; j++) {
+            /* The first line is already loaded from before */
+            if (i != 0 || j != 0) {
+                getline (ifs, line);
+            }
+            if (!ifs.good()) {
+                logfile_printf ("Error parsing bspline xform (idx = %d,%d): %s\n", i, j, filename);
+                delete bxf;
+                return 0;
+            }
+            rc = sscanf (line.c_str(), "%f", &bxf->coeff[j*3 + i]);
+            if (rc != 1) {
+                logfile_printf ("Error parsing bspline xform (idx = %d,%d): %s\n", i, j, filename);
+                delete bxf;
+                return 0;
+            }
+        }
+    }
+
+    ifs.close();
+    return bxf;
+}
+#endif
 
 /* -----------------------------------------------------------------------
    Debugging routines
@@ -347,9 +494,8 @@ bspline_xform_dump_luts (Bspline_xform* bxf)
 }
 
 void
-bspline_xform_initialize 
+Bspline_xform::initialize 
 (
-    Bspline_xform* bxf,           /* Output: bxf is initialized */
     float img_origin[3],          /* Image origin (in mm) */
     float img_spacing[3],         /* Image spacing (in mm) */
     plm_long img_dim[3],          /* Image size (in vox) */
@@ -365,50 +511,51 @@ bspline_xform_initialize
     float *A, *B, *C;
 
     logfile_printf ("bspline_xform_initialize\n");
+
+    this->dc.set (direction_cosines);
     for (d = 0; d < 3; d++) {
         /* copy input parameters over */
-        bxf->img_origin[d] = img_origin[d];
-        bxf->img_spacing[d] = img_spacing[d];
-        bxf->img_dim[d] = img_dim[d];
-        bxf->roi_offset[d] = roi_offset[d];
-        bxf->roi_dim[d] = roi_dim[d];
-        bxf->vox_per_rgn[d] = vox_per_rgn[d];
-        bxf->dc.set (direction_cosines);
+        this->img_origin[d] = img_origin[d];
+        this->img_spacing[d] = img_spacing[d];
+        this->img_dim[d] = img_dim[d];
+        this->roi_offset[d] = roi_offset[d];
+        this->roi_dim[d] = roi_dim[d];
+        this->vox_per_rgn[d] = vox_per_rgn[d];
 
         /* grid spacing is in mm */
-        bxf->grid_spac[d] = bxf->vox_per_rgn[d] * fabs (bxf->img_spacing[d]);
+        this->grid_spac[d] = this->vox_per_rgn[d] * fabs (this->img_spacing[d]);
 
         /* rdims is the number of regions */
-        bxf->rdims[d] = 1 + (bxf->roi_dim[d] - 1) / bxf->vox_per_rgn[d];
+        this->rdims[d] = 1 + (this->roi_dim[d] - 1) / this->vox_per_rgn[d];
 
         /* cdims is the number of control points */
-        bxf->cdims[d] = 3 + bxf->rdims[d];
+        this->cdims[d] = 3 + this->rdims[d];
     }
 
     /* total number of control points & coefficients */
-    bxf->num_knots = bxf->cdims[0] * bxf->cdims[1] * bxf->cdims[2];
-    bxf->num_coeff = bxf->cdims[0] * bxf->cdims[1] * bxf->cdims[2] * 3;
+    this->num_knots = this->cdims[0] * this->cdims[1] * this->cdims[2];
+    this->num_coeff = this->cdims[0] * this->cdims[1] * this->cdims[2] * 3;
 
     /* Allocate coefficients */
-    bxf->coeff = (float*) malloc (sizeof(float) * bxf->num_coeff);
-    memset (bxf->coeff, 0, sizeof(float) * bxf->num_coeff);
+    this->coeff = (float*) malloc (sizeof(float) * this->num_coeff);
+    memset (this->coeff, 0, sizeof(float) * this->num_coeff);
 
     /* Create q_lut */
-    bxf->q_lut = (float*) malloc (sizeof(float) 
-        * bxf->vox_per_rgn[0] 
-        * bxf->vox_per_rgn[1] 
-        * bxf->vox_per_rgn[2] 
+    this->q_lut = (float*) malloc (sizeof(float) 
+        * this->vox_per_rgn[0] 
+        * this->vox_per_rgn[1] 
+        * this->vox_per_rgn[2] 
         * 64);
-    if (!bxf->q_lut) {
+    if (!this->q_lut) {
         print_and_exit ("Error allocating memory for q_lut\n");
     }
 
-    A = (float*) malloc (sizeof(float) * bxf->vox_per_rgn[0] * 4);
-    B = (float*) malloc (sizeof(float) * bxf->vox_per_rgn[1] * 4);
-    C = (float*) malloc (sizeof(float) * bxf->vox_per_rgn[2] * 4);
+    A = (float*) malloc (sizeof(float) * this->vox_per_rgn[0] * 4);
+    B = (float*) malloc (sizeof(float) * this->vox_per_rgn[1] * 4);
+    C = (float*) malloc (sizeof(float) * this->vox_per_rgn[2] * 4);
 
-    for (i = 0; i < bxf->vox_per_rgn[0]; i++) {
-        float ii = ((float) i) / bxf->vox_per_rgn[0];
+    for (i = 0; i < this->vox_per_rgn[0]; i++) {
+        float ii = ((float) i) / this->vox_per_rgn[0];
         float t3 = ii*ii*ii;
         float t2 = ii*ii;
         float t1 = ii;
@@ -418,8 +565,8 @@ bspline_xform_initialize
         A[i*4+3] = (1.0/6.0) * (+ 1.0 * t3);
     }
 
-    for (j = 0; j < bxf->vox_per_rgn[1]; j++) {
-        float jj = ((float) j) / bxf->vox_per_rgn[1];
+    for (j = 0; j < this->vox_per_rgn[1]; j++) {
+        float jj = ((float) j) / this->vox_per_rgn[1];
         float t3 = jj*jj*jj;
         float t2 = jj*jj;
         float t1 = jj;
@@ -429,8 +576,8 @@ bspline_xform_initialize
         B[j*4+3] = (1.0/6.0) * (+ 1.0 * t3);
     }
 
-    for (k = 0; k < bxf->vox_per_rgn[2]; k++) {
-        float kk = ((float) k) / bxf->vox_per_rgn[2];
+    for (k = 0; k < this->vox_per_rgn[2]; k++) {
+        float kk = ((float) k) / this->vox_per_rgn[2];
         float t3 = kk*kk*kk;
         float t2 = kk*kk;
         float t1 = kk;
@@ -441,13 +588,13 @@ bspline_xform_initialize
     }
 
     p = 0;
-    for (k = 0; k < bxf->vox_per_rgn[2]; k++) {
-        for (j = 0; j < bxf->vox_per_rgn[1]; j++) {
-            for (i = 0; i < bxf->vox_per_rgn[0]; i++) {
+    for (k = 0; k < this->vox_per_rgn[2]; k++) {
+        for (j = 0; j < this->vox_per_rgn[1]; j++) {
+            for (i = 0; i < this->vox_per_rgn[0]; i++) {
                 for (tz = 0; tz < 4; tz++) {
                     for (ty = 0; ty < 4; ty++) {
                         for (tx = 0; tx < 4; tx++) {
-                            bxf->q_lut[p++] = A[i*4+tx] * B[j*4+ty] * C[k*4+tz];
+                            this->q_lut[p++] = A[i*4+tx] * B[j*4+ty] * C[k*4+tz];
                         }
                     }
                 }
@@ -459,21 +606,21 @@ bspline_xform_initialize
     free (A);
         
     /* Create c_lut */
-    bxf->c_lut = (plm_long*) malloc (sizeof(plm_long) 
-        * bxf->rdims[0] 
-        * bxf->rdims[1] 
-        * bxf->rdims[2] 
+    this->c_lut = (plm_long*) malloc (sizeof(plm_long) 
+        * this->rdims[0] 
+        * this->rdims[1] 
+        * this->rdims[2] 
         * 64);
     p = 0;
-    for (k = 0; k < bxf->rdims[2]; k++) {
-        for (j = 0; j < bxf->rdims[1]; j++) {
-            for (i = 0; i < bxf->rdims[0]; i++) {
+    for (k = 0; k < this->rdims[2]; k++) {
+        for (j = 0; j < this->rdims[1]; j++) {
+            for (i = 0; i < this->rdims[0]; i++) {
                 for (tz = 0; tz < 4; tz++) {
                     for (ty = 0; ty < 4; ty++) {
                         for (tx = 0; tx < 4; tx++) {
-                            bxf->c_lut[p++] = 
-                                + (k + tz) * bxf->cdims[0] * bxf->cdims[1]
-                                + (j + ty) * bxf->cdims[0] 
+                            this->c_lut[p++] = 
+                                + (k + tz) * this->cdims[0] * this->cdims[1]
+                                + (j + ty) * this->cdims[0] 
                                 + (i + tx);
                         }
                     }
@@ -483,30 +630,33 @@ bspline_xform_initialize
     }
 
     /* Create b_luts */
-    bxf->bx_lut = (float*)malloc(4*bxf->vox_per_rgn[0]*sizeof(float));
-    bxf->by_lut = (float*)malloc(4*bxf->vox_per_rgn[1]*sizeof(float));
-    bxf->bz_lut = (float*)malloc(4*bxf->vox_per_rgn[2]*sizeof(float));
+    this->bx_lut = (float*)malloc(4*this->vox_per_rgn[0]*sizeof(float));
+    this->by_lut = (float*)malloc(4*this->vox_per_rgn[1]*sizeof(float));
+    this->bz_lut = (float*)malloc(4*this->vox_per_rgn[2]*sizeof(float));
 
     for (int j=0; j<4; j++) {
-        for (int i=0; i<bxf->vox_per_rgn[0]; i++) {
-            bxf->bx_lut[i*4+j] = bspline_basis_eval (j, i, bxf->vox_per_rgn[0]);
+        for (int i=0; i<this->vox_per_rgn[0]; i++) {
+            this->bx_lut[i*4+j] = bspline_basis_eval (
+                j, i, this->vox_per_rgn[0]);
         }
-        for (int i=0; i<bxf->vox_per_rgn[1]; i++) {
-            bxf->by_lut[i*4+j] = bspline_basis_eval (j, i, bxf->vox_per_rgn[1]);
+        for (int i=0; i<this->vox_per_rgn[1]; i++) {
+            this->by_lut[i*4+j] = bspline_basis_eval (
+                j, i, this->vox_per_rgn[1]);
         }
-        for (int i=0; i<bxf->vox_per_rgn[2]; i++) {
-            bxf->bz_lut[i*4+j] = bspline_basis_eval (j, i, bxf->vox_per_rgn[2]);
+        for (int i=0; i<this->vox_per_rgn[2]; i++) {
+            this->bz_lut[i*4+j] = bspline_basis_eval (
+                j, i, this->vox_per_rgn[2]);
         }
     }
 
     //dump_luts (bxf);
 
     logfile_printf ("rdims = (%d,%d,%d)\n", 
-        bxf->rdims[0], bxf->rdims[1], bxf->rdims[2]);
+        this->rdims[0], this->rdims[1], this->rdims[2]);
     logfile_printf ("vox_per_rgn = (%d,%d,%d)\n", 
-        bxf->vox_per_rgn[0], bxf->vox_per_rgn[1], bxf->vox_per_rgn[2]);
+        this->vox_per_rgn[0], this->vox_per_rgn[1], this->vox_per_rgn[2]);
     logfile_printf ("cdims = (%d %d %d)\n", 
-        bxf->cdims[0], bxf->cdims[1], bxf->cdims[2]);
+        this->cdims[0], this->cdims[1], this->cdims[2]);
 }
 
 /* -----------------------------------------------------------------------
diff --git a/src/plastimatch/base/bspline_xform.h b/src/plastimatch/base/bspline_xform.h
index 1479feb..44e6b90 100644
--- a/src/plastimatch/base/bspline_xform.h
+++ b/src/plastimatch/base/bspline_xform.h
@@ -22,6 +22,11 @@ public:
     Bspline_xform ();
     ~Bspline_xform ();
 public:
+    enum Lut_type {
+        LUT_ALIGNED,
+        LUT_UNALIGNED
+    };
+public:
     float img_origin[3];         /* Image origin (in mm) */
     float img_spacing[3];        /* Image spacing (in mm) */
     plm_long img_dim[3];         /* Image size (in vox) */
@@ -36,25 +41,26 @@ public:
     int num_coeff;               /* Total number of coefficents (= product(cdims) * 3) */
     float* coeff;                /* Coefficients.  Vector directions interleaved. */
 
+    Lut_type lut_type;
+
     /* Aligned grid (3D) LUTs */
     plm_long* cidx_lut;          /* Lookup volume for region number */
     plm_long* c_lut;             /* Lookup table for control point indices */
     plm_long* qidx_lut;          /* Lookup volume for region offset */
     float* q_lut;                /* Lookup table for influence multipliers */
 
-    /* Non-aligned grid (1D) LUTs */
+    /* Aligned grid (1D) LUTs */
     float *bx_lut;               /* LUT for influence multiplier in x dir */
     float *by_lut;               /* LUT for influence multiplier in y dir */
     float *bz_lut;               /* LUT for influence multiplier in z dir */
 
-public:
-    void fill_coefficients (float val);
-    void get_volume_header (Volume_header *vh);
-};
+    /* Unaligned grid (1D) LUTs */
+    float *ux_lut;               /* LUT for influence multiplier in x dir */
+    float *uy_lut;               /* LUT for influence multiplier in y dir */
+    float *uz_lut;               /* LUT for influence multiplier in z dir */
 
-//PLMBASE_C_API void bspline_xform_set_default (Bspline_xform* bxf);
-PLMBASE_C_API void bspline_xform_initialize (
-        Bspline_xform* bxf,	          /* Output: bxf is initialized */
+public:
+    void initialize (
         float img_origin[3],          /* Image origin (in mm) */
         float img_spacing[3],         /* Image spacing (in mm) */
         plm_long img_dim[3],          /* Image size (in vox) */
@@ -62,11 +68,13 @@ PLMBASE_C_API void bspline_xform_initialize (
         plm_long roi_dim[3],	      /* Dimension of ROI (in vox) */
         plm_long vox_per_rgn[3],      /* Knot spacing (in vox) */
         float direction_cosines[9]    /* Direction cosines */
-);
-//PLMBASE_C_API void bspline_xform_free (Bspline_xform* bxf);
+    );
+    void save (const char* filename);
+    void fill_coefficients (float val);
+    void get_volume_header (Volume_header *vh);
+};
+
 PLMBASE_C_API Bspline_xform* bspline_xform_load (const char* filename);
-PLMBASE_C_API void bspline_xform_save (Bspline_xform* bxf, const char* filename);
-PLMBASE_C_API void bspline_set_coefficients (Bspline_xform* bxf, float val);
 
 /* Debugging routines */
 PLMBASE_C_API void bspline_xform_dump_coeff (Bspline_xform* bxf, const char* fn);
diff --git a/src/plastimatch/base/cxt_extract.cxx b/src/plastimatch/base/cxt_extract.cxx
index cf14ca0..598fff1 100644
--- a/src/plastimatch/base/cxt_extract.cxx
+++ b/src/plastimatch/base/cxt_extract.cxx
@@ -15,6 +15,7 @@
 
 #include "cxt_extract.h"
 #include "itk_image.h"
+#include "itk_image_origin.h"
 #include "itk_image_type.h"
 #include "rtss.h"
 #include "rtss_contour.h"
@@ -224,7 +225,8 @@ cxt_extract (
 	    uchar_slice = and_filter->GetOutput ();
 
 	    run_marching_squares (curr_structure, uchar_slice, slice_no,
-		image->GetOrigin(), image->GetSpacing(), image->GetDirection());
+		itk_image_origin (image), image->GetSpacing(), 
+                image->GetDirection());
 
 	}
 	slice_it.NextSlice();
@@ -321,7 +323,7 @@ cxt_extract (
 		uchar_slice = and_filter->GetOutput ();
 
 		run_marching_squares (curr_structure, uchar_slice, slice_no, 
-                    image->GetOrigin(), image->GetSpacing(), 
+		itk_image_origin (image), image->GetSpacing(), 
                     image->GetDirection());
 	    }
 	}
diff --git a/src/plastimatch/base/dcmtk_file.cxx b/src/plastimatch/base/dcmtk_file.cxx
index 0f9d17f..d769363 100755
--- a/src/plastimatch/base/dcmtk_file.cxx
+++ b/src/plastimatch/base/dcmtk_file.cxx
@@ -111,6 +111,19 @@ Dcmtk_file::get_ds_float (const DcmTagKey& tag_key, float* val) const
 }
 
 bool
+Dcmtk_file::get_uint8_array (const DcmTagKey& tag_key, 
+    const uint8_t** val, unsigned long* count) const
+{
+    const Uint8* foo;
+    OFCondition rc = d_ptr->m_dfile->getDataset()->findAndGetUint8Array (
+	tag_key, foo, count, OFFalse);
+    if (val) {
+        *val = foo;
+    }
+    return rc.good();
+}
+
+bool
 Dcmtk_file::get_int16_array (const DcmTagKey& tag_key, 
     const int16_t** val, unsigned long* count) const
 {
diff --git a/src/plastimatch/base/dcmtk_file.h b/src/plastimatch/base/dcmtk_file.h
index 933995c..bd721fe 100755
--- a/src/plastimatch/base/dcmtk_file.h
+++ b/src/plastimatch/base/dcmtk_file.h
@@ -35,6 +35,8 @@ public:
     bool get_uint16 (const DcmTagKey& tag_key, uint16_t* val) const;
     bool get_float (const DcmTagKey& tag_key, float* val) const;
     bool get_ds_float (const DcmTagKey& tag_key, float* val) const;
+    bool get_uint8_array (const DcmTagKey& tag_key, 
+	const uint8_t** val, unsigned long* count) const;
     bool get_uint16_array (const DcmTagKey& tag_key, 
 	const uint16_t** val, unsigned long* count) const;
     bool get_int16_array (const DcmTagKey& tag_key, 
diff --git a/src/plastimatch/base/dcmtk_image.cxx b/src/plastimatch/base/dcmtk_image.cxx
index ef8c5bf..199ae43 100755
--- a/src/plastimatch/base/dcmtk_image.cxx
+++ b/src/plastimatch/base/dcmtk_image.cxx
@@ -22,7 +22,6 @@
 #include "logfile.h"
 #include "rt_study_metadata.h"
 #include "plm_image.h"
-#include "plm_image_set.h"
 #include "plm_image_header.h"
 #include "plm_math.h"
 #include "plm_uid_prefix.h"
@@ -35,7 +34,6 @@ void
 Dcmtk_loader::image_load ()
 {
     /* Set up outputs */
-    Plm_image_set::Pointer pli_set = Plm_image_set::New();
     Plm_image::Pointer pli = Plm_image::New();
     d_ptr->img = pli;
 
@@ -46,7 +44,7 @@ Dcmtk_loader::image_load ()
     /* Create a container to hold different groups of files */
     std::list<Dcmtk_file_list> group_list;
 
-    /* Insert files into groups according to direction cosines */
+    /* Arrange files into groups according to direction cosines */
     {
         //printf ("----------\n");
         Dcmtk_file_list::const_iterator it;
@@ -110,13 +108,9 @@ Dcmtk_loader::image_load ()
     if (flist.size() < 2) {
         return;
     }
-    
+
     /* Get first slice */
-    std::list<Dcmtk_file::Pointer>::const_iterator it = flist.begin();
-    const Dcmtk_file* df = (*it).get();
-    float z_init, z_prev, z_diff, z_last;
-    int slice_no = 0;
-    float best_chunk_z_start = z_init = z_prev = df->get_z_position ();
+    const Dcmtk_file* df = (*flist.begin()).get();
     
     /* Store UIDs */
     if (d_ptr->m_drs) {
@@ -143,7 +137,158 @@ Dcmtk_loader::image_load ()
         dcmtk_copy_into_metadata (image_metadata, df, DCM_Modality);
     }
 
-    /* Get next slice */
+    /* Divine image type */
+    uint16_t samp_per_pix, bits_alloc, bits_stored, high_bit, pixel_rep;
+    const char* phot_interp;
+    bool rc = df->get_uint16 (DCM_SamplesPerPixel, &samp_per_pix);
+    if (!rc) {
+        return;
+    }
+    phot_interp = df->get_cstr (DCM_PhotometricInterpretation);
+    if (!phot_interp) {
+        return;
+    }
+    rc = df->get_uint16 (DCM_BitsAllocated, &bits_alloc);
+    if (!rc) {
+        return;
+    }
+    rc = df->get_uint16 (DCM_BitsStored, &bits_stored);
+    if (!rc) {
+        return;
+    }
+    rc = df->get_uint16 (DCM_HighBit, &high_bit);
+    if (!rc) {
+        return;
+    }
+    rc = df->get_uint16 (DCM_PixelRepresentation, &pixel_rep);
+    if (!rc) {
+        return;
+    }
+
+    float rescale_slope, rescale_intercept;
+    rc = df->get_ds_float (DCM_RescaleIntercept, &rescale_intercept);
+    if (!rc) {
+        rescale_intercept = 0;
+    }
+    rc = df->get_ds_float (DCM_RescaleSlope, &rescale_slope);
+    if (!rc) {
+        rescale_slope = 1;
+    }
+
+    lprintf ("Samp_per_pix: %d\n", (int) samp_per_pix);
+    lprintf ("Phot_interp: %s\n", phot_interp);
+    lprintf ("Bits_alloc: %d\n", (int) bits_alloc);
+    lprintf ("Bits_stored: %d\n", (int) bits_stored);
+    lprintf ("High_bit: %d\n", (int) high_bit);
+    lprintf ("Pixel_rep: %d\n", (int) pixel_rep);
+    lprintf ("S/I = %f/%f\n", rescale_slope, rescale_intercept);
+
+    /* Some kinds of images we don't know how to deal with.  
+       Don't load these. */
+    if (samp_per_pix != 1) {
+        lprintf ("Sorry, couldn't load image: samp_per_pix\n");
+        return;
+    }
+    if (strcmp (phot_interp, "MONOCHROME2")) {
+        lprintf ("Sorry, couldn't load image: phot_interp\n");
+        return;
+    }
+    if (bits_alloc != 16 && bits_alloc != 8) {
+        lprintf ("Sorry, couldn't load image: bits_alloc\n");
+        return;
+    }
+    if (bits_stored != high_bit + 1) {
+        lprintf ("Sorry, couldn't load image: bits_stored/high_bit\n");
+        return;
+    }
+    if (pixel_rep != 0 && pixel_rep != 1) {
+        lprintf ("Sorry, couldn't load image: pixel_rep\n");
+        return;
+    }
+
+    /* If PLM_CONFIG_VOL_LIST is enabled, the image will be loaded 
+       into a PLM_IMG_TYPE_GPUIT_LIST */
+#if (PLM_CONFIG_VOL_LIST)
+
+    /* Loop through groups */
+    std::list<Dcmtk_file_list>::iterator grit;
+    for (grit = group_list.begin(); grit != group_list.end(); ++grit) {
+
+        //Dcmtk_file_list& dfl = *grit;
+
+        /* Get first slice of group */
+        Dcmtk_file_list::iterator it = grit->begin();
+        const Dcmtk_file* df = it->get();
+
+        /* Get next slice in group */
+        float z_init, z_prev, z_diff, z_last;
+        int slice_no = 0;
+        float best_chunk_z_start = z_init = z_prev = df->get_z_position ();
+
+        ++it; ++slice_no;
+        df = (*it).get();
+        z_diff = df->get_z_position() - z_prev;
+        z_last = z_prev = df->get_z_position();
+
+        /* We want to find the number and spacing for each chunk 
+           within the group.  These are used to set the dim and 
+           spacing of the volume. */
+        int this_chunk_start = 0, best_chunk_start = 0;
+        float this_chunk_diff = z_diff, best_chunk_diff = z_diff;
+        int this_chunk_len = 2, best_chunk_len = 2;
+
+        /* Loop through remaining slices */
+        while (++it != flist.end())
+        {
+            ++slice_no;
+            printf ("Slice no: %d\n", slice_no);
+            df = (*it).get();
+            z_diff = df->get_z_position() - z_prev;
+            z_last = z_prev = df->get_z_position();
+
+            if (fabs (this_chunk_diff - z_diff) > 0.11) {
+                /* Start a new chunk if difference in thickness is 
+                   more than 0.1 millimeter */
+                this_chunk_start = slice_no - 1;
+                this_chunk_len = 2;
+                this_chunk_diff = z_diff;
+            } else {
+                /* Same thickness, increase size of this chunk */
+                this_chunk_diff = ((this_chunk_len * this_chunk_diff) + z_diff)
+                    / (this_chunk_len + 1);
+                this_chunk_len++;
+
+                /* Check if this chunk is now the best chunk */
+                if (this_chunk_len > best_chunk_len) {
+                    best_chunk_start = this_chunk_start;
+                    best_chunk_len = this_chunk_len;
+                    best_chunk_diff = this_chunk_diff;
+                    best_chunk_z_start = z_prev 
+                        - (best_chunk_len-1) * best_chunk_diff;
+                }
+            }
+        }
+
+#if defined (commentout)        
+        Dcmtk_file_list& flp = *grit;
+        const Dcmtk_file::Pointer dfp = grit->front();
+        Volume::Pointer vol = Volume::New (
+            const plm_long dim[3], 
+            const float origin[3], 
+            const float spacing[3], 
+            &dfp->get_direction_cosines(),
+            vh,
+            PT_FLOAT, 1);
+#endif
+    }
+
+#else
+    /* Get next slice in first chunk */
+    float z_init, z_prev, z_diff, z_last;
+    int slice_no = 0;
+    float best_chunk_z_start = z_init = z_prev = df->get_z_position ();
+
+    std::list<Dcmtk_file::Pointer>::const_iterator it = flist.begin();
     ++it; ++slice_no;
     df = (*it).get();
     z_diff = df->get_z_position() - z_prev;
@@ -238,89 +383,6 @@ Dcmtk_loader::image_load ()
     lprintf ("\n");
 #endif
 
-    /* Divine image type */
-    df = (*flist.begin()).get();
-    uint16_t samp_per_pix, bits_alloc, bits_stored, high_bit, pixel_rep;
-    const char* phot_interp;
-    bool rc = df->get_uint16 (DCM_SamplesPerPixel, &samp_per_pix);
-    if (!rc) {
-	//return pli;
-        return;
-    }
-    phot_interp = df->get_cstr (DCM_PhotometricInterpretation);
-    if (!phot_interp) {
-	//return pli;
-        return;
-    }
-    rc = df->get_uint16 (DCM_BitsAllocated, &bits_alloc);
-    if (!rc) {
-	//return pli;
-        return;
-    }
-    rc = df->get_uint16 (DCM_BitsStored, &bits_stored);
-    if (!rc) {
-	//return pli;
-        return;
-    }
-    rc = df->get_uint16 (DCM_HighBit, &high_bit);
-    if (!rc) {
-	//return pli;
-        return;
-    }
-    rc = df->get_uint16 (DCM_PixelRepresentation, &pixel_rep);
-    if (!rc) {
-	//return pli;
-        return;
-    }
-    lprintf ("Samp_per_pix: %d\n", (int) samp_per_pix);
-    lprintf ("Phot_interp: %s\n", phot_interp);
-    lprintf ("Bits_alloc: %d\n", (int) bits_alloc);
-    lprintf ("Bits_stored: %d\n", (int) bits_stored);
-    lprintf ("High_bit: %d\n", (int) high_bit);
-    lprintf ("Pixel_rep: %d\n", (int) pixel_rep);
-
-    float rescale_slope, rescale_intercept;
-    rc = df->get_ds_float (DCM_RescaleIntercept, &rescale_intercept);
-    if (!rc) {
-        rescale_intercept = 0;
-    }
-    rc = df->get_ds_float (DCM_RescaleSlope, &rescale_slope);
-    if (!rc) {
-        rescale_slope = 1;
-    }
-
-    lprintf ("S/I = %f/%f\n", rescale_slope, rescale_intercept);
-
-    /* Some kinds of images we don't know how to deal with.  
-       Don't load these. */
-    if (samp_per_pix != 1) {
-        lprintf ("Sorry, couldn't load image: samp_per_pix\n");
-	//return pli;
-        return;
-    }
-    if (strcmp (phot_interp, "MONOCHROME2")) {
-        lprintf ("Sorry, couldn't load image: phot_interp\n");
-	//return pli;
-        return;
-    }
-    if (bits_alloc != 16) {
-        lprintf ("Sorry, couldn't load image: bits_alloc\n");
-	//return pli;
-        return;
-    }
-    if (bits_stored != high_bit + 1) {
-        lprintf ("Sorry, couldn't load image: bits_stored/high_bit\n");
-	//return pli;
-        return;
-    }
-    if (pixel_rep != 0 && pixel_rep != 1) {
-        lprintf ("Sorry, couldn't load image: pixel_rep\n");
-	//return pli;
-        return;
-    }
-
-    lprintf ("Image looks ok.  Try to load.\n");
-
     pli->m_type = PLM_IMG_TYPE_GPUIT_FLOAT;
     pli->m_original_type = PLM_IMG_TYPE_GPUIT_FLOAT;
     Volume* vol = new Volume (vh, PT_FLOAT, 1);
@@ -343,9 +405,7 @@ Dcmtk_loader::image_load ()
 	}
 
 	/* Load the slice image data into volume */
-	const uint16_t* pixel_data;
 	df = (*best_slice_it).get();
-	unsigned long length;
 
 #if defined (commentout)
 	lprintf ("Loading slice z=%f at location z=%f\n",
@@ -354,7 +414,15 @@ Dcmtk_loader::image_load ()
 
         /* GCS FIX: This should probably use DicomImage::getOutputData()
            cf. http://support.dcmtk.org/docs/mod_dcmimage.html */
-	rc = df->get_uint16_array (DCM_PixelData, &pixel_data, &length);
+	const uint8_t* pixel_data_8;
+	const uint16_t* pixel_data_16;
+	unsigned long length = 0;
+        rc = 0;
+        if (bits_alloc == 8) {
+            rc = df->get_uint8_array (DCM_PixelData, &pixel_data_8, &length);
+        } else if (bits_alloc == 16) {
+            rc = df->get_uint16_array (DCM_PixelData, &pixel_data_16, &length);
+        }
 	if (!rc) {
 	    print_and_exit ("Oops.  Error reading pixel data.  Punting.\n");
 	}
@@ -364,9 +432,16 @@ Dcmtk_loader::image_load ()
 	}
 
         /* Apply slope and offset */
-        for (plm_long j = 0; j < (plm_long) length; j++) {
-            img[j] = rescale_slope * (int16_t) pixel_data[j] 
-                + rescale_intercept;
+        if (bits_alloc == 8) {
+            for (plm_long j = 0; j < (plm_long) length; j++) {
+                img[j] = rescale_slope * (int8_t) pixel_data_8[j] 
+                    + rescale_intercept;
+            }
+        } else if (bits_alloc == 16) {
+            for (plm_long j = 0; j < (plm_long) length; j++) {
+                img[j] = rescale_slope * (int16_t) pixel_data_16[j] 
+                    + rescale_intercept;
+            }
         }
 	img += length;
 
@@ -375,6 +450,7 @@ Dcmtk_loader::image_load ()
             d_ptr->m_drs->set_slice_uid (i, df->get_cstr (DCM_SOPInstanceUID));
         }
     }
+#endif
     if (d_ptr->m_drs) {
         d_ptr->m_drs->set_slice_list_complete ();
     }
@@ -449,11 +525,9 @@ dcmtk_save_slice (const Rt_study_metadata::Pointer drs, Dcmtk_slice_data *dsd)
     dcmtk_copy_from_metadata (dataset, image_metadata, 
         DCM_PatientPosition, "HFS");
 
-    
-
     dataset->putAndInsertString (DCM_SeriesInstanceUID, 
         drs->get_ct_series_uid());
-    dataset->putAndInsertString (DCM_SeriesNumber, "303");
+    dcmtk_copy_from_metadata (dataset, image_metadata, DCM_SeriesNumber, "1");
     tmp = string_format ("%d", dsd->instance_no);
     dataset->putAndInsertString (DCM_InstanceNumber, tmp.c_str());
     //dataset->putAndInsertString (DCM_InstanceNumber, "0");
@@ -487,8 +561,10 @@ dcmtk_save_slice (const Rt_study_metadata::Pointer drs, Dcmtk_slice_data *dsd)
     //dataset->putAndInsertString (DCM_RescaleSlope, "1");
     dataset->putAndInsertString (DCM_RescaleType, "HU");
 
-    dataset->putAndInsertString (DCM_WindowCenter, "40");
-    dataset->putAndInsertString (DCM_WindowWidth, "400");
+    dcmtk_copy_from_metadata (dataset, image_metadata,
+        DCM_WindowCenter, "40");
+    dcmtk_copy_from_metadata (dataset, image_metadata,
+        DCM_WindowWidth, "400");
 
     /* Convert to 16-bit signed int */
     for (size_t i = 0; i < dsd->slice_size; i++) {
diff --git a/src/plastimatch/base/dcmtk_rt_study.cxx b/src/plastimatch/base/dcmtk_rt_study.cxx
index 62d7bbe..df7ec6b 100755
--- a/src/plastimatch/base/dcmtk_rt_study.cxx
+++ b/src/plastimatch/base/dcmtk_rt_study.cxx
@@ -168,9 +168,12 @@ Dcmtk_rt_study::save (const char *dicom_dir)
 {
     /* GCS FIX: If we're writing an image, we always want new metadata;
        but this should probably be handled by somewhere else in the 
-       code. */
+       code. 
+       GCS FIX MORE: Slicer-RT retains the option to set the study UID.
+       There is, I think (?!), no need to regenerate those at this time.
+    */
     if (d_ptr->img) {
-        d_ptr->rt_study_metadata->generate_new_uids ();
+        d_ptr->rt_study_metadata->generate_new_series_uids ();
     }
     if (d_ptr->img) {
         this->save_image (dicom_dir);
diff --git a/src/plastimatch/base/dcmtk_rtdose.cxx b/src/plastimatch/base/dcmtk_rtdose.cxx
index ac8401b..cba392a 100755
--- a/src/plastimatch/base/dcmtk_rtdose.cxx
+++ b/src/plastimatch/base/dcmtk_rtdose.cxx
@@ -84,6 +84,7 @@ Dcmtk_loader::rtdose_load ()
     float *gfov;    /* gfov = GridFrameOffsetVector */
     plm_long gfov_len;
     const char *gfov_str;
+    OFCondition ofrc;
 
     /* Modality -- better be RTDOSE */
     std::string modality = d_ptr->ds_rtdose->get_modality();
@@ -105,6 +106,25 @@ Dcmtk_loader::rtdose_load ()
 	print_and_exit ("Error parsing RTDOSE ipp.\n");
     }
 
+    /* ImageOrientationPatient */
+    float direction_cosines[9] = {
+        1.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f };
+    val = d_ptr->ds_rtdose->get_cstr (DCM_ImageOrientationPatient);
+    if (val) {
+	int rc = parse_dicom_float6 (direction_cosines, val);
+	if (!rc) {
+	    direction_cosines[6] 
+		= direction_cosines[1]*direction_cosines[5] 
+		- direction_cosines[2]*direction_cosines[4];
+	    direction_cosines[7] 
+		= direction_cosines[2]*direction_cosines[3] 
+		- direction_cosines[0]*direction_cosines[5];
+	    direction_cosines[8] 
+		= direction_cosines[0]*direction_cosines[4] 
+		- direction_cosines[1]*direction_cosines[3];
+	}
+    }
+    
     /* Rows */
     if (!d_ptr->ds_rtdose->get_uint16 (DCM_Rows, &val_u16)) {
         print_and_exit ("Couldn't find DCM_Rows in rtdose\n");
@@ -196,10 +216,14 @@ Dcmtk_loader::rtdose_load ()
         sscanf (val, "%f", &dose_scaling);
     }
 
-    printf ("RTDOSE: dim = %d %d %d\n        %f %f %f\n        %f %f %f\n",
+    printf ("RTDOSE: dim = %d %d %d\n  ipp = %f %f %f\n  spc = %f %f %f\n"
+        "  dc  = %f %f %f %f %f %f\n",
         (int) dim[0], (int) dim[1], (int) dim[2],
         ipp[0], ipp[1], ipp[2], 
-        spacing[0], spacing[1], spacing[2]);
+        spacing[0], spacing[1], spacing[2],
+        direction_cosines[0], direction_cosines[1], direction_cosines[2], 
+        direction_cosines[3], direction_cosines[4], direction_cosines[5]
+    );
 
     uint16_t bits_alloc, bits_stored, high_bit, pixel_rep;
     rc = d_ptr->ds_rtdose->get_uint16 (DCM_BitsAllocated, &bits_alloc);
@@ -229,7 +253,7 @@ Dcmtk_loader::rtdose_load ()
     this->set_dose (dose);
 
     /* Create Volume */
-    Volume *vol = new Volume (dim, ipp, spacing, 0, PT_FLOAT, 1);
+    Volume *vol = new Volume (dim, ipp, spacing, direction_cosines, PT_FLOAT, 1);
     float *img = (float*) vol->img;
 
     /* Bind volume to plm_image */
@@ -306,6 +330,9 @@ Dcmtk_rt_study::save_dose (const char *dicom_dir)
         d_ptr->rt_study_metadata->get_study_date());
     dataset->putAndInsertOFStringArray (DCM_StudyTime, 
         d_ptr->rt_study_metadata->get_study_time());
+    dcmtk_copy_from_metadata (dataset, dose_metadata, 
+        DCM_StudyDescription, "");
+
     dataset->putAndInsertOFStringArray (DCM_AccessionNumber, "");
     dataset->putAndInsertOFStringArray (DCM_Modality, "RTDOSE");
     dataset->putAndInsertString (DCM_Manufacturer, "Plastimatch");
@@ -348,7 +375,7 @@ Dcmtk_rt_study::save_dose (const char *dicom_dir)
     dataset->putAndInsertString (DCM_SeriesInstanceUID, 
         d_ptr->rt_study_metadata->get_dose_series_uid());
     dcmtk_copy_from_metadata (dataset, dose_metadata, DCM_StudyID, "10001");
-    dataset->putAndInsertString (DCM_SeriesNumber, "");
+    dcmtk_copy_from_metadata (dataset, dose_metadata, DCM_SeriesNumber, "1");
     dataset->putAndInsertString (DCM_InstanceNumber, "1");
     
     s = string_format ("%g\\%g\\%g", dose_volume->origin[0], 
diff --git a/src/plastimatch/base/dcmtk_rtss.cxx b/src/plastimatch/base/dcmtk_rtss.cxx
index 0038417..3231707 100755
--- a/src/plastimatch/base/dcmtk_rtss.cxx
+++ b/src/plastimatch/base/dcmtk_rtss.cxx
@@ -277,6 +277,9 @@ Dcmtk_rt_study::save_rtss (const char *dicom_dir)
         d_ptr->rt_study_metadata->get_study_date());
     dataset->putAndInsertOFStringArray (DCM_StudyTime, 
         d_ptr->rt_study_metadata->get_study_time());
+    dcmtk_copy_from_metadata (dataset, rtss_metadata, 
+        DCM_StudyDescription, "");
+
     dataset->putAndInsertOFStringArray (DCM_AccessionNumber, "");
     dataset->putAndInsertOFStringArray (DCM_Modality, "RTSTRUCT");
     dataset->putAndInsertString (DCM_Manufacturer, "Plastimatch");
@@ -305,7 +308,7 @@ Dcmtk_rt_study::save_rtss (const char *dicom_dir)
     dataset->putAndInsertString (DCM_SeriesInstanceUID, 
         d_ptr->rt_study_metadata->get_rtss_series_uid());
     dcmtk_copy_from_metadata (dataset, rtss_metadata, DCM_StudyID, "10001");
-    dataset->putAndInsertString (DCM_SeriesNumber, "103");
+    dcmtk_copy_from_metadata (dataset, rtss_metadata, DCM_SeriesNumber, "1");
     dataset->putAndInsertString (DCM_InstanceNumber, "1");
     dataset->putAndInsertString (DCM_StructureSetLabel, "AutoSS");
     dataset->putAndInsertString (DCM_StructureSetName, "AutoSS");
diff --git a/src/plastimatch/base/dcmtk_sro.cxx b/src/plastimatch/base/dcmtk_sro.cxx
index 734460c..d4e6aa0 100644
--- a/src/plastimatch/base/dcmtk_sro.cxx
+++ b/src/plastimatch/base/dcmtk_sro.cxx
@@ -8,6 +8,7 @@
 #include "dcmtk/ofstd/ofstream.h"
 #include "dcmtk/dcmdata/dctk.h"
 
+#include "dcmtk_metadata.h"
 #include "dcmtk_module_general_series.h"
 #include "dcmtk_module_general_study.h"
 #include "dcmtk_module_patient.h"
@@ -23,8 +24,8 @@
 void
 Dcmtk_sro::save (
     const Xform::Pointer& xf,
-    const Rt_study_metadata::Pointer& rsm_src,   /* Fixed image */
-    const Rt_study_metadata::Pointer& rsm_reg,   /* Moving image */
+    const Rt_study_metadata::Pointer& rsm_fixed,
+    const Rt_study_metadata::Pointer& rsm_moving,
     const std::string& dicom_dir,
     bool filenames_with_uid)
 {
@@ -40,38 +41,44 @@ Dcmtk_sro::save (
 
     Rt_study_metadata::Pointer rsm;
     Metadata::Pointer study_meta;
-    if (!rsm_src || !rsm_reg) {
+    if (!rsm_fixed || !rsm_moving) {
         print_and_exit ("Sorry, anonymous spatial registration objects "
             "are not yet supported.\n");
     }
 
-    /* Not sure about this... */
-    rsm = rsm_src;
-    study_meta = rsm_src->get_study_metadata ();
-
     /* Patient module, general study module */
-    Dcmtk_module_patient::set (dataset, study_meta);
-    Dcmtk_module_general_study::set (dataset, rsm);
+    Dcmtk_module_patient::set (dataset, rsm_fixed->get_study_metadata ());
+    Dcmtk_module_general_study::set (dataset, rsm_fixed);
+    dcmtk_copy_from_metadata (dataset, rsm_fixed->get_study_metadata (),
+        DCM_StudyDescription, "");
 
     /* General series module */
-    Dcmtk_module_general_series::set_sro (dataset, rsm);
+    Dcmtk_module_general_series::set_sro (dataset, rsm_fixed);
 
     /* Spatial registration specific items */
-    std::string sro_sop_instance_uid = dicom_uid(PLM_UID_PREFIX);
+    std::string sro_sop_instance_uid = dicom_uid (PLM_UID_PREFIX);
     dataset->putAndInsertString (DCM_Modality, "REG");
     dataset->putAndInsertString (DCM_SOPClassUID, 
         UID_SpatialRegistrationStorage);
     dataset->putAndInsertString (DCM_SOPInstanceUID, 
         sro_sop_instance_uid.c_str());
+
+    /* Study and content date/time are tricky.  Which image should be used?
+       The below is correct for xvi_archive program, but could be made 
+       more general. */
+    dataset->putAndInsertOFStringArray (DCM_StudyDate,
+        rsm_moving->get_study_date());
+    dataset->putAndInsertOFStringArray (DCM_StudyTime,
+        rsm_moving->get_study_time());
     dataset->putAndInsertOFStringArray (DCM_ContentDate, 
-        rsm->get_study_date());
+        rsm_moving->get_study_date());
     dataset->putAndInsertOFStringArray (DCM_ContentTime, 
-        rsm->get_study_time());
+        rsm_moving->get_study_time());
 
     /* ReferencedSeriesSequence */
     DcmItem *rss_item = 0;
     DcmItem *ris_item = 0;
-    /* fixed */
+    /* moving */
     dataset->findOrCreateSequenceItem (
         DCM_ReferencedSeriesSequence, rss_item, -2);
     rss_item->findOrCreateSequenceItem (
@@ -79,10 +86,10 @@ Dcmtk_sro::save (
     ris_item->putAndInsertString (DCM_ReferencedSOPClassUID,
         UID_CTImageStorage);
     ris_item->putAndInsertString (DCM_ReferencedSOPInstanceUID,
-        rsm_reg->get_slice_uid (0));
+        rsm_moving->get_slice_uid (0));
     rss_item->putAndInsertString (DCM_SeriesInstanceUID,
-        rsm_reg->get_ct_series_uid ());
-    /* moving */
+        rsm_moving->get_ct_series_uid ());
+    /* fixed */
     dataset->findOrCreateSequenceItem (
         DCM_ReferencedSeriesSequence, rss_item, -2);
     rss_item->findOrCreateSequenceItem (
@@ -90,14 +97,14 @@ Dcmtk_sro::save (
     ris_item->putAndInsertString (DCM_ReferencedSOPClassUID,
         UID_CTImageStorage);
     ris_item->putAndInsertString (DCM_ReferencedSOPInstanceUID,
-        rsm_src->get_slice_uid (0));
+        rsm_fixed->get_slice_uid (0));
     rss_item->putAndInsertString (DCM_SeriesInstanceUID,
-        rsm_src->get_ct_series_uid ());
+        rsm_fixed->get_ct_series_uid ());
 
     /* FrameOfReferenceUID -- of fixed image */
     dataset->putAndInsertString (
         DCM_FrameOfReferenceUID, 
-        rsm_reg->get_frame_of_reference_uid());
+        rsm_fixed->get_frame_of_reference_uid());
 
     /* Spatial registration module -- fixed image */
     DcmItem *reg_item = 0;
@@ -105,7 +112,7 @@ Dcmtk_sro::save (
         DCM_RegistrationSequence, reg_item, -2);
     reg_item->putAndInsertString (
         DCM_FrameOfReferenceUID, 
-        rsm_reg->get_frame_of_reference_uid());
+        rsm_fixed->get_frame_of_reference_uid());
     DcmItem *mr_item = 0;
     reg_item->findOrCreateSequenceItem (
         DCM_MatrixRegistrationSequence, mr_item, -2);
@@ -128,7 +135,7 @@ Dcmtk_sro::save (
         DCM_RegistrationSequence, reg_item, -2);
     reg_item->putAndInsertString (
         DCM_FrameOfReferenceUID, 
-        rsm_src->get_frame_of_reference_uid());
+        rsm_moving->get_frame_of_reference_uid());
     reg_item->findOrCreateSequenceItem (
         DCM_MatrixRegistrationSequence, mr_item, -2);
     mr_item->findOrCreateSequenceItem (
@@ -138,32 +145,51 @@ Dcmtk_sro::save (
     rtc_item->putAndInsertString (DCM_CodeMeaning, "Visual Alignment");
     mr_item->findOrCreateSequenceItem (DCM_MatrixSequence, m_item, -2);
     std::string matrix_string;
+
     const AffineTransformType::MatrixType& itk_aff_mat 
         = itk_aff->GetMatrix ();
     const AffineTransformType::OutputVectorType& itk_aff_off 
         = itk_aff->GetOffset ();
+
+    printf ("ITK_AFF_OFF\n%f %f %f\n",
+        itk_aff_off[0], itk_aff_off[1], itk_aff_off[2]);
+    
+    /* Nb. ITK does not easily create an inverse affine transform. 
+       Therefore we play with the matrices. */
+    vnl_matrix_fixed< double, 3, 3 > itk_aff_mat_inv =
+        itk_aff_mat.GetInverse();
+    
     matrix_string = string_format (
         "%f\\%f\\%f\\%f\\"
         "%f\\%f\\%f\\%f\\"
         "%f\\%f\\%f\\%f\\"
         "0.0\\0.0\\0.0\\1.0",
-        itk_aff_mat[0][0],
-        itk_aff_mat[0][1],
-        itk_aff_mat[0][2],
-        itk_aff_off[0],
-        itk_aff_mat[1][0],
-        itk_aff_mat[1][1],
-        itk_aff_mat[1][2],
-        itk_aff_off[1],
-        itk_aff_mat[2][0],
-        itk_aff_mat[2][1],
-        itk_aff_mat[2][2],
-        itk_aff_off[2]);
+        itk_aff_mat_inv[0][0],
+        itk_aff_mat_inv[0][1],
+        itk_aff_mat_inv[0][2],
+        - itk_aff_mat_inv[0][0] * itk_aff_off[0]
+        - itk_aff_mat_inv[0][1] * itk_aff_off[1]
+        - itk_aff_mat_inv[0][2] * itk_aff_off[2],
+        itk_aff_mat_inv[1][0],
+        itk_aff_mat_inv[1][1],
+        itk_aff_mat_inv[1][2],
+        - itk_aff_mat_inv[1][0] * itk_aff_off[0]
+        - itk_aff_mat_inv[1][1] * itk_aff_off[1]
+        - itk_aff_mat_inv[1][2] * itk_aff_off[2],
+        itk_aff_mat_inv[2][0],
+        itk_aff_mat_inv[2][1],
+        itk_aff_mat_inv[2][2],
+        - itk_aff_mat_inv[2][0] * itk_aff_off[0]
+        - itk_aff_mat_inv[2][1] * itk_aff_off[1]
+        - itk_aff_mat_inv[2][2] * itk_aff_off[2]
+    );
     m_item->putAndInsertString (DCM_FrameOfReferenceTransformationMatrix,
         matrix_string.c_str());
     m_item->putAndInsertString (DCM_FrameOfReferenceTransformationMatrixType,
         "RIGID");
 
+    printf ("SRO\n%s\n", matrix_string.c_str());
+    
     /* Prepare output file */
     std::string sro_fn;
     if (filenames_with_uid) {
diff --git a/src/plastimatch/base/direction_cosines.cxx b/src/plastimatch/base/direction_cosines.cxx
index b3ec817..fa46e84 100755
--- a/src/plastimatch/base/direction_cosines.cxx
+++ b/src/plastimatch/base/direction_cosines.cxx
@@ -172,11 +172,7 @@ Direction_cosines::set (const DirectionType& itk_dc)
 {
     for (unsigned int d1 = 0; d1 < 3; d1++) {
 	for (unsigned int d2 = 0; d2 < 3; d2++) {
-#if defined (PLM_CONFIG_ALT_DCOS)
-	    d_ptr->direction_matrix[d1*3+d2] = itk_dc[d2][d1];
-#else
 	    d_ptr->direction_matrix[d1*3+d2] = itk_dc[d1][d2];
-#endif
 	}
     }
     solve_inverse ();
diff --git a/src/plastimatch/util/float_pair_list.cxx b/src/plastimatch/base/float_pair_list.cxx
similarity index 100%
rename from src/plastimatch/util/float_pair_list.cxx
rename to src/plastimatch/base/float_pair_list.cxx
diff --git a/src/plastimatch/util/float_pair_list.h b/src/plastimatch/base/float_pair_list.h
similarity index 82%
rename from src/plastimatch/util/float_pair_list.h
rename to src/plastimatch/base/float_pair_list.h
index 6cbff52..c3938b2 100755
--- a/src/plastimatch/util/float_pair_list.h
+++ b/src/plastimatch/base/float_pair_list.h
@@ -4,13 +4,13 @@
 #ifndef _float_pair_list_h_
 #define _float_pair_list_h_
 
-#include "plmutil_config.h"
+#include "plmbase_config.h"
 #include <list>
 #include <utility>
 #include "itk_image_type.h"
 
 typedef std::list< std::pair< float, float > > Float_pair_list;
 
-PLMUTIL_API Float_pair_list parse_float_pairs (const std::string& s);
+PLMBASE_API Float_pair_list parse_float_pairs (const std::string& s);
 
 #endif
diff --git a/src/plastimatch/base/gdcm1_dose.cxx b/src/plastimatch/base/gdcm1_dose.cxx
index 08bb425..2c6420b 100755
--- a/src/plastimatch/base/gdcm1_dose.cxx
+++ b/src/plastimatch/base/gdcm1_dose.cxx
@@ -311,17 +311,18 @@ gdcm1_dose_save (
     gf->InsertValEntry ("1", 0x0020, 0x0013);
     /* ImagePositionPatient */
     s = gdcm::Util::Format ("%g\\%g\\%g", 
-	plh.m_origin[0], plh.m_origin[1], plh.m_origin[2]);
+    plh.GetOrigin()[0], plh.GetOrigin()[1], plh.GetOrigin()[2]);
     gf->InsertValEntry (s, 0x0020, 0x0032);
 
     /* ImageOrientationPatient */
+    itk::Matrix < double, 3, 3 > direction=plh.GetDirection();
     s = gdcm::Util::Format ("%g\\%g\\%g\\%g\\%g\\%g",
-	plh.m_direction[0][0],
-	plh.m_direction[0][1],
-	plh.m_direction[0][2],
-	plh.m_direction[1][0],
-	plh.m_direction[1][1],
-	plh.m_direction[1][2]);
+    direction[0][0],
+    direction[0][1],
+    direction[0][2],
+    direction[1][0],
+    direction[1][1],
+    direction[1][2]);
     gf->InsertValEntry (s, 0x0020, 0x0037);
 
     /* FrameOfReferenceUID */
@@ -332,7 +333,7 @@ gdcm1_dose_save (
     /* PhotometricInterpretation */
     gf->InsertValEntry ("MONOCHROME2", 0x0028, 0x0004);
     /* NumberOfFrames */
-    s = gdcm::Util::Format ("%d", plh.Size(2));
+    s = gdcm::Util::Format ("%d", plh.GetSize()[2]);
     gf->InsertValEntry (s, 0x0028, 0x0008);
 
     /* FrameIncrementPointer */
@@ -342,13 +343,13 @@ gdcm1_dose_save (
     gf->InsertBinEntry ((uint8_t*)fip, 4, 0x0028, 0x0009, std::string("AT"));
 
     /* Rows */
-    s = gdcm::Util::Format ("%d", plh.Size(1));
+    s = gdcm::Util::Format ("%d", plh.GetSize()[1]);
     gf->InsertValEntry (s, 0x0028, 0x0010);
     /* Columns */
-    s = gdcm::Util::Format ("%d", plh.Size(0));
+    s = gdcm::Util::Format ("%d", plh.GetSize()[0]);
     gf->InsertValEntry (s, 0x0028, 0x0011);
     /* PixelSpacing */
-    s = gdcm::Util::Format ("%g\\%g", plh.m_spacing[1], plh.m_spacing[0]);
+    s = gdcm::Util::Format ("%g\\%g", plh.GetSpacing()[1], plh.GetSpacing()[0]);
     gf->InsertValEntry (s, 0x0028, 0x0030);
 
     /* BitsAllocated */
@@ -380,8 +381,8 @@ gdcm1_dose_save (
 
     /* GridFrameOffsetVector */
     s = std::string ("0");
-    for (i = 1; i < plh.Size(2); i++) {
-	s += gdcm::Util::Format ("\\%g", i * plh.m_spacing[2]);
+    for (i = 1; i < plh.GetSize()[2]; i++) {
+    s += gdcm::Util::Format ("\\%g", i * plh.GetSpacing()[2]);
     }
     gf->InsertValEntry (s, 0x3004, 0x000c);
 
diff --git a/src/plastimatch/base/hnd_io.cxx b/src/plastimatch/base/hnd_io.cxx
index c353f14..823ba66 100644
--- a/src/plastimatch/base/hnd_io.cxx
+++ b/src/plastimatch/base/hnd_io.cxx
@@ -85,7 +85,7 @@ hnd_set_proj_matrix (
     vec3_copy (cam, tgt);
     vec3_sub2 (cam, tmp);
 
-    proj_matrix_set (pmat, cam, tgt, vup, sid, pmat->ic, ps, proj->dim);
+    pmat->set (cam, tgt, vup, sid, pmat->ic, ps, proj->dim);
 }
 
 /* -----------------------------------------------------------------------
diff --git a/src/plastimatch/base/itk_dicom_save.h b/src/plastimatch/base/itk_dicom_save.h
index 4857777..e97fd49 100755
--- a/src/plastimatch/base/itk_dicom_save.h
+++ b/src/plastimatch/base/itk_dicom_save.h
@@ -8,7 +8,6 @@
 #include "itk_image_type.h"
 
 class Rt_study_metadata;
-class Slice_index;
 
 void
 itk_dicom_save (
diff --git a/src/plastimatch/base/itk_image.cxx b/src/plastimatch/base/itk_image.cxx
index a88dba2..b5c66c6 100644
--- a/src/plastimatch/base/itk_image.cxx
+++ b/src/plastimatch/base/itk_image.cxx
@@ -114,10 +114,10 @@ template<class T>
 void
 itk_image_set_header (T dest, const Plm_image_header *pih)
 {
-    dest->SetRegions (pih->m_region);
-    dest->SetOrigin (pih->m_origin);
-    dest->SetSpacing (pih->m_spacing);
-    dest->SetDirection (pih->m_direction);
+    dest->SetRegions (pih->GetRegion());
+    dest->SetOrigin (pih->GetOrigin());
+    dest->SetSpacing (pih->GetSpacing());
+    dest->SetDirection (pih->GetDirection());
 }
 
 template<class T>
@@ -154,15 +154,13 @@ itk_image_header_compare (T image1, U image2)
     typedef typename U::ObjectType I1ImageType;
     typedef typename T::ObjectType I2ImageType;
 
-    const typename I1ImageType::SizeType& i1_sz
-	= image1->GetLargestPossibleRegion().GetSize ();
-    const typename I1ImageType::PointType& i1_og = image1->GetOrigin();
+    const SizeType& i1_sz = image1->GetLargestPossibleRegion().GetSize ();
+    const OriginType i1_og = itk_image_origin (image1);
     const typename I1ImageType::SpacingType& i1_sp = image1->GetSpacing();
     const typename I1ImageType::DirectionType& i1_dc = image1->GetDirection();
 
-    const typename I2ImageType::SizeType& i2_sz
-	= image2->GetLargestPossibleRegion().GetSize ();
-    const typename I2ImageType::PointType& i2_og = image2->GetOrigin();
+    const SizeType& i2_sz = image2->GetLargestPossibleRegion().GetSize ();
+    const OriginType i2_og = itk_image_origin (image2);
     const typename I2ImageType::SpacingType& i2_sp = image2->GetSpacing();
     const typename I2ImageType::DirectionType& i2_dc = image2->GetDirection();
 
@@ -190,7 +188,6 @@ itk_image_fix_negative_spacing (T img)
     typename T::ObjectType::SpacingType sp = img->GetSpacing ();
     typename T::ObjectType::DirectionType dc = img->GetDirection ();
 
-    /* Copy header & allocate data for gpuit float */
     for (int d = 0; d < 3; d++) {
         if (sp[d] < 0) {
             sp[d] = -sp[d];
diff --git a/src/plastimatch/base/itk_image.h b/src/plastimatch/base/itk_image.h
index 564ac0a..547673a 100644
--- a/src/plastimatch/base/itk_image.h
+++ b/src/plastimatch/base/itk_image.h
@@ -5,7 +5,7 @@
 #define _itk_image_h_
 
 #include "plmbase_config.h"
-#include "sys/plm_int.h"
+#include "plm_int.h"
 
 #include "itk_image_type.h"
 
@@ -13,12 +13,13 @@ class Plm_image_header;
 class Volume_header;
 
 /* Other types */
-typedef itk::VariableLengthVector<unsigned char> UCharVecType;
-typedef itk::Size < 3 > SizeType;
+typedef itk::Matrix < double, 3, 3 > DirectionType;
+typedef itk::Index < 3 >  IndexType;
 typedef itk::Point < double, 3 >  OriginType;
+typedef itk::ImageRegion < 3 > RegionType;
+typedef itk::Size < 3 > SizeType;
 typedef itk::Vector < double, 3 > SpacingType;
-typedef itk::Matrix < double, 3, 3 > DirectionType;
-typedef itk::ImageRegion < 3 > ImageRegionType;
+typedef itk::VariableLengthVector<unsigned char> UCharVecType;
 
 /* -----------------------------------------------------------------------
    Function prototypes
diff --git a/src/plastimatch/base/itk_image_create.cxx b/src/plastimatch/base/itk_image_create.cxx
index d605726..80a20cd 100644
--- a/src/plastimatch/base/itk_image_create.cxx
+++ b/src/plastimatch/base/itk_image_create.cxx
@@ -19,13 +19,30 @@ itk_image_create (const Plm_image_header& pih)
     img->SetOrigin (pih.GetOrigin());
     img->SetSpacing (pih.GetSpacing());
     img->SetDirection (pih.GetDirection());
-    img->SetRegions (pih.GetLargestPossibleRegion());
+    img->SetRegions (pih.GetRegion());
     img->Allocate ();
     img->FillBuffer (static_cast<T>(0));
 
     return img;
 }
 
+template<>
+DeformationFieldType::Pointer
+itk_image_create<FloatVector3DType> (const Plm_image_header& pih)
+{
+    DeformationFieldType::Pointer img = DeformationFieldType::New ();
+    img->SetOrigin (pih.GetOrigin());
+    img->SetSpacing (pih.GetSpacing());
+    img->SetDirection (pih.GetDirection());
+    img->SetRegions (pih.GetRegion());
+    img->Allocate ();
+    FloatVector3DType v;
+    v.Fill (0);
+    img->FillBuffer (v);
+    return img;
+}
+
 /* Explicit instantiations */
-template PLMBASE_API itk::Image<float,3>::Pointer itk_image_create<float> (const Plm_image_header& pih);
 template PLMBASE_API itk::Image<unsigned char,3>::Pointer itk_image_create<unsigned char> (const Plm_image_header& pih);
+template PLMBASE_API itk::Image<float,3>::Pointer itk_image_create<float> (const Plm_image_header& pih);
+//template PLMBASE_API itk::Image<FloatVector3DType,3>::Pointer itk_image_create<FloatVector3DType> (const Plm_image_header& pih);
diff --git a/src/plastimatch/base/itk_image_load.txx b/src/plastimatch/base/itk_image_load.txx
index 73f553d..7894cda 100644
--- a/src/plastimatch/base/itk_image_load.txx
+++ b/src/plastimatch/base/itk_image_load.txx
@@ -176,6 +176,7 @@ template<class T>
 T
 itk_image_load_postprocess (T img)
 {
+//    img = orient_image (img);
     img = itk_image_fix_negative_spacing (img);
     return img;
 }
diff --git a/src/plastimatch/base/itk_image_load_char.cxx b/src/plastimatch/base/itk_image_load_char.cxx
index 5e09d95..e63c269 100755
--- a/src/plastimatch/base/itk_image_load_char.cxx
+++ b/src/plastimatch/base/itk_image_load_char.cxx
@@ -17,7 +17,5 @@ itk_image_load_char (const char* fname, Plm_image_type* original_type)
 	img = itk_image_load_any (fname, original_type, 
 	    static_cast<char>(0));
     }
-    //return orient_image (img);
-    //return img;
     return itk_image_load_postprocess (img);
 }
diff --git a/src/plastimatch/base/itk_image_load_double.cxx b/src/plastimatch/base/itk_image_load_double.cxx
index a4854f5..3f0628a 100755
--- a/src/plastimatch/base/itk_image_load_double.cxx
+++ b/src/plastimatch/base/itk_image_load_double.cxx
@@ -16,7 +16,5 @@ itk_image_load_double (const char* fname, Plm_image_type* original_type)
     } else {
 	img = itk_image_load_any (fname, original_type, static_cast<double>(0));
     }
-    //return orient_image (img);
-    //return img;
     return itk_image_load_postprocess (img);
 }
diff --git a/src/plastimatch/base/itk_image_load_float.cxx b/src/plastimatch/base/itk_image_load_float.cxx
index 172472e..3d07e6c 100644
--- a/src/plastimatch/base/itk_image_load_float.cxx
+++ b/src/plastimatch/base/itk_image_load_float.cxx
@@ -16,8 +16,6 @@ itk_image_load_float (const char* fname, Plm_image_type* original_type)
     } else {
 	img = itk_image_load_any (fname, original_type, static_cast<float>(0));
     }
-    //return orient_image (img);
-    //return img;
     return itk_image_load_postprocess (img);
 }
 
diff --git a/src/plastimatch/base/itk_image_load_int32.cxx b/src/plastimatch/base/itk_image_load_int32.cxx
index 2a0b90a..155ee5b 100644
--- a/src/plastimatch/base/itk_image_load_int32.cxx
+++ b/src/plastimatch/base/itk_image_load_int32.cxx
@@ -16,7 +16,5 @@ itk_image_load_int32 (const char* fname, Plm_image_type* original_type)
     } else {
 	img = itk_image_load_any (fname, original_type, static_cast<int32_t>(0));
     }
-    //return orient_image (img);
-    //return img;
     return itk_image_load_postprocess (img);
 }
diff --git a/src/plastimatch/base/itk_image_load_short.cxx b/src/plastimatch/base/itk_image_load_short.cxx
index b67563c..2eec3cd 100644
--- a/src/plastimatch/base/itk_image_load_short.cxx
+++ b/src/plastimatch/base/itk_image_load_short.cxx
@@ -16,7 +16,5 @@ itk_image_load_short (const char* fname, Plm_image_type* original_type)
     } else {
 	img = itk_image_load_any (fname, original_type, static_cast<short>(0));
     }
-    //return orient_image (img);
-    //return img;
     return itk_image_load_postprocess (img);
 }
diff --git a/src/plastimatch/base/itk_image_load_uchar.cxx b/src/plastimatch/base/itk_image_load_uchar.cxx
index cf8b8e2..2ca9007 100644
--- a/src/plastimatch/base/itk_image_load_uchar.cxx
+++ b/src/plastimatch/base/itk_image_load_uchar.cxx
@@ -17,8 +17,6 @@ itk_image_load_uchar (const char* fname, Plm_image_type* original_type)
 	img = itk_image_load_any (fname, original_type, 
 	    static_cast<unsigned char>(0));
     }
-    //return orient_image (img);
-    //return img;
     return itk_image_load_postprocess (img);
 }
 
diff --git a/src/plastimatch/base/itk_image_load_uint32.cxx b/src/plastimatch/base/itk_image_load_uint32.cxx
index d381f29..80f6ae1 100644
--- a/src/plastimatch/base/itk_image_load_uint32.cxx
+++ b/src/plastimatch/base/itk_image_load_uint32.cxx
@@ -16,7 +16,5 @@ itk_image_load_uint32 (const char* fname, Plm_image_type* original_type)
     } else {
 	img = itk_image_load_any (fname, original_type, static_cast<uint32_t>(0));
     }
-    //return orient_image (img);
-    //return img;
     return itk_image_load_postprocess (img);
 }
diff --git a/src/plastimatch/base/itk_image_load_ushort.cxx b/src/plastimatch/base/itk_image_load_ushort.cxx
index 74ec026..2b73382 100644
--- a/src/plastimatch/base/itk_image_load_ushort.cxx
+++ b/src/plastimatch/base/itk_image_load_ushort.cxx
@@ -16,7 +16,5 @@ itk_image_load_ushort (const char* fname, Plm_image_type* original_type)
     } else {
 	img = itk_image_load_any (fname, original_type, static_cast<unsigned short>(0));
     }
-    //return orient_image (img);
-    //return img;
     return itk_image_load_postprocess (img);
 }
diff --git a/src/plastimatch/base/itk_image_origin.cxx b/src/plastimatch/base/itk_image_origin.cxx
new file mode 100644
index 0000000..8d2af38
--- /dev/null
+++ b/src/plastimatch/base/itk_image_origin.cxx
@@ -0,0 +1,53 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plmbase_config.h"
+
+#include "itk_image_origin.h"
+
+template<class T> 
+OriginType
+itk_image_origin (const T& image)
+{
+    OriginType origin;
+    image->TransformIndexToPhysicalPoint (
+ 	image->GetLargestPossibleRegion().GetIndex(), origin);
+    return origin;
+}
+
+template<class T> 
+OriginType
+itk_image_origin (const T* image)
+{
+    OriginType origin;
+    image->TransformIndexToPhysicalPoint (
+ 	image->GetLargestPossibleRegion().GetIndex(), origin);
+    return origin;
+}
+
+/* GCS FIX: The below does not work, because OriginType is a 4D vector */
+//template PLMBASE_API OriginType itk_image_origin (const UCharImage4DType::Pointer& image);
+
+
+/* Explicit instantiations */
+template PLMBASE_API OriginType itk_image_origin (const CharImageType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const UCharImageType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const ShortImageType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const UShortImageType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const Int32ImageType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const UInt32ImageType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const FloatImageType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const DoubleImageType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const DeformationFieldType::Pointer& image);
+template PLMBASE_API OriginType itk_image_origin (const UCharVecImageType::Pointer& image);
+
+template PLMBASE_API OriginType itk_image_origin (const CharImageType* image);
+template PLMBASE_API OriginType itk_image_origin (const UCharImageType* image);
+template PLMBASE_API OriginType itk_image_origin (const ShortImageType* image);
+template PLMBASE_API OriginType itk_image_origin (const UShortImageType* image);
+template PLMBASE_API OriginType itk_image_origin (const Int32ImageType* image);
+template PLMBASE_API OriginType itk_image_origin (const UInt32ImageType* image);
+template PLMBASE_API OriginType itk_image_origin (const FloatImageType* image);
+template PLMBASE_API OriginType itk_image_origin (const DoubleImageType* image);
+template PLMBASE_API OriginType itk_image_origin (const DeformationFieldType* image);
+template PLMBASE_API OriginType itk_image_origin (const UCharVecImageType* image);
diff --git a/src/plastimatch/base/plm_image_p.h b/src/plastimatch/base/itk_image_origin.h
similarity index 54%
copy from src/plastimatch/base/plm_image_p.h
copy to src/plastimatch/base/itk_image_origin.h
index bf611b3..1732930 100644
--- a/src/plastimatch/base/plm_image_p.h
+++ b/src/plastimatch/base/itk_image_origin.h
@@ -1,16 +1,12 @@
 /* -----------------------------------------------------------------------
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
-#ifndef _plm_image_p_h_
-#define _plm_image_p_h_
+#ifndef _itk_image_origin_h_
+#define _itk_image_origin_h_
 
-#include "plmbase_config.h"
-#include "volume.h"
+#include "itk_image.h"
 
-class Plm_image_private {
-public:
-    Metadata::Pointer m_meta;
-    Volume::Pointer m_vol;
-};
+template<class T> OriginType itk_image_origin (const T&);
+template<class T> OriginType itk_image_origin (const T*);
 
 #endif
diff --git a/src/plastimatch/base/itk_image_region.cxx b/src/plastimatch/base/itk_image_region.cxx
new file mode 100644
index 0000000..9247224
--- /dev/null
+++ b/src/plastimatch/base/itk_image_region.cxx
@@ -0,0 +1,55 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plmbase_config.h"
+
+#include "itk_image_region.h"
+
+template<class T> 
+RegionType
+itk_image_region (const T& image)
+{
+    RegionType region = image->GetLargestPossibleRegion();
+    IndexType index;
+    index[0] = 0;
+    index[1] = 0;
+    index[2] = 0;
+    region.SetIndex (index);
+    return region;
+}
+
+template<class T> 
+RegionType
+itk_image_region (const T* image)
+{
+    RegionType region = image->GetLargestPossibleRegion();
+    IndexType index;
+    index[0] = 0;
+    index[1] = 0;
+    index[2] = 0;
+    region.SetIndex (index);
+    return region;
+}
+
+/* Explicit instantiations */
+template PLMBASE_API RegionType itk_image_region (const CharImageType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const UCharImageType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const ShortImageType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const UShortImageType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const Int32ImageType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const UInt32ImageType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const FloatImageType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const DoubleImageType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const DeformationFieldType::Pointer& image);
+template PLMBASE_API RegionType itk_image_region (const UCharVecImageType::Pointer& image);
+
+template PLMBASE_API RegionType itk_image_region (const CharImageType* image);
+template PLMBASE_API RegionType itk_image_region (const UCharImageType* image);
+template PLMBASE_API RegionType itk_image_region (const ShortImageType* image);
+template PLMBASE_API RegionType itk_image_region (const UShortImageType* image);
+template PLMBASE_API RegionType itk_image_region (const Int32ImageType* image);
+template PLMBASE_API RegionType itk_image_region (const UInt32ImageType* image);
+template PLMBASE_API RegionType itk_image_region (const FloatImageType* image);
+template PLMBASE_API RegionType itk_image_region (const DoubleImageType* image);
+template PLMBASE_API RegionType itk_image_region (const DeformationFieldType* image);
+template PLMBASE_API RegionType itk_image_region (const UCharVecImageType* image);
diff --git a/src/plastimatch/base/plm_image_p.h b/src/plastimatch/base/itk_image_region.h
similarity index 54%
copy from src/plastimatch/base/plm_image_p.h
copy to src/plastimatch/base/itk_image_region.h
index bf611b3..dfca911 100644
--- a/src/plastimatch/base/plm_image_p.h
+++ b/src/plastimatch/base/itk_image_region.h
@@ -1,16 +1,12 @@
 /* -----------------------------------------------------------------------
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
-#ifndef _plm_image_p_h_
-#define _plm_image_p_h_
+#ifndef _itk_image_region_h_
+#define _itk_image_region_h_
 
-#include "plmbase_config.h"
-#include "volume.h"
+#include "itk_image.h"
 
-class Plm_image_private {
-public:
-    Metadata::Pointer m_meta;
-    Volume::Pointer m_vol;
-};
+template<class T> RegionType itk_image_region (const T&);
+template<class T> RegionType itk_image_region (const T*);
 
 #endif
diff --git a/src/plastimatch/base/itk_image_save.cxx b/src/plastimatch/base/itk_image_save.cxx
index 94a9586..532323f 100644
--- a/src/plastimatch/base/itk_image_save.cxx
+++ b/src/plastimatch/base/itk_image_save.cxx
@@ -10,13 +10,6 @@
 #include "itkImageFileReader.h"
 #include "itkImageFileWriter.h"
 #include "itkOrientImageFilter.h"
-#if (defined(_WIN32) || defined(WIN32))
-#define snprintf _snprintf
-#define mkdir(a,b) _mkdir(a)
-#else
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
 
 #include "file_util.h"
 #include "itk_dicom_save.h"
diff --git a/src/plastimatch/base/itk_image_save.h b/src/plastimatch/base/itk_image_save.h
index 06de034..3d5e6cd 100644
--- a/src/plastimatch/base/itk_image_save.h
+++ b/src/plastimatch/base/itk_image_save.h
@@ -10,18 +10,17 @@
 
 class Metadata;
 class Rt_study_metadata;
-class Slice_index;
 
 /* -----------------------------------------------------------------------
    Function prototypes
    ----------------------------------------------------------------------- */
-void itk_image_save (const FloatImageType::Pointer& img_ptr, 
+PLMBASE_API void itk_image_save (const FloatImageType::Pointer& img_ptr, 
     const std::string& fname, Plm_image_type image_type);
-void itk_image_save (const FloatImageType::Pointer& img_ptr, 
+PLMBASE_API void itk_image_save (const FloatImageType::Pointer& img_ptr, 
     const char* fname, Plm_image_type image_type);
-template<class T> void itk_image_save (T img_ptr, const char* fname);
-template<class T> void itk_image_save (T img_ptr, const std::string& fname);
-template<class T> void itk_image_save_short_dicom (T image, const char* dir_name, Rt_study_metadata *);
+template<class T> PLMBASE_API void itk_image_save (T img_ptr, const char* fname);
+template<class T> PLMBASE_API void itk_image_save (T img_ptr, const std::string& fname);
+template<class T> PLMBASE_API void itk_image_save_short_dicom (T image, const char* dir_name, Rt_study_metadata *);
 
 template<class T> PLMBASE_API void itk_image_save_char (T img_ptr, const char* fname);
 template<class T> PLMBASE_API void itk_image_save_uchar (T img_ptr, const char* fname);
diff --git a/src/plastimatch/base/itk_image_type.h b/src/plastimatch/base/itk_image_type.h
index 7a3f3ff..69a5bd8 100755
--- a/src/plastimatch/base/itk_image_type.h
+++ b/src/plastimatch/base/itk_image_type.h
@@ -17,9 +17,6 @@
 //#endif
 
 #include "itkImageIOBase.h"
-#if (PLM_ITK_ORIENTED_IMAGES)
-#include "itkOrientedImage.h"
-#endif
 #include "itkVectorImage.h"
 
 #include "itk_point.h"
diff --git a/src/plastimatch/base/itk_resample.cxx b/src/plastimatch/base/itk_resample.cxx
index 63134e5..16eef99 100644
--- a/src/plastimatch/base/itk_resample.cxx
+++ b/src/plastimatch/base/itk_resample.cxx
@@ -26,10 +26,10 @@ vector_resample_image (const T& vf_image, const Plm_image_header* pih)
 
     typename FilterType::Pointer filter = FilterType::New();
 
-    filter->SetOutputOrigin (pih->m_origin);
-    filter->SetOutputSpacing (pih->m_spacing);
-    filter->SetSize (pih->m_region.GetSize());
-    filter->SetOutputDirection (pih->m_direction);
+    filter->SetOutputOrigin (pih->GetOrigin());
+    filter->SetOutputSpacing (pih->GetSpacing());
+    filter->SetSize (pih->GetSize());
+    filter->SetOutputDirection (pih->GetDirection());
 
     typedef itk::AffineTransform< double, 3 > TransformType;
     TransformType::Pointer transform = TransformType::New();
@@ -121,8 +121,8 @@ resample_image (
     float default_val, 
     int interp_lin)
 {
-    return resample_image (image, pih->m_origin, pih->m_spacing,
-        pih->m_region.GetSize(), pih->m_direction,
+    return resample_image (image, pih->GetOrigin(), pih->GetSpacing(),
+        pih->GetSize(), pih->GetDirection(),
         default_val, interp_lin);
 }
 
@@ -134,9 +134,8 @@ resample_image (
     float default_val, 
     int interp_lin)
 {
-    return resample_image (image, pih.m_origin, pih.m_spacing,
-        pih.m_region.GetSize(), pih.m_direction,
-        default_val, interp_lin);
+    return resample_image (image, pih.GetOrigin(), pih.GetSpacing(),
+        pih.GetSize(), pih.GetDirection(), default_val, interp_lin);
 }
 
 template <class T>
@@ -318,24 +317,30 @@ subsample_image (T& image, int x_sampling_rate,
 template PLMBASE_API DeformationFieldType::Pointer vector_resample_image (const DeformationFieldType::Pointer&, const Plm_image_header*);
 
 template PLMBASE_API UCharImageType::Pointer resample_image (UCharImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
-template PLMBASE_API ShortImageType::Pointer resample_image (ShortImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
+template PLMBASE_API CharImageType::Pointer resample_image (CharImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
 template PLMBASE_API UShortImageType::Pointer resample_image (UShortImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
-template PLMBASE_API Int32ImageType::Pointer resample_image (Int32ImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
+template PLMBASE_API ShortImageType::Pointer resample_image (ShortImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
 template PLMBASE_API UInt32ImageType::Pointer resample_image (UInt32ImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
+template PLMBASE_API Int32ImageType::Pointer resample_image (Int32ImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
 template PLMBASE_API FloatImageType::Pointer resample_image (FloatImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
+template PLMBASE_API DoubleImageType::Pointer resample_image (DoubleImageType::Pointer&, const Plm_image_header*, float default_val, int interp_lin);
 
 template PLMBASE_API UCharImageType::Pointer resample_image (UCharImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
-template PLMBASE_API ShortImageType::Pointer resample_image (ShortImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
+template PLMBASE_API CharImageType::Pointer resample_image (CharImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
 template PLMBASE_API UShortImageType::Pointer resample_image (UShortImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
-template PLMBASE_API Int32ImageType::Pointer resample_image (Int32ImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
+template PLMBASE_API ShortImageType::Pointer resample_image (ShortImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
 template PLMBASE_API UInt32ImageType::Pointer resample_image (UInt32ImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
+template PLMBASE_API Int32ImageType::Pointer resample_image (Int32ImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
 template PLMBASE_API FloatImageType::Pointer resample_image (FloatImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
+template PLMBASE_API DoubleImageType::Pointer resample_image (DoubleImageType::Pointer&, const Plm_image_header&, float default_val, int interp_lin);
 
 template PLMBASE_API FloatImageType::Pointer resample_image (FloatImageType::Pointer&, float spacing[3]);
 
 template PLMBASE_API UCharImageType::Pointer subsample_image (UCharImageType::Pointer&, int, int, int, float);
-template PLMBASE_API ShortImageType::Pointer subsample_image (ShortImageType::Pointer&, int, int, int, float);
+template PLMBASE_API CharImageType::Pointer subsample_image (CharImageType::Pointer&, int, int, int, float);
 template PLMBASE_API UShortImageType::Pointer subsample_image (UShortImageType::Pointer&, int, int, int, float);
-template PLMBASE_API Int32ImageType::Pointer subsample_image (Int32ImageType::Pointer&, int, int, int, float);
+template PLMBASE_API ShortImageType::Pointer subsample_image (ShortImageType::Pointer&, int, int, int, float);
 template PLMBASE_API UInt32ImageType::Pointer subsample_image (UInt32ImageType::Pointer&, int, int, int, float);
+template PLMBASE_API Int32ImageType::Pointer subsample_image (Int32ImageType::Pointer&, int, int, int, float);
 template PLMBASE_API FloatImageType::Pointer subsample_image (FloatImageType::Pointer&, int, int, int, float);
+template PLMBASE_API DoubleImageType::Pointer subsample_image (DoubleImageType::Pointer&, int, int, int, float);
diff --git a/src/plastimatch/base/itk_volume_header.cxx b/src/plastimatch/base/itk_volume_header.cxx
index bdc9374..41d808a 100644
--- a/src/plastimatch/base/itk_volume_header.cxx
+++ b/src/plastimatch/base/itk_volume_header.cxx
@@ -2,6 +2,7 @@
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
 #include "plmbase_config.h"
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
@@ -47,8 +48,8 @@ Itk_volume_header::set_spacing (float spacing[3])
 void
 Itk_volume_header::set_dim (plm_long dim[3])
 {
-    ImageRegionType::SizeType itk_size;
-    ImageRegionType::IndexType itk_index;
+    SizeType itk_size;
+    IndexType itk_index;
     for (unsigned int d = 0; d < 3; d++) {
 	itk_index[d] = 0;
 	itk_size[d] = dim[d];
@@ -64,8 +65,8 @@ Itk_volume_header::set_from_gpuit (
     plm_long gpuit_dim[3],
     float gpuit_direction_cosines[9])
 {
-    ImageRegionType::SizeType itk_size;
-    ImageRegionType::IndexType itk_index;
+    SizeType itk_size;
+    IndexType itk_index;
 
     for (unsigned int d1 = 0; d1 < 3; d1++) {
 	m_origin[d1] = gpuit_origin[d1];
@@ -111,7 +112,7 @@ Itk_volume_header::get_spacing (float spacing[3])
 void 
 Itk_volume_header::get_dim (plm_long dim[3])
 {
-    ImageRegionType::SizeType itk_size = m_region.GetSize ();
+    SizeType itk_size = m_region.GetSize ();
     for (unsigned int d = 0; d < 3; d++) {
 	dim[d] = itk_size[d];
     }
@@ -126,7 +127,7 @@ Itk_volume_header::get_direction_cosines (float direction_cosines[9])
 void
 Itk_volume_header::print (void) const
 {
-    ImageRegionType::SizeType itk_size;
+    SizeType itk_size;
     itk_size = m_region.GetSize ();
 
     printf ("Origin =");
@@ -153,10 +154,14 @@ Itk_volume_header::print (void) const
 void 
 Itk_volume_header::get_image_center (float center[3])
 {
-    int d;
-    for (d = 0; d < 3; d++) {
-	center[d] = this->m_origin[d] 
-	    + this->m_spacing[d] * (this->Size(d) - 1) / 2;
+    for (int d1 = 0; d1 < 3; d1++) {
+	center[d1] = this->m_origin[d1];
+    }
+    for (int d1 = 0; d1 < 3; d1++) {
+        float v = this->m_spacing[d1] * (this->Size(d1) - 1.) / 2;
+        for (int d2 = 0; d2 < 3; d2++) {
+            center[d2] += v * m_direction[d2][d1];
+        }
     }
 }
 
diff --git a/src/plastimatch/base/itk_volume_header.h b/src/plastimatch/base/itk_volume_header.h
index db405fb..09f604b 100644
--- a/src/plastimatch/base/itk_volume_header.h
+++ b/src/plastimatch/base/itk_volume_header.h
@@ -11,7 +11,7 @@ class Bspline_xform;
 
 class PLMBASE_API Itk_volume_header {
 public:
-    ImageRegionType m_region;
+    RegionType m_region;
     OriginType m_origin;
     SpacingType m_spacing;
     DirectionType m_direction;
diff --git a/src/plastimatch/base/metadata.cxx b/src/plastimatch/base/metadata.cxx
index 4356c94..7ffbea0 100644
--- a/src/plastimatch/base/metadata.cxx
+++ b/src/plastimatch/base/metadata.cxx
@@ -111,6 +111,23 @@ Metadata::set_metadata (unsigned short key1, unsigned short key2,
 }
 
 void
+Metadata::set_metadata (const std::vector<std::string>& metadata)
+{
+    std::vector<std::string>::const_iterator it = metadata.begin();
+    while (it < metadata.end()) {
+        const std::string& str = (*it);
+        size_t eq_pos = str.find_first_of ('=');
+        if (eq_pos != std::string::npos) {
+            std::string key = str.substr (0, eq_pos);
+            std::string val = str.substr (eq_pos+1);
+            /* Set newer-style metadata, used by dcmtk */
+            this->set_metadata (key, val);
+        }
+        ++it;
+    }
+}
+
+void
 Metadata::print_metadata () const
 {
     std::map<std::string, std::string>::const_iterator it;
diff --git a/src/plastimatch/base/metadata.h b/src/plastimatch/base/metadata.h
index bd966eb..0a98728 100644
--- a/src/plastimatch/base/metadata.h
+++ b/src/plastimatch/base/metadata.h
@@ -7,8 +7,14 @@
 #include "plmbase_config.h"
 #include <map>
 #include <string>
+#include <vector>
 #include "smart_pointer.h"
 
+/*! \brief 
+ * The Metadata class encapsulate DICOM metadata for a single series.
+ * It is implemented as a map from string to string, where the 
+ * key string is of the form "XXXX,XXXX".
+ */
 class PLMBASE_API Metadata
 {
 public:
@@ -18,7 +24,7 @@ public:
 
 public:
     /* GCS: Note use of unsigned short instead of uint16_t, because of 
-       broken stdint implementation in gdcm. */
+       broken stdint implementation in gdcm 1.X. */
     std::string
     make_key (unsigned short key1, unsigned short key2) const;
     const char*
@@ -31,9 +37,11 @@ public:
     get_metadata (unsigned short key1, unsigned short key2) const;
     void
     set_metadata (const std::string& key, const std::string& val);
-    void
-    set_metadata (unsigned short key1, unsigned short key2,
+    void set_metadata (unsigned short key1, unsigned short key2,
         const std::string& val);
+    /*! \brief Copy a list of strings of the form "XXXX,YYYY=string"
+      into the metadata map. */
+    void set_metadata (const std::vector<std::string>& metadata);
     void set_parent (const Metadata::Pointer& parent) {
         m_parent = parent;
     }
diff --git a/src/plastimatch/base/nki_io.cxx b/src/plastimatch/base/nki_io.cxx
index 26d42a9..bf2ef00 100644
--- a/src/plastimatch/base/nki_io.cxx
+++ b/src/plastimatch/base/nki_io.cxx
@@ -118,9 +118,9 @@ nki_load (const char* filename)
     vol->dim[0] = dim3;
     vol->dim[1] = dim2;
     vol->dim[2] = dim1;
-    vol->origin[0] = -0.5 * dim3;
-    vol->origin[1] = -0.5 * dim2;
-    vol->origin[2] = -0.5 * dim1;
+    vol->origin[0] = -0.5 * dim3 + 0.5;
+    vol->origin[1] = -0.5 * dim2 + 0.5;
+    vol->origin[2] = -0.5 * dim1 + 0.5;
     vol->img = (void*) tgt;
     vol->npix = dim1*dim2*dim3; //bug fixed by YKPark 20131029
 
diff --git a/src/plastimatch/base/plm_image.cxx b/src/plastimatch/base/plm_image.cxx
index 260e962..f757ebd 100644
--- a/src/plastimatch/base/plm_image.cxx
+++ b/src/plastimatch/base/plm_image.cxx
@@ -173,11 +173,11 @@ Plm_image::create (Plm_image_type type, const Plm_image_header& pih)
 /* -----------------------------------------------------------------------
    Cloning
    ----------------------------------------------------------------------- */
-Plm_image*
+Plm_image::Pointer
 Plm_image::clone (void)
 {
-    Plm_image *pli = new Plm_image;
-    if (!pli) return 0;
+    Plm_image::Pointer pli = Plm_image::New();
+    if (!pli) return pli;
 
     pli->m_original_type = this->m_original_type;
     pli->m_type = this->m_type;
@@ -218,8 +218,7 @@ Plm_image::clone (void)
 Plm_image::Pointer
 Plm_image::clone (const Plm_image::Pointer& pli)
 {
-    Plm_image* new_pli = pli->clone();
-    return Plm_image::New (new_pli);
+    return pli->clone ();
 }
 
 /* -----------------------------------------------------------------------
@@ -1507,11 +1506,15 @@ Plm_image::dim (size_t d1)
         return this->m_itk_float->GetLargestPossibleRegion().GetSize()[d];
     case PLM_IMG_TYPE_ITK_DOUBLE:
         return this->m_itk_double->GetLargestPossibleRegion().GetSize()[d];
+    case PLM_IMG_TYPE_GPUIT_UCHAR:
     case PLM_IMG_TYPE_GPUIT_SHORT:
     case PLM_IMG_TYPE_GPUIT_UINT16:
     case PLM_IMG_TYPE_GPUIT_UINT32:
     case PLM_IMG_TYPE_GPUIT_INT32:
     case PLM_IMG_TYPE_GPUIT_FLOAT:
+    case PLM_IMG_TYPE_GPUIT_FLOAT_FIELD:
+    case PLM_IMG_TYPE_GPUIT_UCHAR_VEC:
+        return d_ptr->m_vol->dim[d];
     case PLM_IMG_TYPE_ITK_UCHAR_VEC:
     default:
 	print_and_exit (
@@ -1522,6 +1525,7 @@ Plm_image::dim (size_t d1)
     return 0;
 }
 
+/* GCS FIX: This is inefficient. */
 float 
 Plm_image::origin (size_t d1)
 {
@@ -1530,26 +1534,30 @@ Plm_image::origin (size_t d1)
     case PLM_IMG_TYPE_UNDEFINED:
 	return 0;
     case PLM_IMG_TYPE_ITK_CHAR:
-        return this->m_itk_char->GetOrigin()[d];
+        return itk_image_origin(this->m_itk_char)[d];
     case PLM_IMG_TYPE_ITK_UCHAR:
-        return this->m_itk_uchar->GetOrigin()[d];
+        return itk_image_origin(this->m_itk_uchar)[d];
     case PLM_IMG_TYPE_ITK_SHORT:
-        return this->m_itk_short->GetOrigin()[d];
+        return itk_image_origin(this->m_itk_short)[d];
     case PLM_IMG_TYPE_ITK_USHORT:
-        return this->m_itk_ushort->GetOrigin()[d];
+        return itk_image_origin(this->m_itk_ushort)[d];
     case PLM_IMG_TYPE_ITK_LONG:
-        return this->m_itk_int32->GetOrigin()[d];
+        return itk_image_origin(this->m_itk_int32)[d];
     case PLM_IMG_TYPE_ITK_ULONG:
-        return this->m_itk_uint32->GetOrigin()[d];
+        return itk_image_origin(this->m_itk_uint32)[d];
     case PLM_IMG_TYPE_ITK_FLOAT:
-        return this->m_itk_float->GetOrigin()[d];
+        return itk_image_origin(this->m_itk_float)[d];
     case PLM_IMG_TYPE_ITK_DOUBLE:
-        return this->m_itk_double->GetOrigin()[d];
+        return itk_image_origin(this->m_itk_double)[d];
+    case PLM_IMG_TYPE_GPUIT_UCHAR:
     case PLM_IMG_TYPE_GPUIT_SHORT:
     case PLM_IMG_TYPE_GPUIT_UINT16:
     case PLM_IMG_TYPE_GPUIT_UINT32:
     case PLM_IMG_TYPE_GPUIT_INT32:
     case PLM_IMG_TYPE_GPUIT_FLOAT:
+    case PLM_IMG_TYPE_GPUIT_FLOAT_FIELD:
+    case PLM_IMG_TYPE_GPUIT_UCHAR_VEC:
+        return d_ptr->m_vol->origin[d];
     case PLM_IMG_TYPE_ITK_UCHAR_VEC:
     default:
 	print_and_exit (
@@ -1583,11 +1591,15 @@ Plm_image::spacing (size_t d1)
         return this->m_itk_float->GetSpacing()[d];
     case PLM_IMG_TYPE_ITK_DOUBLE:
         return this->m_itk_double->GetSpacing()[d];
+    case PLM_IMG_TYPE_GPUIT_UCHAR:
     case PLM_IMG_TYPE_GPUIT_SHORT:
     case PLM_IMG_TYPE_GPUIT_UINT16:
     case PLM_IMG_TYPE_GPUIT_UINT32:
     case PLM_IMG_TYPE_GPUIT_INT32:
     case PLM_IMG_TYPE_GPUIT_FLOAT:
+    case PLM_IMG_TYPE_GPUIT_FLOAT_FIELD:
+    case PLM_IMG_TYPE_GPUIT_UCHAR_VEC:
+        return d_ptr->m_vol->spacing[d];
     case PLM_IMG_TYPE_ITK_UCHAR_VEC:
     default:
 	print_and_exit (
diff --git a/src/plastimatch/base/plm_image.h b/src/plastimatch/base/plm_image.h
index fdeac43..b284c79 100644
--- a/src/plastimatch/base/plm_image.h
+++ b/src/plastimatch/base/plm_image.h
@@ -16,7 +16,6 @@ class Plm_image_header;
 class Plm_image;
 class Plm_image_private;
 class Rt_study_metadata;
-class Slice_index;
 
 /*! \brief 
  * The Plm_image class represents a three-dimensional volume.  
@@ -91,7 +90,7 @@ public:
     void init ();
     void free ();
     bool have_image ();
-    Plm_image* clone (void);
+    Plm_image::Pointer clone (void);
     void create (Plm_image_type type, const Plm_image_header& pih);
 
     /* Loading */
diff --git a/src/plastimatch/base/plm_image_convert.cxx b/src/plastimatch/base/plm_image_convert.cxx
index 93166c8..e6bd60d 100644
--- a/src/plastimatch/base/plm_image_convert.cxx
+++ b/src/plastimatch/base/plm_image_convert.cxx
@@ -77,14 +77,17 @@ Plm_image::convert_itk_to_gpuit (T img)
     typename ImageType::SpacingType sp = img->GetSpacing();
     typename ImageType::SizeType sz = rg.GetSize();
     typename ImageType::DirectionType dc = img->GetDirection();
+    typename ImageType::IndexType rgi = rg.GetIndex();
 
     /* Copy header & allocate data for gpuit float */
     plm_long dim[3];
+    plm_long rgidx[3];
     float origin[3];
     float spacing[3];
     float direction_cosines[9];
     for (d1 = 0; d1 < 3; d1++) {
         dim[d1] = sz[d1];
+        rgidx[d1] = rgi[d1];
         origin[d1] = og[d1];
         spacing[d1] = sp[d1];
     }
@@ -114,6 +117,9 @@ Plm_image::convert_itk_to_gpuit (T img)
         pix_type, 1);
     U *vol_img = (U*) vol->img;
 
+    /* Fix itk images with non-zero region indices */
+    vol->move_origin_to_idx (rgidx);
+
     /* Copy data into gpuit */
     typedef typename itk::ImageRegionIterator< ImageType > IteratorType;
     IteratorType it (img, rg);
diff --git a/src/plastimatch/base/plm_image_header.cxx b/src/plastimatch/base/plm_image_header.cxx
index 2802ea4..8c3cfd7 100644
--- a/src/plastimatch/base/plm_image_header.cxx
+++ b/src/plastimatch/base/plm_image_header.cxx
@@ -10,22 +10,152 @@
 #include <stdio.h>
 #include <math.h>
 
-
 #include "bspline_xform.h"
 #include "direction_cosines.h"
 #include "direction_matrices.h"
 #include "itk_directions.h"
+#include "logfile.h"
 #include "plm_image.h"
 #include "plm_image_header.h"
 #include "print_and_exit.h"
 #include "volume.h"
 #include "volume_header.h"
 
+class Plm_image_header_private
+{
+public:
+    OriginType m_origin;
+};
+
+/* -----------------------------------------------------------------------
+   Constructors, destructors, operator=
+   ----------------------------------------------------------------------- */
+Plm_image_header::Plm_image_header ()
+{
+    d_ptr = new Plm_image_header_private;
+}
+
+Plm_image_header::Plm_image_header (
+    plm_long dim[3], float origin[3], float spacing[3])
+{
+    d_ptr = new Plm_image_header_private;
+    this->set_from_gpuit (dim, origin, spacing, 0);
+}
+
+Plm_image_header::Plm_image_header (
+    plm_long dim[3], float origin[3], float spacing[3],
+    float direction_cosines[9])
+{
+    d_ptr = new Plm_image_header_private;
+    this->set_from_gpuit (dim, origin, spacing, direction_cosines);
+}
+
+Plm_image_header::Plm_image_header (
+    const RegionType& region, const OriginType& origin,
+    const SpacingType& spacing, const DirectionType& direction)
+{
+    d_ptr = new Plm_image_header_private;
+    this->set (region, origin,spacing, direction);
+}
+
+Plm_image_header::Plm_image_header (Plm_image *pli) 
+{
+    d_ptr = new Plm_image_header_private;
+    this->set_from_plm_image (pli);
+}
+
+Plm_image_header::Plm_image_header (const Plm_image& pli) 
+{
+    d_ptr = new Plm_image_header_private;
+    this->set_from_plm_image (pli);
+}
+
+Plm_image_header::Plm_image_header (const Plm_image::Pointer& pli) 
+{
+    d_ptr = new Plm_image_header_private;
+    this->set_from_plm_image (pli);
+}
+
+Plm_image_header::Plm_image_header (const Volume_header& vh) 
+{
+    d_ptr = new Plm_image_header_private;
+    this->set (vh);
+}
+
+Plm_image_header::Plm_image_header (const Volume& vol) 
+{
+    d_ptr = new Plm_image_header_private;
+    this->set (vol);
+}
+
+Plm_image_header::Plm_image_header (const Volume* vol) 
+{
+    d_ptr = new Plm_image_header_private;
+    this->set (vol);
+}
+
+Plm_image_header::Plm_image_header (Volume* vol) 
+{
+    d_ptr = new Plm_image_header_private;
+    this->set (vol);
+}
+
+template<class T> 
+Plm_image_header::Plm_image_header (T image) {
+    d_ptr = new Plm_image_header_private;
+    this->set_from_itk_image (image);
+}
+
+Plm_image_header::Plm_image_header (const Plm_image_header& other)
+{
+    d_ptr = new Plm_image_header_private ();
+    this->m_origin = other.m_origin;
+    this->m_spacing = other.m_spacing;
+    this->m_region = other.m_region;
+    this->m_direction = other.m_direction;
+}
+
+Plm_image_header::~Plm_image_header ()
+{
+    delete d_ptr;
+}
+
+const Plm_image_header& 
+Plm_image_header::operator= (const Plm_image_header& other)
+{
+    this->m_origin = other.m_origin;
+    this->m_spacing = other.m_spacing;
+    this->m_region = other.m_region;
+    this->m_direction = other.m_direction;
+    return *this;
+}
+
+/* -----------------------------------------------------------------------
+   Getters and Setters
+   ----------------------------------------------------------------------- */
+int 
+Plm_image_header::dim (int d) const
+{
+    return m_region.GetSize()[d];
+}
+
+float 
+Plm_image_header::origin (int d) const
+{
+    return m_origin[d];
+}
+
+float 
+Plm_image_header::spacing (int d) const
+{
+    return m_spacing[d];
+}
+
 void
 Plm_image_header::set_dim (const plm_long dim[3])
 {
-    ImageRegionType::SizeType itk_size;
-    ImageRegionType::IndexType itk_index;
+    RegionType::SizeType itk_size;
+    RegionType::IndexType itk_index;
     for (unsigned int d = 0; d < 3; d++) {
 	itk_index[d] = 0;
 	itk_size[d] = dim[d];
@@ -205,6 +335,130 @@ Plm_image_header::set (const Volume* vol)
 	vol->spacing, vol->direction_cosines);
 }
 
+void Plm_image_header::set (
+    const RegionType& region, const OriginType& origin,
+    const SpacingType& spacing, const DirectionType& direction)
+{
+    m_region = region;
+    m_origin = origin;
+    m_spacing = spacing;
+    m_direction = direction;
+
+    /* Adjust origin and set index to zero in case of non-zero 
+       ITK region index */
+    const IndexType& index = region.GetIndex();
+    for (int d1 = 0; d1 < 3; d1++) {
+        for (int d2 = 0; d2 < 3; d2++) {
+            m_origin[d2] += index[d1] * spacing[d1] * direction[d2][d1];
+        }
+    }
+    IndexType i2;
+    i2[0] = i2[1] = i2[2] = 0;
+    m_region.SetIndex (i2);
+}
+
+template<class T> 
+void 
+Plm_image_header::set_from_itk_image (const T& image)
+{
+    m_origin = itk_image_origin (image);
+    m_spacing = image->GetSpacing ();
+    m_region = itk_image_region (image);
+    m_direction = image->GetDirection ();
+}
+
+template<class T> 
+void 
+Plm_image_header::set_from_itk_image (const T* image)
+{
+    m_origin = itk_image_origin (image);
+    m_spacing = image->GetSpacing ();
+    m_region = itk_image_region (image);
+    m_direction = image->GetDirection ();
+}
+
+const OriginType& 
+Plm_image_header::GetOrigin () const
+{
+    return m_origin;
+}
+
+const SpacingType& 
+Plm_image_header::GetSpacing () const
+{
+    return m_spacing;
+}
+
+const RegionType& 
+Plm_image_header::GetRegion () const 
+{
+    return m_region;
+}
+
+const DirectionType& 
+Plm_image_header::GetDirection () const 
+{
+    return m_direction;
+}
+
+const SizeType& 
+Plm_image_header::GetSize (void) const
+{
+    return m_region.GetSize ();
+}
+
+void
+Plm_image_header::get_volume_header (Volume_header *vh) const
+{
+    this->get_origin (vh->get_origin());
+    this->get_dim (vh->get_dim());
+    this->get_spacing (vh->get_spacing());
+    this->get_direction_cosines (vh->get_direction_cosines());
+}
+
+void 
+Plm_image_header::get_origin (float origin[3]) const
+{
+    for (unsigned int d = 0; d < 3; d++) {
+	origin[d] = m_origin[d];
+    }
+}
+
+void 
+Plm_image_header::get_spacing (float spacing[3]) const
+{
+    for (unsigned int d = 0; d < 3; d++) {
+	spacing[d] = m_spacing[d];
+    }
+}
+
+void 
+Plm_image_header::get_dim (plm_long dim[3]) const
+{
+    RegionType::SizeType itk_size = m_region.GetSize ();
+    for (unsigned int d = 0; d < 3; d++) {
+	dim[d] = itk_size[d];
+    }
+}
+
+void 
+Plm_image_header::get_direction_cosines (float direction_cosines[9]) const
+{
+    dc_from_itk_direction (direction_cosines, &m_direction);
+}
+
+/* -----------------------------------------------------------------------
+   Algorithms
+   ----------------------------------------------------------------------- */
+/* static */ void 
+Plm_image_header::clone (Plm_image_header *dest, const Plm_image_header *src)
+{
+    dest->m_origin = src->m_origin;
+    dest->m_spacing = src->m_spacing;
+    dest->m_region = src->m_region;
+    dest->m_direction = src->m_direction;
+}
+
 void 
 Plm_image_header::expand_to_contain (
     const FloatPoint3DType& position)
@@ -219,21 +473,22 @@ Plm_image_header::expand_to_contain (
     this->get_spacing (spacing);
     compute_direction_matrices (step, proj, dc, spacing);
 
-    ImageRegionType::SizeType itk_size = m_region.GetSize();
+    RegionType::SizeType itk_size = m_region.GetSize();
 
     /* Expand the volume to contain the point */
     for (int d1 = 0; d1 < 3; d1++) {
         if (idx[d1] < 0) {
             float extra = (float) floor ((double) idx[d1]);
             for (int d2 = 0; d2 < 3; d2++) {
-                m_origin += extra * step[d1*3+d2];
+                m_origin[d2] += extra * step[d2*3+d1];
             }
-            itk_size[d1] += -extra;
+            itk_size[d1] += (int) -extra;
         }
-        else if (idx[d1] > itk_size[d1]) {
+        else if (idx[d1] > itk_size[d1] - 1) {
             itk_size[d1] = (int) floor ((double) idx[d1]) + 1;
         }
     }
+    m_region.SetSize (itk_size);
 }
 
 void 
@@ -297,73 +552,33 @@ Plm_image_header::set_geometry_to_contain (
 }
 
 void
-Plm_image_header::get_volume_header (Volume_header *vh) const
-{
-    this->get_origin (vh->get_origin());
-    this->get_dim (vh->get_dim());
-    this->get_spacing (vh->get_spacing());
-    this->get_direction_cosines (vh->get_direction_cosines());
-}
-
-void 
-Plm_image_header::get_origin (float origin[3]) const
-{
-    for (unsigned int d = 0; d < 3; d++) {
-	origin[d] = m_origin[d];
-    }
-}
-
-void 
-Plm_image_header::get_spacing (float spacing[3]) const
-{
-    for (unsigned int d = 0; d < 3; d++) {
-	spacing[d] = m_spacing[d];
-    }
-}
-
-void 
-Plm_image_header::get_dim (plm_long dim[3]) const
-{
-    ImageRegionType::SizeType itk_size = m_region.GetSize ();
-    for (unsigned int d = 0; d < 3; d++) {
-	dim[d] = itk_size[d];
-    }
-}
-
-void 
-Plm_image_header::get_direction_cosines (float direction_cosines[9]) const
-{
-    dc_from_itk_direction (direction_cosines, &m_direction);
-}
-
-void
 Plm_image_header::print (void) const
 {
-    ImageRegionType::SizeType itk_size;
+    RegionType::SizeType itk_size;
     itk_size = m_region.GetSize ();
     float dc[9];
     this->get_direction_cosines (dc);
 
-    printf ("Origin =");
+    lprintf ("Origin =");
     for (unsigned int d = 0; d < 3; d++) {
-	printf (" %g", m_origin[d]);
+	lprintf (" %g", m_origin[d]);
     }
-    printf ("\nSize =");
+    lprintf ("\nSize =");
     for (unsigned int d = 0; d < 3; d++) {
-	printf (" %lu", itk_size[d]);
+	lprintf (" %lu", itk_size[d]);
     }
-    printf ("\nSpacing =");
+    lprintf ("\nSpacing =");
     for (unsigned int d = 0; d < 3; d++) {
-	printf (" %g", m_spacing[d]);
+	lprintf (" %g", m_spacing[d]);
     }
-    printf ("\nDirection =");
+    lprintf ("\nDirection =");
     for (unsigned int d1 = 0; d1 < 3; d1++) {
 	for (unsigned int d2 = 0; d2 < 3; d2++) {
-	    printf (" %g", dc[d1*3+d2]);
+	    lprintf (" %g", dc[d1*3+d2]);
 	}
     }
 
-    printf ("\n");
+    lprintf ("\n");
 }
 
 FloatPoint3DType
@@ -381,8 +596,10 @@ Plm_image_header::get_index (const FloatPoint3DType& pos) const
     for (int d1 = 0; d1 < 3; d1++) {
         tmp[d1] = pos[d1] - m_origin[d1];
         idx[d1] = 0;
+    }
+    for (int d1 = 0; d1 < 3; d1++) {
         for (int d2 = 0; d2 < 3; d2++) {
-            idx[d1] += pos[d2] * proj[d1*3+d2];
+            idx[d1] += tmp[d2] * proj[d1*3+d2];
         }
     }
 
@@ -395,9 +612,11 @@ Plm_image_header::get_position (const float index[3]) const
     FloatPoint3DType pos;
 
     for (int d = 0; d < 3; d++) {
-        pos[d] = 0.f;
+        pos[d] = m_origin[d];
+    }
+    for (int d = 0; d < 3; d++) {
         for (int dc = 0; dc < 3; dc++) {
-            pos[d] += m_spacing[d] * index[dc] * m_direction[dc][d];
+            pos[dc] += m_spacing[d] * index[d] * m_direction[d][dc];
         }
     }
     return pos;
@@ -409,14 +628,14 @@ Plm_image_header::get_image_center (float center[3]) const
     int d;
     for (d = 0; d < 3; d++) {
 	center[d] = this->m_origin[d] 
-	    + this->m_spacing[d] * (this->Size(d) - 1) / 2;
+	    + this->m_spacing[d] * (this->dim(d) - 1) / 2;
     }
 }
 
 plm_long
 Plm_image_header::get_num_voxels (void) const
 {
-    return this->Size(0) * this->Size(1) * this->Size(2);
+    return this->dim(0) * this->dim(1) * this->dim(2);
 }
 
 void 
@@ -424,7 +643,7 @@ Plm_image_header::get_image_extent (float extent[3]) const
 {
     int d;
     for (d = 0; d < 3; d++) {
-	extent[d] = this->m_spacing[d] * (this->Size(d) - 1);
+	extent[d] = this->m_spacing[d] * (this->dim(d) - 1);
     }
 }
 
@@ -441,7 +660,7 @@ Plm_image_header::compare (Plm_image_header *pli1, Plm_image_header *pli2,
         if (fabs (pli1->m_spacing[d] - pli2->m_spacing[d]) > threshold) {
             return false;
         }
-        if (pli1->Size(d) != pli2->Size(d)) {
+        if (pli1->dim(d) != pli2->dim(d)) {
             return false;
         }
     }
@@ -450,3 +669,37 @@ Plm_image_header::compare (Plm_image_header *pli1, Plm_image_header *pli2,
 
     return true;
 }
+
+/* Explicit instantiations */
+template PLMBASE_API Plm_image_header::Plm_image_header (CharImageType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (UCharImageType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (ShortImageType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (UShortImageType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (Int32ImageType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (UInt32ImageType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (FloatImageType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (DoubleImageType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (DeformationFieldType::Pointer image);
+template PLMBASE_API Plm_image_header::Plm_image_header (UCharVecImageType::Pointer image);
+
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const CharImageType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const UCharImageType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const ShortImageType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const UShortImageType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const Int32ImageType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const UInt32ImageType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const FloatImageType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const DoubleImageType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const DeformationFieldType::Pointer& image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const UCharVecImageType::Pointer& image);
+
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const CharImageType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const UCharImageType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const ShortImageType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const UShortImageType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const Int32ImageType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const UInt32ImageType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const FloatImageType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const DoubleImageType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const DeformationFieldType* image);
+template PLMBASE_API void Plm_image_header::set_from_itk_image (const UCharVecImageType* image);
diff --git a/src/plastimatch/base/plm_image_header.h b/src/plastimatch/base/plm_image_header.h
index 293b290..871d562 100644
--- a/src/plastimatch/base/plm_image_header.h
+++ b/src/plastimatch/base/plm_image_header.h
@@ -7,10 +7,12 @@
 #include "plmbase_config.h"
 #include "direction_cosines.h"
 #include "itk_image.h"
+#include "itk_image_region.h"
+#include "itk_image_origin.h"
 #include "plm_image.h"
 
 class Bspline_xform;
-class Plm_image_header;
+class Plm_image_header_private;
 class Volume;
 class Volume_header;
 
@@ -21,65 +23,40 @@ class Volume_header;
  */
 class PLMBASE_API Plm_image_header {
 public:
+    Plm_image_header_private *d_ptr;
+
+private:
     OriginType m_origin;
     SpacingType m_spacing;
-    ImageRegionType m_region;
+    RegionType m_region;
     DirectionType m_direction;
 
 public:
-    Plm_image_header () {}
-    Plm_image_header (
-        plm_long dim[3], float origin[3], float spacing[3])
-    {
-        this->set_from_gpuit (dim, origin, spacing, 0);
-    }
-    Plm_image_header (
-        plm_long dim[3], float origin[3], float spacing[3],
-        float direction_cosines[9])
-    {
-        this->set_from_gpuit (dim, origin, spacing, direction_cosines);
-    }
-    Plm_image_header (Plm_image *pli) {
-        this->set_from_plm_image (pli);
-    }
-    Plm_image_header (const Plm_image& pli) {
-        this->set_from_plm_image (pli);
-    }
-    Plm_image_header (const Plm_image::Pointer& pli) {
-        this->set_from_plm_image (pli);
-    }
-    Plm_image_header (const Volume_header& vh) {
-        this->set (vh);
-    }
-    Plm_image_header (const Volume& vol) {
-        this->set (vol);
-    }
-    Plm_image_header (const Volume* vol) {
-        this->set (vol);
-    }
-    Plm_image_header (Volume* vol) {
-        this->set (vol);
-    }
-    template<class T> 
-        Plm_image_header (T image) {
-        this->set_from_itk_image (image);
-    }
+    Plm_image_header ();
+    Plm_image_header (plm_long dim[3], float origin[3], float spacing[3]);
+    Plm_image_header (plm_long dim[3], float origin[3], float spacing[3],
+        float direction_cosines[9]);
+    Plm_image_header (const RegionType& region, const OriginType& origin,
+        const SpacingType& spacing, const DirectionType& direction);
+    Plm_image_header (Plm_image *pli);
+    Plm_image_header (const Plm_image& pli);
+    Plm_image_header (const Plm_image::Pointer& pli);
+    Plm_image_header (const Volume_header& vh);
+    Plm_image_header (const Volume& vol);
+    Plm_image_header (const Volume* vol);
+    Plm_image_header (Volume* vol);
+    template<class T> Plm_image_header (T image);
+    Plm_image_header (const Plm_image_header&);
+    ~Plm_image_header ();
 
 public:
-    int Size (int d) const { return m_region.GetSize()[d]; }
-    const SizeType& GetSize (void) const { return m_region.GetSize (); }
-public:
-    /*! \brief Return true if the two headers are the same. 
-      Tolerance on origin and spacing can be specified 
-      using the threshold parameter */
-    static bool compare (Plm_image_header *pli1, Plm_image_header *pli2, 
-        float threshold = 1e-5);
-
-    int dim (int d) const { return m_region.GetSize()[d]; }
-    float origin (int d) const { return m_origin[d]; }
-    float spacing (int d) const { return m_spacing[d]; }
+    const Plm_image_header& operator= (const Plm_image_header&);
 
 public:
+    /* Getters and Setters */
+    int dim (int d) const;
+    float origin (int d) const;
+    float spacing (int d) const;
     void set_dim (const plm_long dim[3]);
     void set_origin (const float origin[3]);
     void set_spacing (const float spacing[3]);
@@ -111,6 +88,23 @@ public:
     void set (const Volume_header& vh);
     void set (const Volume& vol);
     void set (const Volume* vol);
+    void set (const RegionType& region, const OriginType& origin,
+        const SpacingType& spacing, const DirectionType& direction);
+    template<class T> void set_from_itk_image (const T& image);
+    template<class T> void set_from_itk_image (const T* image);
+    const OriginType& GetOrigin () const;
+    const SpacingType& GetSpacing () const;
+    const RegionType& GetRegion () const;
+    const DirectionType& GetDirection () const;
+    const SizeType& GetSize (void) const;
+    void get_volume_header (Volume_header *vh) const;
+    void get_origin (float origin[3]) const;
+    void get_spacing (float spacing[3]) const;
+    void get_dim (plm_long dim[3]) const;
+    void get_direction_cosines (float direction_cosines[9]) const;
+
+    /* Algorithms */
+    static void clone (Plm_image_header *dest, const Plm_image_header *src);
 
     /*! \brief Expand existing geometry to contain the 
       specified point.  Only origin and dimensions can change, 
@@ -124,49 +118,23 @@ public:
         const Plm_image_header& reference_pih,
         const Plm_image_header& compare_pih);
 
-    template<class T> 
-        void set_from_itk_image (T image) {
-        m_origin = image->GetOrigin ();
-        m_spacing = image->GetSpacing ();
-        m_region = image->GetLargestPossibleRegion ();
-        m_direction = image->GetDirection ();
-    }
-    static void clone (Plm_image_header *dest, const Plm_image_header *src) {
-        dest->m_origin = src->m_origin;
-        dest->m_spacing = src->m_spacing;
-        dest->m_region = src->m_region;
-        dest->m_direction = src->m_direction;
-    }
-
-    void get_volume_header (Volume_header *vh) const;
-    void get_origin (float origin[3]) const;
-    void get_spacing (float spacing[3]) const;
-    void get_dim (plm_long dim[3]) const;
-    void get_direction_cosines (float direction_cosines[9]) const;
-
-    const OriginType& GetOrigin () const {
-        return m_origin;
-    }
-    const SpacingType& GetSpacing () const {
-        return m_spacing;
-    }
-    const ImageRegionType& GetLargestPossibleRegion () const {
-        return m_region;
-    }
-    const DirectionType& GetDirection () const {
-        return m_direction;
-    }
-
     void print (void) const;
 
+    /*! \brief Return true if the two headers are the same. 
+      Tolerance on origin and spacing can be specified 
+      using the threshold parameter */
+    static bool compare (Plm_image_header *pli1, Plm_image_header *pli2, 
+        float threshold = 1e-5);
+
     FloatPoint3DType get_index (const FloatPoint3DType& pos) const;
     FloatPoint3DType get_position (const float index[3]) const;
     void get_image_center (float center[3]) const;
 
     /*! \brief Get the number of voxels in the image */
     plm_long get_num_voxels () const;
-    /*! \brief Get the physical size of the image, from first voxel center
-      to last voxel center.  Size is zero if only one voxel. */
+    /*! \brief Get the physical extent (size) of the image, from 
+      first voxel center to last voxel center.  Extent is zero 
+      if only one voxel. */
     void get_image_extent (float extent[3]) const;
 };
 
@@ -179,6 +147,4 @@ direction_cosines_from_itk (
     DirectionType* itk_direction
 );
 
-
-
 #endif
diff --git a/src/plastimatch/base/plm_image_p.h b/src/plastimatch/base/plm_image_p.h
index bf611b3..8246cfe 100644
--- a/src/plastimatch/base/plm_image_p.h
+++ b/src/plastimatch/base/plm_image_p.h
@@ -11,6 +11,7 @@ class Plm_image_private {
 public:
     Metadata::Pointer m_meta;
     Volume::Pointer m_vol;
+    std::list<Volume::Pointer> m_vol_list;
 };
 
 #endif
diff --git a/src/plastimatch/base/plm_image_type.cxx b/src/plastimatch/base/plm_image_type.cxx
index 6ed3200..b815048 100755
--- a/src/plastimatch/base/plm_image_type.cxx
+++ b/src/plastimatch/base/plm_image_type.cxx
@@ -60,30 +60,32 @@ plm_image_type_string (Plm_image_type type)
     switch (type) {
     case PLM_IMG_TYPE_UNDEFINED:
         return "PLM_IMG_TYPE_UNDEFINED";
-    case PLM_IMG_TYPE_ITK_CHAR:
-        return "PLM_IMG_TYPE_ITK_CHAR";
     case PLM_IMG_TYPE_ITK_UCHAR:
         return "PLM_IMG_TYPE_ITK_UCHAR";
-    case PLM_IMG_TYPE_ITK_SHORT:
-        return "PLM_IMG_TYPE_ITK_SHORT";
+    case PLM_IMG_TYPE_ITK_CHAR:
+        return "PLM_IMG_TYPE_ITK_CHAR";
     case PLM_IMG_TYPE_ITK_USHORT:
         return "PLM_IMG_TYPE_ITK_USHORT";
-    case PLM_IMG_TYPE_ITK_LONG:
-        return "PLM_IMG_TYPE_ITK_LONG";
+    case PLM_IMG_TYPE_ITK_SHORT:
+        return "PLM_IMG_TYPE_ITK_SHORT";
     case PLM_IMG_TYPE_ITK_ULONG:
         return "PLM_IMG_TYPE_ITK_ULONG";
+    case PLM_IMG_TYPE_ITK_LONG:
+        return "PLM_IMG_TYPE_ITK_LONG";
     case PLM_IMG_TYPE_ITK_FLOAT:
         return "PLM_IMG_TYPE_ITK_FLOAT";
     case PLM_IMG_TYPE_ITK_DOUBLE:
         return "PLM_IMG_TYPE_ITK_DOUBLE";
     case PLM_IMG_TYPE_ITK_FLOAT_FIELD:
         return "PLM_IMG_TYPE_ITK_FLOAT_FIELD";
+    case PLM_IMG_TYPE_ITK_UCHAR_VEC:
+        return "PLM_IMG_TYPE_ITK_UCHAR_VEC";
     case PLM_IMG_TYPE_GPUIT_UCHAR:
         return "PLM_IMG_TYPE_GPUIT_UCHAR";
-    case PLM_IMG_TYPE_GPUIT_SHORT:
-        return "PLM_IMG_TYPE_GPUIT_SHORT";
     case PLM_IMG_TYPE_GPUIT_UINT16:
         return "PLM_IMG_TYPE_GPUIT_UINT16";
+    case PLM_IMG_TYPE_GPUIT_SHORT:
+        return "PLM_IMG_TYPE_GPUIT_SHORT";
     case PLM_IMG_TYPE_GPUIT_UINT32:
         return "PLM_IMG_TYPE_GPUIT_UINT32";
     case PLM_IMG_TYPE_GPUIT_INT32:
@@ -92,8 +94,8 @@ plm_image_type_string (Plm_image_type type)
         return "PLM_IMG_TYPE_GPUIT_FLOAT";
     case PLM_IMG_TYPE_GPUIT_FLOAT_FIELD:
         return "PLM_IMG_TYPE_GPUIT_FLOAT_FIELD";
-    case PLM_IMG_TYPE_ITK_UCHAR_VEC:
-        return "PLM_IMG_TYPE_ITK_UCHAR_VEC";
+    case PLM_IMG_TYPE_GPUIT_LIST:
+        return "PLM_IMG_TYPE_GPUIT_LIST";
     case PLM_IMG_TYPE_GPUIT_UCHAR_VEC:
         return "PLM_IMG_TYPE_GPUIT_UCHAR_VEC";
     default:
@@ -107,30 +109,32 @@ plm_image_type_string_simple (Plm_image_type type)
     switch (type) {
     case PLM_IMG_TYPE_UNDEFINED:
         return "undefined";
-    case PLM_IMG_TYPE_ITK_CHAR:
-        return "char";
     case PLM_IMG_TYPE_ITK_UCHAR:
         return "unsigned char";
-    case PLM_IMG_TYPE_ITK_SHORT:
-        return "short";
+    case PLM_IMG_TYPE_ITK_CHAR:
+        return "char";
     case PLM_IMG_TYPE_ITK_USHORT:
         return "unsigned short";
-    case PLM_IMG_TYPE_ITK_LONG:
-        return "long";
+    case PLM_IMG_TYPE_ITK_SHORT:
+        return "short";
     case PLM_IMG_TYPE_ITK_ULONG:
         return "unsigned long";
+    case PLM_IMG_TYPE_ITK_LONG:
+        return "long";
     case PLM_IMG_TYPE_ITK_FLOAT:
         return "float";
     case PLM_IMG_TYPE_ITK_DOUBLE:
         return "double";
     case PLM_IMG_TYPE_ITK_FLOAT_FIELD:
         return "float";
+    case PLM_IMG_TYPE_ITK_UCHAR_VEC:
+        return "unsigned char";
     case PLM_IMG_TYPE_GPUIT_UCHAR:
         return "unsigned char";
-    case PLM_IMG_TYPE_GPUIT_SHORT:
-        return "short";
     case PLM_IMG_TYPE_GPUIT_UINT16:
         return "unsigned short";
+    case PLM_IMG_TYPE_GPUIT_SHORT:
+        return "short";
     case PLM_IMG_TYPE_GPUIT_UINT32:
         return "unsigned long";
     case PLM_IMG_TYPE_GPUIT_INT32:
@@ -139,8 +143,8 @@ plm_image_type_string_simple (Plm_image_type type)
         return "float";
     case PLM_IMG_TYPE_GPUIT_FLOAT_FIELD:
         return "float";
-    case PLM_IMG_TYPE_ITK_UCHAR_VEC:
-        return "unsigned char";
+    case PLM_IMG_TYPE_GPUIT_LIST:
+        return "list (unknown)";
     case PLM_IMG_TYPE_GPUIT_UCHAR_VEC:
         return "unsigned char";
     default:
diff --git a/src/plastimatch/base/plm_image_type.h b/src/plastimatch/base/plm_image_type.h
index e3b51d5..d681347 100644
--- a/src/plastimatch/base/plm_image_type.h
+++ b/src/plastimatch/base/plm_image_type.h
@@ -21,12 +21,13 @@ enum Plm_image_type {
     PLM_IMG_TYPE_ITK_FLOAT_FIELD, 
     PLM_IMG_TYPE_ITK_UCHAR_VEC,
     PLM_IMG_TYPE_GPUIT_UCHAR, 
-    PLM_IMG_TYPE_GPUIT_SHORT, 
     PLM_IMG_TYPE_GPUIT_UINT16, 
+    PLM_IMG_TYPE_GPUIT_SHORT, 
     PLM_IMG_TYPE_GPUIT_UINT32,
     PLM_IMG_TYPE_GPUIT_INT32,
     PLM_IMG_TYPE_GPUIT_FLOAT, 
     PLM_IMG_TYPE_GPUIT_FLOAT_FIELD, 
+    PLM_IMG_TYPE_GPUIT_LIST,
     PLM_IMG_TYPE_GPUIT_UCHAR_VEC
 };
 
diff --git a/src/plastimatch/base/plm_image_p.h b/src/plastimatch/base/plm_itk.h
similarity index 55%
copy from src/plastimatch/base/plm_image_p.h
copy to src/plastimatch/base/plm_itk.h
index bf611b3..56c45c8 100644
--- a/src/plastimatch/base/plm_image_p.h
+++ b/src/plastimatch/base/plm_itk.h
@@ -1,16 +1,16 @@
 /* -----------------------------------------------------------------------
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
-#ifndef _plm_image_p_h_
-#define _plm_image_p_h_
+#ifndef __plm_itk_h_
+#define __plm_itk_h_
 
-#include "plmbase_config.h"
-#include "volume.h"
+/* ITK 3.20 is missing this */
+#if defined __cplusplus
+#include <stddef.h>
+#endif
 
-class Plm_image_private {
-public:
-    Metadata::Pointer m_meta;
-    Volume::Pointer m_vol;
-};
+#if (ITK_FOUND && !PLM_CUDA_COMPILE)
+#include "itkConfigure.h"
+#endif
 
 #endif
diff --git a/src/plastimatch/base/pointset.cxx b/src/plastimatch/base/pointset.cxx
index fd382fb..593058d 100644
--- a/src/plastimatch/base/pointset.cxx
+++ b/src/plastimatch/base/pointset.cxx
@@ -156,6 +156,16 @@ Pointset<T>::insert_ras (
 
 template<class T>
 void
+Pointset<T>::insert_ras (
+    const float *xyz
+)
+{
+    /* RAS to LPS adjustment */
+    this->point_list.push_back (T ("", -xyz[0], -xyz[1], xyz[2]));
+}
+
+template<class T>
+void
 Pointset<T>::insert_lps (
     const std::string& label,
     float x,
diff --git a/src/plastimatch/base/pointset.h b/src/plastimatch/base/pointset.h
index 6f62750..ddcdd5d 100644
--- a/src/plastimatch/base/pointset.h
+++ b/src/plastimatch/base/pointset.h
@@ -81,6 +81,7 @@ public:
     void insert_lps (const float* xyz);
     void insert_lps (const std::string& label, const float* xyz);
     void insert_ras (const std::string& label, float x, float y, float z);
+    void insert_ras (const float* xyz);
 
     /* Return reference to a point */
     const T& point (int idx) const {
@@ -98,6 +99,9 @@ public:
     void truncate (size_t new_length);
 
     void debug () const;
+private:
+    Pointset (const Pointset&);
+    Pointset& operator= (const Pointset&);
 };
 
 typedef Pointset<Labeled_point> Labeled_pointset;
diff --git a/src/plastimatch/base/proj_image.cxx b/src/plastimatch/base/proj_image.cxx
index 6660de3..f076da8 100644
--- a/src/plastimatch/base/proj_image.cxx
+++ b/src/plastimatch/base/proj_image.cxx
@@ -65,10 +65,12 @@ void
 Proj_image::clear ()
 {
     if (this->pmat) {
-        free (this->pmat);
+        delete pmat;
+        this->pmat = 0;
     }
     if (this->img) {
         free (this->img);
+        this->img = 0;
     }
 }
 
@@ -399,38 +401,38 @@ proj_image_create_img (Proj_image *proj, int dim[2])
 }
 
 void
-proj_image_debug_header (Proj_image *proj)
+Proj_image::debug_header ()
 {
     int i;
-    printf ("Image center: %g %g\n", proj->pmat->ic[0], proj->pmat->ic[1]);
+    printf ("Image center: %g %g\n", this->pmat->ic[0], this->pmat->ic[1]);
     printf ("Projection matrix: ");
     for (i = 0; i < 12; i++) {
-        printf ("%g ", proj->pmat->matrix[i]);
+        printf ("%g ", this->pmat->matrix[i]);
     }
     printf ("\n");
 }
 
 void
-proj_image_stats (Proj_image *proj)
+Proj_image::stats ()
 {
     int i, num;
     float min_val, max_val;
     double sum = 0.0;
 
-    if (!proj) {
+    if (!this) {
         printf ("No image.\n");
         return;
     }
 
-    num = proj->dim[0]*proj->dim[1];
-    if (!proj->img || num == 0) {
+    num = this->dim[0]*this->dim[1];
+    if (!this->img || num == 0) {
         printf ("No image.\n");
         return;
     }
     
-    min_val = max_val = proj->img[0];
+    min_val = max_val = this->img[0];
     for (i = 0; i < num; i++) {
-        float v = proj->img[i];
+        float v = this->img[i];
         if (min_val > v) min_val = v;
         if (max_val < v) max_val = v;
         sum += v;
@@ -467,30 +469,29 @@ Proj_image::load (
 }
 
 void
-proj_image_save (
-    Proj_image *proj,
+Proj_image::save (
     const char *img_filename,
     const char *mat_filename
 )
 {
     if (img_filename) {
         if (extension_is (img_filename, ".pfm")) {
-            pfm_save (proj, img_filename);
+            pfm_save (this, img_filename);
         }
         else if (extension_is (img_filename, ".raw")) {
-            raw_save (proj, img_filename);
+            raw_save (this, img_filename);
         }
         else if (extension_is (img_filename, ".pgm")) {
-            pgm_save (proj, img_filename);
+            pgm_save (this, img_filename);
         }
 #if defined (commentout)
         else if (extension_is (img_filename, "mha.")) {
-            mha_save (proj, img_filename);
+            mha_save (this, img_filename);
         }
 #endif
     }
     if (mat_filename) {
-        proj_matrix_save (proj->pmat, mat_filename);
+        this->pmat->save (mat_filename);
     }
 }
 
diff --git a/src/plastimatch/base/proj_image.h b/src/plastimatch/base/proj_image.h
index 6317851..9b341e1 100644
--- a/src/plastimatch/base/proj_image.h
+++ b/src/plastimatch/base/proj_image.h
@@ -29,35 +29,18 @@ public:
     void clear ();
     bool have_image ();
     void init ();
+    void save (const char *img_filename, const char *mat_filename);
     void load (const std::string& img_filename, std::string mat_filename = "");
     void load_pfm (const char* img_filename, const char* mat_filename);
     void load_raw (const char* img_filename, const char* mat_filename);
     void load_hnd (const char* img_filename);
     void set_xy_offset (const double xy_offset[2]);
+
+    void debug_header ();
+    void stats ();
 };
 
-PLMBASE_C_API void proj_image_free (Proj_image* proj);
-PLMBASE_C_API Proj_image* proj_image_load (
-    const char* img_filename,
-    const char* mat_filename
-);
-PLMBASE_C_API void proj_image_save (
-    Proj_image *proj,
-    const char *img_filename,
-    const char *mat_filename
-);
-PLMBASE_C_API void proj_image_debug_header (Proj_image *proj);
 PLMBASE_C_API void proj_image_create_pmat (Proj_image *proj);
 PLMBASE_C_API void proj_image_create_img (Proj_image *proj, int dim[2]);
-PLMBASE_C_API void proj_image_stats (Proj_image *proj); 
-
-#if 0
-PLMBASE_C_API Proj_image* proj_image_create (void);
-PLMBASE_C_API Proj_image* proj_image_load_and_filter (
-    Fdk_parms* parms, 
-    const char* img_filename, 
-    const char* mat_filename
-);
-#endif
 
 #endif
diff --git a/src/plastimatch/base/proj_matrix.cxx b/src/plastimatch/base/proj_matrix.cxx
index c95b6f6..1cd1c17 100644
--- a/src/plastimatch/base/proj_matrix.cxx
+++ b/src/plastimatch/base/proj_matrix.cxx
@@ -9,6 +9,7 @@
 #include "file_util.h"
 #include "plm_math.h"
 #include "proj_matrix.h"
+#include "string_util.h"
 
 Proj_matrix::Proj_matrix ()
 {
@@ -22,7 +23,7 @@ Proj_matrix::Proj_matrix ()
 }
 
 Proj_matrix*
-proj_matrix_clone (Proj_matrix* pmat_in)
+Proj_matrix::clone ()
 {
     Proj_matrix *pmat;
     
@@ -30,11 +31,31 @@ proj_matrix_clone (Proj_matrix* pmat_in)
     if (!pmat) return 0;
 
     /* No dynamically allocated memory in proj_matrix */
-    memcpy (pmat, pmat_in, sizeof (Proj_matrix));
+    memcpy (pmat, this, sizeof (Proj_matrix));
 
     return pmat;
 }
 
+std::string
+Proj_matrix::get ()
+{
+    std::string s;
+    s = PLM_to_string (ic, 2);
+    s += " " + PLM_to_string (matrix, 12);
+    s += " " + PLM_to_string (sad);
+    s += " " + PLM_to_string (sid);
+    s += " " + PLM_to_string (cam, 3);
+    s += " " + PLM_to_string (nrm, 3);
+    s += " " + PLM_to_string (extrinsic, 16);
+    s += " " + PLM_to_string (intrinsic, 12);
+    return s;
+}
+
+void
+Proj_matrix::set (const std::string& s)
+{
+}
+
 static
 void
 proj_matrix_write (
@@ -86,23 +107,19 @@ proj_matrix_write (
 }
 
 void
-proj_matrix_debug (
-    Proj_matrix *pmat
-)
+Proj_matrix::debug ()
 {
-    proj_matrix_write (pmat, stdout);
+    proj_matrix_write (this, stdout);
 }
 
 void
-proj_matrix_save (
-    Proj_matrix *pmat,
+Proj_matrix::save (
     const char *fn
 )
 {
     FILE *fp;
 
     if (!fn) return;
-    if (!pmat) return;
 
     make_parent_directories (fn);
     fp = fopen (fn, "w");
@@ -111,14 +128,13 @@ proj_matrix_save (
 	exit (-1);
     }
 
-    proj_matrix_write (pmat, fp);
+    proj_matrix_write (this, fp);
 
     fclose (fp);
 }
 
 void
-proj_matrix_set (
-    Proj_matrix *pmat,
+Proj_matrix::set (
     const double* cam, 
     const double* tgt, 
     const double* vup, 
@@ -133,11 +149,11 @@ proj_matrix_set (
     double plt[3];       /* Panel left (toward first column) */
     double pup[3];       /* Panel up (toward top row) */
 
-    vec3_copy (pmat->cam, cam);
-    pmat->sid = sid;
-    pmat->sad = vec3_len (cam);
-    pmat->ic[0] = ic[0];
-    pmat->ic[1] = ic[1];
+    vec3_copy (this->cam, cam);
+    this->sid = sid;
+    this->sad = vec3_len (cam);
+    this->ic[0] = ic[0];
+    this->ic[1] = ic[1];
 
     /* Compute imager coordinate sys (nrm,pup,plt) 
        ---------------
@@ -162,51 +178,51 @@ proj_matrix_set (
 #endif
 
     /* Build extrinsic matrix - rotation part */
-    vec_zero (pmat->extrinsic, 16);
-    vec3_copy (&pmat->extrinsic[0], plt);
-    vec3_copy (&pmat->extrinsic[4], pup);
-    vec3_copy (&pmat->extrinsic[8], nrm);
-    vec3_invert (&pmat->extrinsic[0]);
-    vec3_invert (&pmat->extrinsic[4]);
-    vec3_invert (&pmat->extrinsic[8]);
-    m_idx (pmat->extrinsic,cols,3,3) = 1.0;
+    vec_zero (this->extrinsic, 16);
+    vec3_copy (&this->extrinsic[0], plt);
+    vec3_copy (&this->extrinsic[4], pup);
+    vec3_copy (&this->extrinsic[8], nrm);
+    vec3_invert (&this->extrinsic[0]);
+    vec3_invert (&this->extrinsic[4]);
+    vec3_invert (&this->extrinsic[8]);
+    m_idx (this->extrinsic,cols,3,3) = 1.0;
 
     /* Build extrinsic matrix - translation part */
-    pmat->extrinsic[3] = vec3_dot (plt, tgt);
-    pmat->extrinsic[7] = vec3_dot (pup, tgt);
-    pmat->extrinsic[11] = vec3_dot (nrm, tgt) + pmat->sad;
+    this->extrinsic[3] = vec3_dot (plt, tgt);
+    this->extrinsic[7] = vec3_dot (pup, tgt);
+    this->extrinsic[11] = vec3_dot (nrm, tgt) + this->sad;
 
 #if defined (commentout)
     printf ("EXTRINSIC\n%g %g %g %g\n%g %g %g %g\n"
 	"%g %g %g %g\n%g %g %g %g\n",
-	pmat->extrinsic[0], pmat->extrinsic[1], 
-	pmat->extrinsic[2], pmat->extrinsic[3],
-	pmat->extrinsic[4], pmat->extrinsic[5], 
-	pmat->extrinsic[6], pmat->extrinsic[7],
-	pmat->extrinsic[8], pmat->extrinsic[9], 
-	pmat->extrinsic[10], pmat->extrinsic[11],
-	pmat->extrinsic[12], pmat->extrinsic[13], 
-	pmat->extrinsic[14], pmat->extrinsic[15]);
+	this->extrinsic[0], this->extrinsic[1], 
+	this->extrinsic[2], this->extrinsic[3],
+	this->extrinsic[4], this->extrinsic[5], 
+	this->extrinsic[6], this->extrinsic[7],
+	this->extrinsic[8], this->extrinsic[9], 
+	this->extrinsic[10], this->extrinsic[11],
+	this->extrinsic[12], this->extrinsic[13], 
+	this->extrinsic[14], this->extrinsic[15]);
 #endif
 
     /* Build intrinsic matrix */
-    vec_zero (pmat->intrinsic, 12);
-    m_idx (pmat->intrinsic,cols,0,0) = 1 / ps[0];
-    m_idx (pmat->intrinsic,cols,1,1) = 1 / ps[1];
-    m_idx (pmat->intrinsic,cols,2,2) = 1 / sid;
+    vec_zero (this->intrinsic, 12);
+    m_idx (this->intrinsic,cols,0,0) = 1 / ps[0];
+    m_idx (this->intrinsic,cols,1,1) = 1 / ps[1];
+    m_idx (this->intrinsic,cols,2,2) = 1 / sid;
 
 #if defined (commentout)
     printf ("INTRINSIC\n%g %g %g %g\n%g %g %g %g\n%g %g %g %g\n",
-	pmat->intrinsic[0], pmat->intrinsic[1], 
-	pmat->intrinsic[2], pmat->intrinsic[3],
-	pmat->intrinsic[4], pmat->intrinsic[5], 
-	pmat->intrinsic[6], pmat->intrinsic[7],
-	pmat->intrinsic[8], pmat->intrinsic[9], 
-	pmat->intrinsic[10], pmat->intrinsic[11]);
+	this->intrinsic[0], this->intrinsic[1], 
+	this->intrinsic[2], this->intrinsic[3],
+	this->intrinsic[4], this->intrinsic[5], 
+	this->intrinsic[6], this->intrinsic[7],
+	this->intrinsic[8], this->intrinsic[9], 
+	this->intrinsic[10], this->intrinsic[11]);
 #endif
 
     /* Build projection matrix */
-    mat_mult_mat (pmat->matrix, pmat->intrinsic,3,4, pmat->extrinsic,4,4);
+    mat_mult_mat (this->matrix, this->intrinsic,3,4, this->extrinsic,4,4);
 }
 
 void
diff --git a/src/plastimatch/base/proj_matrix.h b/src/plastimatch/base/proj_matrix.h
index 8ac9fb9..2ab757f 100644
--- a/src/plastimatch/base/proj_matrix.h
+++ b/src/plastimatch/base/proj_matrix.h
@@ -5,6 +5,7 @@
 #define _proj_matrix_h_
 
 #include "plmbase_config.h"
+#include <string>
 
 class PLMBASE_API Proj_matrix {
 public:
@@ -22,23 +23,25 @@ public:
     double intrinsic[12];
 
 public:
+    std::string get ();
     void get_nrm (double nrm[3]);
     void get_pdn (double pdn[3]);
     void get_prt (double prt[3]);
-};
 
-PLMBASE_API Proj_matrix* proj_matrix_clone (Proj_matrix* pmat_in);
-PLMBASE_API void proj_matrix_set (
-    Proj_matrix *pmat,
-    const double* cam, 
-    const double* tgt, 
-    const double* vup, 
-    double sid, 
-    const double* ic, 
-    const double* ps, 
-    const int* ires
-);
-PLMBASE_API void proj_matrix_debug (Proj_matrix *pmat);
-PLMBASE_API void proj_matrix_save (Proj_matrix *pmat, const char *fn);
+    void set (
+        const double* cam, 
+        const double* tgt, 
+        const double* vup, 
+        double sid, 
+        const double* ic, 
+        const double* ps, 
+        const int* ires
+    );
+    void set (const std::string& s);
+
+    void save (const char *fn);
+    void debug ();
+    Proj_matrix* clone ();
+};
 
 #endif
diff --git a/src/plastimatch/base/proj_volume.cxx b/src/plastimatch/base/proj_volume.cxx
index f54eaf2..9dd611d 100644
--- a/src/plastimatch/base/proj_volume.cxx
+++ b/src/plastimatch/base/proj_volume.cxx
@@ -2,16 +2,21 @@
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
 #include "plm_config.h"
-
+#include <fstream>
+#include "file_util.h"
+#include "logfile.h"
+#include "path_util.h"
 #include "plm_image.h"
+#include "print_and_exit.h"
 #include "proj_matrix.h"
 #include "proj_volume.h"
+#include "string_util.h"
 #include "volume.h"
 
 class Proj_volume_private {
 public:
     Proj_volume_private () {
-        vol = new Volume;
+        vol = Volume::New();
         pmat = new Proj_matrix;
 
         num_steps = 0;
@@ -30,11 +35,10 @@ public:
         }
     }
     ~Proj_volume_private () {
-        delete vol;
         delete pmat;
     }
 public:
-    Volume *vol;
+    Volume::Pointer vol;
     Proj_matrix *pmat;
     int num_steps;
     double step_length;
@@ -97,8 +101,7 @@ Proj_volume::set_geometry (
     d_ptr->step_length = step_length;
 
     /* build projection matrix */
-    proj_matrix_set (
-        d_ptr->pmat,
+    d_ptr->pmat->set (
         src, 
         iso, 
         vup, 
@@ -168,10 +171,10 @@ Proj_volume::allocate ()
     plm_long dim[3] = { d_ptr->image_dim[0], d_ptr->image_dim[1], 
                         d_ptr->num_steps };
     float origin[3] = { 0, 0, 0 };
-	float spacing[3] = { 1,1,1};
+    float spacing[3] = { 1, 1, 1 };
     float direction_cosines[9] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
 
-	printf("%lg %lg %lg \n", (float) dim[0], (float) dim[1], (float) dim[2]);
+    printf("%lg %lg %lg \n", (float) dim[0], (float) dim[1], (float) dim[2]);
     d_ptr->vol->create (dim, origin, spacing,
         direction_cosines, PT_FLOAT, 1);
 }
@@ -246,17 +249,214 @@ Proj_volume::get_ul_room ()
 Volume*
 Proj_volume::get_vol ()
 {
-    return d_ptr->vol;
+    return d_ptr->vol.get();
 }
 
 const Volume*
 Proj_volume::get_vol () const
 {
-    return d_ptr->vol;
+    return d_ptr->vol.get();
 }
 
 void
-Proj_volume::save (const char *filename)
+Proj_volume::save_img (const char *filename)
 {
     Plm_image(d_ptr->vol).save_image(filename);
 }
+
+void
+Proj_volume::save_img (const std::string& filename)
+{
+    this->save_img (filename.c_str());
+}
+
+void
+Proj_volume::save_header (const char *filename)
+{
+    FILE *fp = plm_fopen (filename, "wb");
+    if (!fp) {
+        print_and_exit ("Error opening file %s for write\n", filename);
+    }
+
+    std::string s = d_ptr->pmat->get ();
+    fprintf (fp, "num_steps=%d\n", d_ptr->num_steps);
+    fprintf (fp, "step_length=%g\n", d_ptr->step_length);
+    fprintf (fp, "image_dim=%d %d\n", 
+        d_ptr->image_dim[0], d_ptr->image_dim[1]);
+    fprintf (fp, "image_spacing=%g %g\n", 
+        d_ptr->image_spacing[0], d_ptr->image_spacing[1]);
+    fprintf (fp, "clipping_dist=%g %g\n", 
+        d_ptr->clipping_dist[0], d_ptr->clipping_dist[1]);
+    fprintf (fp, "nrm=%g %g %g\n", 
+        d_ptr->nrm[0], d_ptr->nrm[1], d_ptr->nrm[2]);
+    fprintf (fp, "src=%g %g %g\n", 
+        d_ptr->src[0], d_ptr->src[1], d_ptr->src[2]);
+    fprintf (fp, "iso=%g %g %g\n", 
+        d_ptr->iso[0], d_ptr->iso[1], d_ptr->iso[2]);
+    fprintf (fp, "ul_room=%g %g %g\n", 
+        d_ptr->ul_room[0], d_ptr->ul_room[1], d_ptr->ul_room[2]);
+    fprintf (fp, "incr_r=%g %g %g\n", 
+        d_ptr->incr_r[0], d_ptr->incr_r[1], d_ptr->incr_r[2]);
+    fprintf (fp, "incr_c=%g %g %g\n", 
+        d_ptr->incr_c[0], d_ptr->incr_c[1], d_ptr->incr_c[2]);
+    fprintf (fp, "pmat=%s\n", s.c_str());
+    fclose (fp);
+}
+
+void
+Proj_volume::save_header (const std::string& filename)
+{
+    this->save_header (filename.c_str());
+}
+
+void
+Proj_volume::save_projv (const char *filename)
+{
+    std::string fn_base = strip_extension_if (filename, "projv");
+    std::string proj_vol_hdr_fn = fn_base + ".projv";
+    this->save_header (proj_vol_hdr_fn);
+    std::string proj_vol_img_fn = fn_base + ".nrrd";
+    this->save_img (proj_vol_img_fn);
+}
+
+void
+Proj_volume::save_projv (const std::string& filename)
+{
+    this->save_projv (filename.c_str());
+}
+
+void
+Proj_volume::load_img (const char *filename)
+{
+    Plm_image::Pointer plm_image = Plm_image::New (filename);
+    d_ptr->vol = plm_image->get_volume ();
+}
+
+void
+Proj_volume::load_img (const std::string& filename)
+{
+    this->load_img (filename.c_str());
+}
+
+void
+Proj_volume::load_header (const char* filename)
+{
+    std::ifstream ifs (filename);
+    if (!ifs.is_open()) {
+        logfile_printf ("Error opening %s for read", filename);
+        return;
+    }
+
+    while (true) {
+        std::string line;
+        getline (ifs, line);
+        if (!ifs.good()) {
+            /* End of file. */
+            break;
+        }
+
+        std::string tag, val;
+        if (!split_tag_val (line, tag, val)) {
+            /* No "=" found. */
+            break;
+        }
+
+        int rc;
+        rc = sscanf (line.c_str(), "num_steps = %d\n", &d_ptr->num_steps);
+        if (rc == 1) continue;
+
+        float f, g;
+        rc = sscanf (line.c_str(), "step_length = %f\n", &f);
+        if (rc == 1) {
+            d_ptr->step_length = f;
+            continue;
+        }
+
+        int a, b;
+        rc = sscanf (line.c_str(), "image_dim = %d %d\n", &a, &b);
+        if (rc == 3) {
+            d_ptr->image_dim[0] = a;
+            d_ptr->image_dim[1] = b;
+            continue;
+        }
+
+        rc = sscanf (line.c_str(), "image_spacing = %f %f\n", &f, &g);
+        if (rc == 2) {
+            d_ptr->image_spacing[0] = f;
+            d_ptr->image_spacing[1] = g;
+            continue;
+        }
+
+#if defined (commentout)
+        rc = sscanf (line.c_str(), "roi_offset = %d %d %d\n", &a, &b, &c);
+        if (rc == 3) {
+            roi_offset[0] = a;
+            roi_offset[1] = b;
+            roi_offset[2] = c;
+            continue;
+        }
+
+        rc = sscanf (line.c_str(), "roi_dim = %d %d %d\n", &a, &b, &c);
+        if (rc == 3) {
+            roi_dim[0] = a;
+            roi_dim[1] = b;
+            roi_dim[2] = c;
+            continue;
+        }
+
+        rc = sscanf (line.c_str(), "vox_per_rgn = %d %d %d\n", &a, &b, &c);
+        if (rc == 3) {
+            vox_per_rgn[0] = a;
+            vox_per_rgn[1] = b;
+            vox_per_rgn[2] = c;
+            continue;
+        }
+#endif
+
+        logfile_printf ("Error loading projv file\n%s\n", line.c_str());
+        return;
+    }
+
+#if defined (commentout)
+    fprintf (fp, "clipping_dist=%g %g\n", 
+        d_ptr->image_spacing[0], d_ptr->image_spacing[1]);
+    fprintf (fp, "nrm=%g %g %g\n", 
+        d_ptr->nrm[0], d_ptr->nrm[1], d_ptr->nrm[2]);
+    fprintf (fp, "src=%g %g %g\n", 
+        d_ptr->src[0], d_ptr->src[1], d_ptr->src[2]);
+    fprintf (fp, "iso=%g %g %g\n", 
+        d_ptr->iso[0], d_ptr->iso[1], d_ptr->iso[2]);
+    fprintf (fp, "ul_room=%g %g %g\n", 
+        d_ptr->ul_room[0], d_ptr->ul_room[1], d_ptr->ul_room[2]);
+    fprintf (fp, "incr_r=%g %g %g\n", 
+        d_ptr->incr_r[0], d_ptr->incr_r[1], d_ptr->incr_r[2]);
+    fprintf (fp, "incr_c=%g %g %g\n", 
+        d_ptr->incr_c[0], d_ptr->incr_c[1], d_ptr->incr_c[2]);
+    std::string s = d_ptr->pmat->get ();
+    fprintf (fp, "pmat=%s\n", s.c_str());
+    fclose (fp);
+#endif
+
+}
+
+void
+Proj_volume::load_header (const std::string& filename)
+{
+    this->load_header (filename.c_str());
+}
+
+void
+Proj_volume::load_projv (const char *filename)
+{
+    std::string fn_base = strip_extension_if (filename, "projv");
+    std::string proj_vol_hdr_fn = fn_base + ".projv";
+    this->load_header (proj_vol_hdr_fn);
+    std::string proj_vol_img_fn = fn_base + ".nrrd";
+    this->load_img (proj_vol_img_fn);
+}
+
+void
+Proj_volume::load_projv (const std::string& filename)
+{
+    this->load_projv (filename.c_str());
+}
diff --git a/src/plastimatch/base/proj_volume.h b/src/plastimatch/base/proj_volume.h
index 3c2d459..2b43a74 100644
--- a/src/plastimatch/base/proj_volume.h
+++ b/src/plastimatch/base/proj_volume.h
@@ -5,6 +5,7 @@
 #define _proj_volume_h_
 
 #include "plmbase_config.h"
+#include <string>
 
 class Proj_matrix;
 class Proj_volume_private;
@@ -51,7 +52,19 @@ public:
     const Volume *get_vol () const;
 
     void allocate ();
-    void save (const char* filename);
+
+    void save_img (const char* filename);
+    void save_img (const std::string& filename);
+    void save_header (const char* filename);
+    void save_header (const std::string& filename);
+    void save_projv (const char* filename);
+    void save_projv (const std::string& filename);
+    void load_img (const char* filename);
+    void load_img (const std::string& filename);
+    void load_header (const char* filename);
+    void load_header (const std::string& filename);
+    void load_projv (const char* filename);
+    void load_projv (const std::string& filename);
 
     void debug ();
 };
diff --git a/src/plastimatch/base/pwlut.cxx b/src/plastimatch/base/pwlut.cxx
new file mode 100755
index 0000000..8a1296f
--- /dev/null
+++ b/src/plastimatch/base/pwlut.cxx
@@ -0,0 +1,90 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plmbase_config.h"
+
+#include "print_and_exit.h"
+#include "pwlut.h"
+
+Pwlut::Pwlut ()
+{
+    left_slope = 1.0;
+    right_slope = 1.0;
+}
+
+void
+Pwlut::set_lut (const Float_pair_list& pwlut_fpl)
+{
+    /* Set the LUT */
+    this->fpl = pwlut_fpl;
+
+    /* Initialize some other variables for use during lookup */
+    left_slope = 1.0;
+    right_slope = 1.0;
+    ait_start = fpl.begin();
+    ait_end = fpl.end();
+    if (ait_start->first == -std::numeric_limits<float>::max()) {
+        left_slope = ait_start->second;
+        ait_start++;
+    }
+    if ((--ait_end)->first == std::numeric_limits<float>::max()) {
+        right_slope = ait_end->second;
+        --ait_end;
+    }
+}
+
+void
+Pwlut::set_lut (const std::string& pwlut_string)
+{
+    /* Parse the LUT string */
+    Float_pair_list fpl = parse_float_pairs (pwlut_string);
+    if (fpl.empty()) {
+        print_and_exit ("Error: couldn't parse pwlut string: %s\n",
+            pwlut_string.c_str());
+    }
+
+    this->set_lut (fpl);
+}
+
+float
+Pwlut::lookup (float vin) const
+{
+    float vout;
+
+    /* Three possible cases: before first node, between two nodes, and 
+       after last node */
+
+    /* Case 1 */
+    if (vin <= ait_start->first) {
+        vout = ait_start->second + (vin - ait_start->first) * left_slope;
+#if defined (commentout)
+        printf ("[1] < %f (%f -> %f)\n", ait_start->first, vin, vout);
+#endif
+        return vout;
+    }
+    else if (ait_start != ait_end) {
+        Float_pair_list::const_iterator ait = ait_start;
+        Float_pair_list::const_iterator prev = ait_start;
+        do {
+            ait++;
+            /* Case 2 */
+            if (vin <= ait->first) {
+                float slope = (ait->second - prev->second) 
+                    / (ait->first - prev->first);
+                vout = prev->second + (vin - prev->first) * slope;
+#if defined (commentout)
+                printf ("[2] in (%f,%f) (%f -> %f)\n", prev->first, 
+                    ait->first, vin, vout);
+#endif
+                return vout;
+            }
+            prev = ait;
+        } while (ait != ait_end);
+    }
+    /* Case 3 */
+    vout = ait_end->second + (vin - ait_end->first) * right_slope;
+#if defined (commentout)
+    printf ("[3] > %f (%f -> %f)\n", ait_end->first, vin, vout);
+#endif
+    return vout;
+}
diff --git a/src/plastimatch/base/pwlut.h b/src/plastimatch/base/pwlut.h
new file mode 100755
index 0000000..cd1c012
--- /dev/null
+++ b/src/plastimatch/base/pwlut.h
@@ -0,0 +1,38 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#ifndef _pwlut_h_
+#define _pwlut_h_
+
+#include "plmutil_config.h"
+#include <list>
+#include <utility>
+#include "itk_image_type.h"
+#include "float_pair_list.h"
+
+/*! \brief 
+ * The Pwlut class implements a lookup table with piecewise 
+ * linear segments
+ */
+class PLMBASE_API Pwlut {
+public:
+    Pwlut ();
+protected:
+    Float_pair_list fpl;
+    float left_slope;
+    float right_slope;
+    Float_pair_list::const_iterator ait_start;
+    Float_pair_list::const_iterator ait_end;
+public:
+    /*! \name Inputs */
+    ///@{
+    /*! \brief Set the input/output pairs in the lookup table */
+    void set_lut (const std::string& pwlut_string);
+    /*! \brief Set the input/output pairs in the lookup table */
+    void set_lut (const Float_pair_list& pwlut_fpl);
+    /*! \brief Interpolate a lookup value */
+    float lookup (float vin) const;
+    ///@}
+};
+
+#endif
diff --git a/src/plastimatch/base/ray_data.h b/src/plastimatch/base/ray_data.h
index 84441a1..72edb8d 100644
--- a/src/plastimatch/base/ray_data.h
+++ b/src/plastimatch/base/ray_data.h
@@ -6,18 +6,18 @@
 
 #include "plmbase_config.h"
 
-class Ray_data {
-public:
-    int ap_idx;
-    bool intersects_volume;
-    double ip1[3];       /* Front intersection with volume */
-    double ip2[3];       /* Back intersection with volume */
-    double p2[3];        /* Intersection with aperture plane */
-    double ray[3];       /* Unit vector in direction of ray */
-    double front_dist;   /* Distance from aperture to ip1 */
-    double back_dist;    /* Distance from aperture to ip2 */
-    double cp[3];        /* Intersection with front clipping plane */
-	int step_offset;	 /* Number of steps before reaching k = 0 */
+class Ray_data {
+public:
+    int ap_idx;
+    bool intersects_volume;
+    double ip1[3];       /* Front intersection with volume */
+    double ip2[3];       /* Back intersection with volume */
+    double p2[3];        /* Intersection with aperture plane */
+    double ray[3];       /* Unit vector in direction of ray */
+    double front_dist;   /* Distance from aperture to ip1 */
+    double back_dist;    /* Distance from aperture to ip2 */
+    double cp[3];        /* Intersection with front clipping plane */
+    int step_offset;	 /* Number of steps before reaching k = 0 */
 };
 
 #endif
diff --git a/src/plastimatch/base/ray_trace.h b/src/plastimatch/base/ray_trace.h
index 307bc56..7cea27e 100644
--- a/src/plastimatch/base/ray_trace.h
+++ b/src/plastimatch/base/ray_trace.h
@@ -10,7 +10,8 @@
 class Volume;
 class Volume_limit;
 
-//#define DRR_VERBOSE
+// #define DRR_VERBOSE 1
+
 #define DRR_PLANE_RAY_TOLERANCE 1e-8
 #define DRR_STRIDE_TOLERANCE 1e-10
 #define DRR_HUGE_DOUBLE 1e10
diff --git a/src/plastimatch/base/ray_trace_exact.cxx b/src/plastimatch/base/ray_trace_exact.cxx
index ef76cdd..5fcc5cd 100644
--- a/src/plastimatch/base/ray_trace_exact.cxx
+++ b/src/plastimatch/base/ray_trace_exact.cxx
@@ -23,15 +23,17 @@ ray_trace_exact_init_loopvars (
     double pt,         /* Input:  initial intersection of ray with volume */
     double ry,         /* Input:  normalized direction of ray */
     double origin,     /* Input:  origin of volume */
+    plm_long dim,      /* Input:  dimension of volume */
     double samp        /* Input:  pixel spacing of volume */
 )
 {
-#if (DRR_VERBOSE)
+#if DRR_VERBOSE
     printf ("pt/ry/off/samp: %g %g %g %g\n", pt, ry, origin, samp);
 #endif
 
     *aidir = SIGN (ry) * SIGN (samp);
     *ai = ROUND_INT ((pt - origin) / samp);
+    *ai = clamp<int> (*ai, 0, (int) dim - 1);
     *ao = SIGN (ry) 
 	* (((*ai) * samp + origin) + (SIGN (ry) * 0.5 * fabs (samp)) - pt);
 
@@ -42,25 +44,6 @@ ray_trace_exact_init_loopvars (
 	*ao = DRR_HUGE_DOUBLE;
 	*al = DRR_HUGE_DOUBLE;
     }
-
-#if defined (commentout)
-    if (ry > 0) {
-	*aidir = 1 * SIGN (samp);
-        *ai = (int) floor ((pt - origin + 0.5 * samp) / samp);
-        *ao = fabs(samp - ((pt - origin + 0.5 * samp) - (*ai) * samp));
-    } else {
-	*aidir = -1 * SIGN (samp);
-        *ai = (int) floor ((pt - origin + 0.5 * samp) / samp);
-        *ao = fabs(samp - ((*ai+1) * samp - (pt - origin + 0.5 * samp)));
-    }
-    if (fabs(ry) > DRR_STRIDE_TOLERANCE) {
-	*ao = *ao / fabs(ry);
-	*al = fabs(samp) / fabs(ry);
-    } else {
-	*ao = DRR_HUGE_DOUBLE;
-	*al = DRR_HUGE_DOUBLE;
-    }
-#endif
 }
 
 /* Initialize loop variables.  Returns 1 if the segment intersects 
@@ -118,18 +101,21 @@ ray_trace_exact_init (
 	ip1[0],
 	ray[0], 
 	vol->origin[0], 
+	vol->dim[0],
 	vol->spacing[0]);
     ray_trace_exact_init_loopvars (
 	ai_y, aiydir, ao_y, al_y, 
 	ip1[1],
 	ray[1], 
 	vol->origin[1], 
+	vol->dim[1], 
 	vol->spacing[1]);
     ray_trace_exact_init_loopvars (
 	ai_z, aizdir, ao_z, al_z, 
 	ip1[2], 
 	ray[2], 
 	vol->origin[2], 
+	vol->dim[2], 
 	vol->spacing[2]);
 
 #if defined (DRR_VERBOSE)
diff --git a/src/plastimatch/base/ray_trace_uniform.cxx b/src/plastimatch/base/ray_trace_uniform.cxx
index 1a68684..aaeca65 100644
--- a/src/plastimatch/base/ray_trace_uniform.cxx
+++ b/src/plastimatch/base/ray_trace_uniform.cxx
@@ -32,7 +32,6 @@ ray_trace_uniform (
     double ip1[3];
     double ip2[3];
     double phy_step[3];
-    double phy_step_mag;
 
     int ai[3]={0,0,0};
     double frac[3]={0.0, 0.0, 0.0};
@@ -71,26 +70,16 @@ ray_trace_uniform (
     phy_step[0] = uv[0] * ray_step;
     phy_step[1] = uv[1] * ray_step;
     phy_step[2] = uv[2] * ray_step;
-    phy_step_mag = vec3_len (phy_step);
 
     // Trace the ray
     z = 0;
-    for (pt = 0; pt < rlen; pt += phy_step_mag)
+    for (pt = 0; pt < rlen; pt += ray_step)
     {
         // Compute a point along the ray
         ipx[0] = ip1[0] + phy_step[0] * z;
         ipx[1] = ip1[1] + phy_step[1] * z;
         ipx[2] = ip1[2] + phy_step[2] * z;
 
-#if defined (commentout)
-        /* OLD VERSION - Compute CT Volume indices @ point */
-        ai[0] = (int) floorf ((ipx[0] - vol->origin[0] + 0.5 * ps[0]) / ps[0]);
-        ai[1] = (int) floorf ((ipx[1] - vol->origin[1] + 0.5 * ps[1]) / ps[1]);
-        ai[2] = (int) floorf ((ipx[2] - vol->origin[2] + 0.5 * ps[2]) / ps[2]);
-        idx = ((ai[2]*vol->dim[1] + ai[1]) * vol->dim[0]) + ai[0];
-        pix_density = img[idx];
-#endif
-
         // NEW VERSION - Compute CT volume indices and their fraction @ point + interpolation
 
         mijk[0] = (float) ((ipx[0] - vol->origin[0])/ps[0]);
@@ -100,13 +89,9 @@ ray_trace_uniform (
         li_clamp_3d(mijk, mijk_f, mijk_r, li_frac1, li_frac2, vol);
         idx = volume_index (vol->dim, mijk_f);
         pix_density = li_value (li_frac1[0], li_frac2[0], li_frac1[1], li_frac2[1], li_frac1[2], li_frac2[2], idx, img, vol);
-        //if (pt/phy_step_mag < 30 && pt/phy_step_mag > 24){
-        //	printf("%lg - %lg %lg %lg - %lg %lg %lg\n", pt/phy_step_mag, mijk[0], li_frac1[1], li_frac1[2], li_frac2[0], li_frac2[1], li_frac2[2]);}
-        //if (pix_density <= -999 || pix_density >= 4000) {pix_density = -1000;}
-		
+
         // I am passing the current step along the ray (z) through
         // vox_index here... not exactly great but not horrible.
-//        (*callback) (callback_data, z++, ray_step, pix_density);
-        (*callback) (callback_data, z++, phy_step_mag, pix_density);
+        (*callback) (callback_data, z++, ray_step, pix_density);
     }
 }
diff --git a/src/plastimatch/base/rpl_volume.cxx b/src/plastimatch/base/rpl_volume.cxx
index 67a2a3c..4db8d69 100644
--- a/src/plastimatch/base/rpl_volume.cxx
+++ b/src/plastimatch/base/rpl_volume.cxx
@@ -11,16 +11,20 @@
 #include "aperture.h"
 #include "interpolate.h"
 #include "compiler_warnings.h"
+#include "file_util.h"
 #include "logfile.h"
 #include "mha_io.h"
+#include "path_util.h"
 #include "plm_int.h"
 #include "plm_math.h"
+#include "plm_image_header.h"
 #include "proj_matrix.h"
 #include "proj_volume.h"
 #include "ray_data.h"
 #include "ray_trace.h"
 #include "rpl_volume.h"
 #include "volume.h"
+#include "volume_fill.h"
 #include "volume_limit.h"
 #include "volume_macros.h"
 #include "print_and_exit.h"
@@ -42,20 +46,20 @@ static void rpl_ray_trace_callback_ct_HU (
     double vox_len, 
     float vox_value);
 static void rpl_ray_trace_callback_PrSTPR (
-	void *callback_data, 
-	size_t vox_index, 
-	double vox_len, 
-	float vox_value);
+    void *callback_data, 
+    size_t vox_index, 
+    double vox_len, 
+    float vox_value);
 static void rpl_ray_trace_callback_PrSTPR_XiO_MGH (
-	void *callback_data, 
-	size_t vox_index, 
-	double vox_len, 
-	float vox_value);
+    void *callback_data, 
+    size_t vox_index, 
+    double vox_len, 
+    float vox_value);
 static void rpl_ray_trace_callback_range_length (
-	void *callback_data, 
-	size_t vox_index, 
-	double vox_len, 
-	float vox_value);
+    void *callback_data, 
+    size_t vox_index, 
+    double vox_len, 
+    float vox_value);
 
 typedef struct callback_data Callback_data;
 struct callback_data {
@@ -79,6 +83,7 @@ public:
     Aperture::Pointer aperture;
     double max_wed;
     double min_wed;
+    double min_distance_target;
 
 public:
     Rpl_volume_private () {
@@ -90,6 +95,7 @@ public:
         aperture = Aperture::New ();
         min_wed = 0.;
         max_wed = 0.;
+        min_distance_target = 0.;
     }
     ~Rpl_volume_private () {
         delete proj_vol;
@@ -254,7 +260,7 @@ Rpl_volume::get_rgdepth (
     Proj_matrix *pmat = d_ptr->proj_vol->get_proj_matrix();
 
     if (debug) {
-        proj_matrix_debug (pmat);
+        pmat->debug ();
     }
 
     /* Back project the voxel to the aperture plane */
@@ -303,69 +309,6 @@ Rpl_volume::get_rgdepth (
     return rgdepth;
 }
 
-double
-Rpl_volume::get_rgdepth2 (
-    const double* ct_xyz         /* I: location of voxel in world space */
-)
-{
-    int ap_ij[2], ap_idx;
-    double ap_xy[3];
-    double dist, rgdepth = 0.;
-    int debug = 0;
-
-    /* A couple of abbreviations */
-    const int *ires = d_ptr->proj_vol->get_image_dim();
-    Proj_matrix *pmat = d_ptr->proj_vol->get_proj_matrix();
-
-    if (debug) {
-        proj_matrix_debug (pmat);
-    }
-    /* Back project the voxel to the aperture plane */
-    mat43_mult_vec3 (ap_xy, pmat->matrix, ct_xyz);
-    ap_xy[0] = pmat->ic[0] + ap_xy[0] / ap_xy[2];
-    ap_xy[1] = pmat->ic[1] + ap_xy[1] / ap_xy[2];
-
-    /* Make sure value is not inf or NaN */
-    if (!is_number (ap_xy[0]) || !is_number (ap_xy[1])) {
-		return -1;
-    }
-
-	printf ("ap_xy = %lg %lg ->", ap_xy[0], ap_xy[1]);
-
-    /* Round to nearest aperture index */
-    ap_ij[0] = ROUND_INT (ap_xy[0]);
-    ap_ij[1] = ROUND_INT (ap_xy[1]);
-
-	printf (" %g %g", ap_xy[0], ap_xy[1]);
-
-    /* Only handle voxels inside the (square) aperture */
-    if (ap_ij[0] < 0 || ap_ij[0] >= ires[0] ||
-        ap_ij[1] < 0 || ap_ij[1] >= ires[1]) {
-        return -1;
-    }
-
-    ap_idx = ap_ij[1] * ires[0] + ap_ij[0];
-
-    /* Look up pre-computed data for this ray */
-    Ray_data *ray_data = &d_ptr->ray_data[ap_idx];
-    double *ap_xyz = ray_data->p2;
-
-    if (debug) {
-	printf ("ap_xyz = %g %g %g\n", ap_xyz[0], ap_xyz[1], ap_xyz[2]);
-    }
-
-    /* Compute distance from aperture to voxel */
-    dist = vec3_dist (ap_xyz, ct_xyz);
-
-    /* Subtract off standoff distance */
-    dist -= d_ptr->front_clipping_dist;
-
-    /* Retrieve the radiographic depth */
-    rgdepth = this->get_rgdepth (ap_xy, dist);
-
-    return rgdepth;
-}
-
 void Rpl_volume::set_ct (const Plm_image::Pointer& ct_volume)
 {
     d_ptr->ct = ct_volume;
@@ -419,6 +362,18 @@ double Rpl_volume::get_back_clipping_plane() const
     return d_ptr->back_clipping_dist;
 }
 
+void 
+Rpl_volume::set_minimum_distance_target(double min)
+{
+    d_ptr->min_distance_target = min;
+}
+	
+double 
+Rpl_volume::get_minimum_distance_target()
+{
+    return d_ptr->min_distance_target;
+}
+
 double
 Rpl_volume::get_max_wed ()
 {
@@ -434,14 +389,11 @@ Rpl_volume::get_min_wed ()
 void 
 Rpl_volume::compute_ray_data ()
 {
-    int ires[2];
-
     /* A couple of abbreviations */
     Proj_volume *proj_vol = d_ptr->proj_vol;
     const double *src = proj_vol->get_src();
     const double *nrm = proj_vol->get_nrm();
-    ires[0] = d_ptr->proj_vol->get_image_dim (0);
-    ires[1] = d_ptr->proj_vol->get_image_dim (1);
+    const int *ires = d_ptr->proj_vol->get_image_dim();
     Volume *ct_vol = d_ptr->ct->get_vol();
 
     lprintf ("Proj vol:\n");
@@ -449,15 +401,11 @@ Rpl_volume::compute_ray_data ()
     lprintf ("Ref vol:\n");
     ct_vol->debug ();
 
-    /* Make two passes through the aperture grid.  The first pass 
-       is used to find the clipping planes.  The second pass actually 
-       traces the rays. */
-
     /* Allocate data for each ray */
     if (d_ptr->ray_data) delete[] d_ptr->ray_data;
     d_ptr->ray_data = new Ray_data[ires[0]*ires[1]];
 
-    /* Scan through the aperture -- first pass */
+    /* Scan through the aperture plane */
     for (int r = 0; r < ires[1]; r++) {
         double r_tgt[3];
         double tmp[3];
@@ -560,7 +508,8 @@ Rpl_volume::compute_ray_data ()
     }
 }
 
-/* This function samples the CT into a RPL equivalent geometry */
+/* This function samples the CT into a RPL equivalent geometry.
+   The rpl_volume should be in proj_wed format, not in proj_ct format. */
 void 
 Rpl_volume::compute_rpl_ct_density ()
 {
@@ -578,8 +527,6 @@ Rpl_volume::compute_rpl_ct_density ()
     }
     Volume *ct_vol = d_ptr->ct->get_vol();
 
-    /* We don't need to do the first pass, as it was already done for the real rpl_volume */
-
     /* Ahh.  Now we can set the clipping planes and allocate the 
        actual volume. */
     double clipping_dist[2] = {
@@ -655,7 +602,7 @@ Rpl_volume::compute_rpl_HU ()
 
     /* We don't need to do the first pass, as it was already done for the real rpl_volume */
 
-	/* Ahh.  Now we can set the clipping planes and allocate the 
+    /* Ahh.  Now we can set the clipping planes and allocate the 
        actual volume. */
     double clipping_dist[2] = {
         d_ptr->front_clipping_dist, d_ptr->back_clipping_dist};
@@ -686,16 +633,10 @@ Rpl_volume::compute_rpl_HU ()
             }
 #endif
 
-            /* Check if beamlet is inside aperture, if not 
-               we skip ray tracing */
-            if (ap_img && ap_img[r*ires[0]+c] == 0) {
-                continue;
-            }
-
             this->rpl_ray_trace (
                 ct_vol,            /* I: CT volume */
                 ray_data,          /* I: Pre-computed data for this ray */
-				rpl_ray_trace_callback_ct_HU, /* I: callback */
+                rpl_ray_trace_callback_ct_HU, /* I: callback */
                 &d_ptr->ct_limit,  /* I: CT bounding region */
                 src,               /* I: @ source */
                 0,            /* I: range compensator thickness */
@@ -767,7 +708,6 @@ Rpl_volume::compute_rpl_range_length_rgc ()
 {
     int ires[2];
 
-
     /* A couple of abbreviations */
     Proj_volume *proj_vol = d_ptr->proj_vol;
     const double *src = proj_vol->get_src();
@@ -827,12 +767,6 @@ Rpl_volume::compute_rpl_range_length_rgc ()
             }
 #endif
 
-            /* Check if beamlet is inside aperture, if not 
-               we skip ray tracing */
-            if (ap_img && ap_img[r*ires[0]+c] == 0) {
-                continue;
-            }
-
             /* Initialize ray trace accum to range compensator thickness */
             double rc_thk = 0.;
             if (rc_img) {
@@ -856,7 +790,6 @@ void
 Rpl_volume::compute_rpl_PrSTRP_no_rgc ()
 {
     int ires[2];
-
     /* A couple of abbreviations */
     Proj_volume *proj_vol = d_ptr->proj_vol;
     const double *src = proj_vol->get_src();
@@ -869,7 +802,6 @@ Rpl_volume::compute_rpl_PrSTRP_no_rgc ()
     }
 
     Volume *ct_vol = d_ptr->ct->get_vol();
-
     /* Preprocess data by clipping against volume */
     this->compute_ray_data ();
 
@@ -882,11 +814,14 @@ Rpl_volume::compute_rpl_PrSTRP_no_rgc ()
 
     /* Ahh.  Now we can set the clipping planes and allocate the 
        actual volume. */
+
     double clipping_dist[2] = {
       d_ptr->front_clipping_dist, d_ptr->back_clipping_dist};
+
     d_ptr->proj_vol->set_clipping_dist (clipping_dist);
+
     d_ptr->proj_vol->allocate ();
- 
+
     /* Scan through the aperture -- second pass */
     for (int r = 0; r < ires[1]; r++) {
         for (int c = 0; c < ires[0]; c++) {
@@ -896,6 +831,7 @@ Rpl_volume::compute_rpl_PrSTRP_no_rgc ()
 
             /* Make some aliases */
             Ray_data *ray_data = &d_ptr->ray_data[ap_idx];
+
             /* Compute intersection with front clipping plane */
             vec3_scale3 (ray_data->cp, ray_data->ray, 
                 d_ptr->front_clipping_dist);
@@ -910,13 +846,6 @@ Rpl_volume::compute_rpl_PrSTRP_no_rgc ()
                 printf ("Tracing ray (%d,%d)\n", r, c);
             }
 #endif
-
-            /* Check if beamlet is inside aperture, if not 
-               we skip ray tracing */
-            if (ap_img && ap_img[r*ires[0]+c] == 0) {
-                continue;
-            }
-
             this->rpl_ray_trace (
                 ct_vol,            /* I: CT volume */
                 ray_data,          /* I: Pre-computed data for this ray */
@@ -933,7 +862,8 @@ Rpl_volume::compute_rpl_PrSTRP_no_rgc ()
 
 double Rpl_volume::compute_farthest_penetrating_ray_on_nrm(float range)
 {
-    int dim[3] = { this->get_vol()->dim[0], this->get_vol()->dim[1], this->get_vol()->dim[2]};
+    //int dim[3] = { this->get_vol()->dim[0], this->get_vol()->dim[1], this->get_vol()->dim[2]};
+    const plm_long *dim = this->get_vol()->dim;
     int idx = 0;
     double POI[3] = {0.0, 0.0, 0.0};
     double tmp[3] = {0.0, 0.0, 0.0};
@@ -954,7 +884,7 @@ double Rpl_volume::compute_farthest_penetrating_ray_on_nrm(float range)
 			if (s == dim[2]-1 || dim[2] == 0)
 			{
 				max_dist = offset + (double) dim[2] * this->get_vol()->spacing[2];
-				printf("Warning: Range > ray_length in volume => Some rays stop outside of the volume image.\n");
+				printf("Warning: Range > ray_length in volume => Some rays might stop outside of the volume image.\n");
 				printf("position of the maximal range on the z axis: z = %lg\n", max_dist);
 				return max_dist;
 			}
@@ -991,7 +921,8 @@ Rpl_volume::compute_proj_wed_volume (
     Proj_volume *proj_vol = d_ptr->proj_vol;
     float *proj_wed_vol_img = (float*) proj_wed_vol->img;
 
-    //Get some parameters from the proj volume, calculate src to isocenter distance
+    /* Get some parameters from the proj volume, 
+       calculate src to isocenter distance */
     const double *src = proj_vol->get_src();
     const double *iso = proj_vol->get_iso();
     const double sid_length = proj_vol->get_proj_matrix()->sid; //distance from source to aperture
@@ -1040,11 +971,11 @@ Rpl_volume::compute_proj_wed_volume (
     }
 }
 
+/* Resample a ct or dose volume into a wed_ct or wed_dose volume */
 void 
 Rpl_volume::compute_wed_volume (
     Volume *wed_vol, Volume *in_vol, float background)
 {
-
     /* A couple of abbreviations */
     Proj_volume *proj_vol = d_ptr->proj_vol;
     Volume *rvol = proj_vol->get_vol();
@@ -1054,9 +985,11 @@ Rpl_volume::compute_wed_volume (
     const int *ires = proj_vol->get_image_dim();
 
     plm_long wijk[3];  /* Index within wed_volume */
+
+    /* Fill the wed_vol with background values */
+    volume_fill (wed_vol, background);
    
     for (wijk[1] = 0; wijk[1] < ires[1]; wijk[1]++) {
-
         for (wijk[0] = 0; wijk[0] < ires[0]; wijk[0]++) {
 
             /* Compute index of aperture pixel */
@@ -1064,54 +997,42 @@ Rpl_volume::compute_wed_volume (
 
             bool debug = false;
             if (ap_idx == (ires[1]/2) * ires[0] + (ires[0] / 2)) {
-                //                printf ("DEBUGGING %d %d\n", ires[1], ires[0]);
-                //                debug = true;
+                // printf ("DEBUGGING %d %d\n", ires[1], ires[0]);
+                // debug = true;
             }
 #if defined (commentout)
 #endif
 
-            /* Make some aliases */
+	    /* Nothing to do if ray misses volume */
             Ray_data *ray_data = &d_ptr->ray_data[ap_idx];
-
-	    //Set the default to background, if ray misses volume
-	    //ires[2] - is this meaningless?
             if (!ray_data->intersects_volume) {
-                for (wijk[2] = 0; wijk[2] < ires[2]; wijk[2]++) {
-                    plm_long widx = volume_index (rvol->dim, wijk);
-                    wed_vol_img[widx] = background;
-                }
                 continue;
             }
 
-            /* Index within rpl_volume */
+            /* Keep track of index within rpl_volume */
             plm_long rijk[3] = { wijk[0], wijk[1], 0 };
 
-	    /*Perform the clipping, so the projection volume start points are the same*/
-
+	    /*Perform the clipping, so the projection volume 
+              start points are the same */
 	    double ray_start[3];
 	    double ray_end[3];
-	    
+
+            /* GCS FIX: Why do this?  Is the ray data not already valid? */
 	    if (!volume_limit_clip_segment (&d_ptr->ct_limit, ray_start, ray_end, ray_data->p2, ray_data->ip2)) {
                 printf("Error in ray clipping, exiting...\n");
                 return;
 	    }
-	    //	    volume_limit_clip_segment (&d_ptr->ct_limit, ray_start, ray_end, ray_data->p2, ray_data->ip2);
 
             /* Loop, looking for each output voxel */
             for (wijk[2] = 0; wijk[2] < rvol->dim[2]; wijk[2]++) {
                 plm_long widx = volume_index (rvol->dim, wijk);
 
-		//Set the default to background.
-		wed_vol_img[widx] = background;
-
                 /* Compute the currently required rpl for this step */
                 double req_rpl = wijk[2] * 1.0;
 
                 if (debug) printf ("--- (%d,%f)\n", (int) wijk[2], req_rpl);
 
-                /* Loop through input voxels looking for appropriate 
-                   value */
-
+                /* Loop through input voxels looking for appropriate value */
 		double prev_rpl = 0.;
 
                 while (rijk[2] < rvol->dim[2]) {
@@ -1132,8 +1053,6 @@ Rpl_volume::compute_wed_volume (
                         vec3_scale3 (xyz_init, ray_data->ray, dist);
 			vec3_add3 (xyz, xyz_init, ray_start);
                         
-			//NEW
-
 			float in_ijk_f[3];
 			in_ijk_f[0] = (xyz[0] - in_vol->origin[0]) / in_vol->spacing[0];
 			in_ijk_f[1] = (xyz[1] - in_vol->origin[1]) / in_vol->spacing[1];
@@ -1157,41 +1076,6 @@ Rpl_volume::compute_wed_volume (
 
 			float value = li_value(li_1[0], li_2[0],li_1[1], li_2[1],li_1[2], li_2[2],idx_floor,in_vol_img,in_vol);
 
-			/////////////////
-			
-                        /* Look up value at coordinate in input image */
-			
-			//OLD
-			/*
-                          plm_long in_ijk[3];
-                          in_ijk[2] = ROUND_PLM_LONG(
-                          (xyz[2] - in_vol->origin[2]) / in_vol->spacing[2]);
-                          in_ijk[1] = ROUND_PLM_LONG(
-                          (xyz[1] - in_vol->origin[1]) / in_vol->spacing[1]);
-                          in_ijk[0] = ROUND_PLM_LONG(
-                          (xyz[0] - in_vol->origin[0]) / in_vol->spacing[0]);
-
-                          if (debug) {
-                          printf ("%f %f %f\n", xyz[0], xyz[1], xyz[2]);
-                          printf ("%d %d %d\n", (int) in_ijk[0], 
-                          (int) in_ijk[1], (int) in_ijk[2]);
-                          }
-
-
-                          if (in_ijk[2] < 0 || in_ijk[2] >= in_vol->dim[2])
-                          break;
-                          if (in_ijk[1] < 0 || in_ijk[1] >= in_vol->dim[1])
-                          break;
-                          if (in_ijk[0] < 0 || in_ijk[0] >= in_vol->dim[0])
-                          break;
-
-                          plm_long in_idx = volume_index(in_vol->dim, in_ijk);
-
-                          float value = in_vol_img[in_idx];
-                          //		value = in_vol_img[in_idx];
-                          */
-
-
 			/* Write value to output image */
 			wed_vol_img[widx] = value;
 
@@ -1210,7 +1094,8 @@ Rpl_volume::compute_wed_volume (
 }
 
 void 
-Rpl_volume::compute_dew_volume (Volume *wed_vol, Volume *dew_vol, float background)
+Rpl_volume::compute_dew_volume (
+    Volume *wed_vol, Volume *dew_vol, float background)
 {
   
     double dummy_vec[3] = {0., 0., 0.};
@@ -1372,15 +1257,15 @@ Rpl_volume::compute_dew_volume (Volume *wed_vol, Volume *dew_vol, float backgrou
                     ray_adj_len = (vec3_len(coord_vec)/coord_ap_len)*vec3_len(dummy_adj_ray);
                     vec3_scale3(adj_ray_coord,ray_adj[i]->ray,ray_adj_len);
 
-		    if (!volume_limit_clip_segment (&d_ptr->ct_limit, ray_start, ray_end, ray_adj[i]->p2, ray_adj[i]->ip2)) {
+                    if (!volume_limit_clip_segment (&d_ptr->ct_limit, ray_start, ray_end, ray_adj[i]->p2, ray_adj[i]->ip2)) {
                         printf("Error in ray clipping, exiting...\n");
                         return;
-		    }
+                    }
 
                     vec3_add2(adj_ray_coord,src);
-		    vec3_sub2(adj_ray_coord,ray_start);
+                    vec3_sub2(adj_ray_coord,ray_start);
 		    
-		    rad_depth_input = vec3_len(adj_ray_coord);	    
+                    rad_depth_input = vec3_len(adj_ray_coord);	    
 
                     //Now look up the radiation length, using the provided function,
                     //knowing the ray and the length along it.
@@ -1411,15 +1296,15 @@ Rpl_volume::compute_dew_volume (Volume *wed_vol, Volume *dew_vol, float backgrou
                     else {
                         dummy_lin_ex = ray_rad_len[i]-floor(ray_rad_len[i]);
 
-			wijk[0] = (ray_lookup[i][0] - 1)/wed_vol->spacing[0];
-			wijk[1] = (ray_lookup[i][1] - 1)/wed_vol->spacing[1];
+                        wijk[0] = (ray_lookup[i][0] - 1)/wed_vol->spacing[0];
+                        wijk[1] = (ray_lookup[i][1] - 1)/wed_vol->spacing[1];
 
-                        //	    wijk[0] = ray_lookup[i][0] - 1;
-                        //	    wijk[1] = ray_lookup[i][1] - 1;
+                        //wijk[0] = ray_lookup[i][0] - 1;
+                        //wijk[1] = ray_lookup[i][1] - 1;
 
                         //Needed if dew dimensions are not automatically set by wed in wed_main.
-			//			wijk[0] = ((ray_lookup[i][0] - 1) - wed_vol->origin[0])/wed_vol->spacing[0];
-			//			wijk[1] = ((ray_lookup[i][1] - 1) - wed_vol->origin[1])/wed_vol->spacing[1];
+                        //wijk[0] = ((ray_lookup[i][0] - 1) - wed_vol->origin[0])/wed_vol->spacing[0];
+                        //wijk[1] = ((ray_lookup[i][1] - 1) - wed_vol->origin[1])/wed_vol->spacing[1];
 
                         if (wijk[0] < 0 || wijk[0] >= wed_vol->dim[0]) {break;}
                         if (wijk[1] < 0 || wijk[1] >= wed_vol->dim[1]) {break;}
@@ -1447,264 +1332,152 @@ Rpl_volume::compute_dew_volume (Volume *wed_vol, Volume *dew_vol, float backgrou
 }
 
 void 
-Rpl_volume::compute_beam_modifiers (
-    Volume *seg_vol, 
-    float background
-)
+Rpl_volume::apply_smearing_to_target(float smearing, std::vector <double>& map_min_distance, std::vector <double>& map_max_distance)
 {
-    double threshold = .2;  //theshold for interpolated, segmented volume
-
-    /* This assumes that dim & spacing are correctly set in aperture */
-    d_ptr->aperture->allocate_aperture_images ();
-
-    Volume::Pointer aperture_vol = d_ptr->aperture->get_aperture_volume ();
-    Volume::Pointer segdepth_vol 
-        = d_ptr->aperture->get_range_compensator_volume ();
-
-    Proj_volume *proj_vol = d_ptr->proj_vol;
-    Volume *rvol = proj_vol->get_vol();
-    float *seg_img = (float*) seg_vol->img;
-
-    unsigned char *aperture_img = (unsigned char*) aperture_vol->img;
-    float *segdepth_img = (float*) segdepth_vol->img;
-
-    const int *ires = proj_vol->get_image_dim();  //resolution of the 2-D proj vol aperture
-    int ires2[2];  //resolution of the output - user defined aperuture and segdepth_vol
-    ires2[0] = aperture_vol->dim[0];
-    ires2[1] = aperture_vol->dim[1];
-
-    int aij[2];  /* Index within aperture plane */
-    plm_long ap_idx;  /* Image index of aperture*/
-    plm_long output_idx;  /* Image index of output*/
-
-    plm_long rijk[3]; /* Index with rvol */
-
-    double cp_origin[3]; //intersection of clipping plane with ray
-    double seg_unit_ray[3]; //unit vector along ray
-    double seg_long_ray[3]; //unit vector along ray
-    double final_vec[3];  //final vector to target point
-    float final_index[3]; //index of final vector
-
-    //Trilinear interpoloation variables
-    plm_long ijk_floor[3];  //floor of rounded
-    plm_long ijk_round[3];  //ceiling of rounded
-    float li_1[3], li_2[3]; //upper/lower fractions
-    plm_long idx_floor;
-
-    //Interpolated seg_volume value
-    double interp_seg_value;
-
-    double current_depth; //current wed depth
-    double previous_depth; //previous wed depth
-    bool intersect_seg; //boolean that checks whether or not ray intersects with seg. volume
-    bool first_seg_check; //first point along a ray in the seg volume, to determine min energy
-
-    Ray_data *seg_ray;
-
-    std::vector< std::vector<double> > seg_max_wed;  //vector containing all max wed's in seg volume
-    seg_max_wed.resize( ires[1] );
-    for (int i=0;i!=ires[1];++i)  {
-        seg_max_wed[i].resize( ires[0] );
+    printf("Apply smearing to the target.\nThe smearing width is defined at the minimum depth of the target.\n");
+    /* Create a structured element of the right size */
+    int strel_half_size[2];
+    int strel_size[2];
+
+    /* Found the min of the target to be sure the smearing (margins) at the minimal depth */
+    double min = DBL_MAX;
+    for (int i = 0; i < map_min_distance.size(); i++)
+    {
+        if (map_min_distance[i] > 0 && map_min_distance[i] < min)
+        {
+            min = map_min_distance[i];
+        }
     }
-
-    std::vector< std::vector<double> > seg_min_wed;  //vector containing all min wed's in seg volume
-    seg_min_wed.resize( ires[1] );
-    for (int i=0;i!=ires[1];++i)  {
-        seg_min_wed[i].resize( ires[0] );
+    if (min == DBL_MAX)
+    {
+        printf("***ERROR: Target depth min is null for each ray. Smearing not applied\n");
+        return;
     }
+    d_ptr->min_distance_target = min + d_ptr->aperture->get_distance() + d_ptr->front_clipping_dist;
 
-    double min_seg_depth = 0; //value we assign the minimum wed in the seg volume
-    double max_seg_depth = 0;  //value we assign the maximum wed in the seg volume
 
-    double max_comp_depth = 0;  //maximum compensator volume depth
+    /* The smearing width is scaled to the aperture */
+    strel_half_size[0] = ROUND_INT(smearing * d_ptr->aperture->get_distance() / (d_ptr->min_distance_target * d_ptr->aperture->get_spacing()[0]));
+    strel_half_size[1] = ROUND_INT(smearing * d_ptr->aperture->get_distance() / (d_ptr->min_distance_target * d_ptr->aperture->get_spacing()[1]));
 
-    //Disposable variables
-    double max_wed_print = 0;
-    double min_wed_print = 0;
+    strel_size[0] = 1 + 2 * strel_half_size[0];
+    strel_size[1] = 1 + 2 * strel_half_size[1];
+    float smearing_ap = smearing * d_ptr->aperture->get_distance() / (min + d_ptr->aperture->get_distance() + d_ptr->front_clipping_dist);
 
-    for (int i=0; i!=ires2[1]; ++i) {
-        for (int j=0; j!=ires2[0]; ++j) {
+    int *strel = new int[strel_size[0]*strel_size[1]];
+    /* (rf, cf) center of the smearing */
+    for (int r = 0; r < strel_size[1]; r++) {
+        float rf = (float) (r - strel_half_size[1]) * d_ptr->aperture->get_spacing()[0];
+        for (int c = 0; c < strel_size[0]; c++) {
+            float cf = (float) (c - strel_half_size[0]) * d_ptr->aperture->get_spacing()[1];
 
-            output_idx = i*ires[0]+j;
+            int idx = r*strel_size[0] + c;
 
-            //Set aperture grid to 0 background.
-            aperture_img[output_idx] = 0;
-            //Set segdepth background.
-            segdepth_img[output_idx]= background;
+            strel[idx] = 0;
+            if ((rf*rf + cf*cf) <= smearing_ap*smearing_ap) {
+                strel[idx] = 1;
+            }
         }
     }
 
-    for (aij[1] = 0; aij[1] < ires[1]; aij[1]++) {
-        for (aij[0] = 0; aij[0] < ires[0]; aij[0]++) {
-
-            ap_idx = aij[1] * ires[0] + aij[0];
-
-            seg_ray = &d_ptr->ray_data[ap_idx];
-            vec3_copy(cp_origin, seg_ray->cp);
-            vec3_copy(seg_unit_ray, seg_ray->ray);
-
-
-
-            rijk[0] = aij[0];
-            rijk[1] = aij[1];
-            rijk[2] = 0.;
-
-            //Reset ray variables.
-            current_depth = 0;
-            previous_depth = 0;
-            min_seg_depth = 0;
-            max_seg_depth = 0;
-            first_seg_check = true;
-            intersect_seg = false;
-
-            //Increment by 1 along each ray, getting the position at each point.
-            while (rijk[2] < rvol->dim[2]) {
-	
-                //Scale distance along ray to rijk depth
-                vec3_scale3(seg_long_ray, seg_unit_ray, rijk[2]);
-                vec3_add3(final_vec, cp_origin, seg_long_ray);
-		
-                final_index[0] = (final_vec[0]-seg_vol->origin[0])/seg_vol->spacing[0];
-                final_index[1] = (final_vec[1]-seg_vol->origin[1])/seg_vol->spacing[1];
-                final_index[2] = (final_vec[2]-seg_vol->origin[2])/seg_vol->spacing[2];
+    /* Debugging information */
+    for (int r = 0; r < strel_size[1]; r++) {
+        for (int c = 0; c < strel_size[0]; c++) {
+            int idx = r*strel_size[0] + c;
+            printf ("%d ", strel[idx]);
+        }
+        printf ("\n");
+    }
 
-                //Trilinear interpolate the seg_vol binary matrix to find value of point
-                li_clamp_3d (final_index, ijk_floor, ijk_round,li_1,li_2,seg_vol);
-                idx_floor = volume_index(seg_vol->dim, ijk_floor);
-                interp_seg_value = li_value(li_1[0], li_2[0],li_1[1], li_2[1],li_1[2], li_2[2],idx_floor,seg_img,seg_vol);
+    /* Apply smear to target maps */
+    double distance_min;
+    double distance_max;
+    std::vector<double> min_distance_tmp (map_min_distance.size(), 0);
+    std::vector<double> max_distance_tmp (map_max_distance.size(), 0);
 
-                if (interp_seg_value > threshold)  {
+    for (int ar = 0; ar < d_ptr->aperture->get_dim()[1]; ar++) {
+        for (int ac = 0; ac < d_ptr->aperture->get_dim()[0]; ac++) {
+            int aidx = ar * d_ptr->aperture->get_dim()[0] + ac;
 
-                    intersect_seg = true; //this ray intersects the segmentation volume
+            /* Reset the limit values */
+            distance_min = DBL_MAX;
+            distance_max = 0;
 
-                    //If point is within segmentation volume, set wed.
-                    current_depth = this->get_rgdepth (aij, rijk[2]);
-	  
-                    if (first_seg_check)  {
-                        min_seg_depth = current_depth;
-                        first_seg_check = false;
+            for (int sr = 0; sr < strel_size[1]; sr++) {
+                int pr = ar + sr - strel_half_size[1];
+                if (pr < 0 || pr >= d_ptr->aperture->get_dim()[1]) {
+                    continue;
+                }
+                for (int sc = 0; sc < strel_size[0]; sc++) {
+                    int pc = ac + sc - strel_half_size[0];
+                    if (pc < 0 || pc >= d_ptr->aperture->get_dim()[0]) {
+                        continue;
                     }
 
-                    previous_depth = current_depth;
-                }
+                    int sidx = sr * strel_size[0] + sc;
+                    if (strel[sidx] == 0) {
+                        continue;
+                    }
 
-                else {
-                    if (intersect_seg)  { 
-                        //while we aren't currently in the seg. volume, this ray has been,
-                        //so check if we just exited to set the max_seg_depth
-                        if (previous_depth>0)  {
-                            max_seg_depth = previous_depth;
-                            previous_depth = 0; //redundant
-                        }
+                    int pidx = pr * d_ptr->aperture->get_dim()[0] + pc;
+                    if (map_min_distance[pidx] > 0 && map_min_distance[pidx] < distance_min) {
+                        distance_min = map_min_distance[pidx];
+                    }
+                    if (map_max_distance[pidx] > distance_max) {
+                        distance_max = map_max_distance[pidx];
                     }
                 }
-
-                rijk[2]++;
             }
-            //Total ray wed tabulated
-
-            seg_min_wed[ aij[1] ][ aij[0] ] = min_seg_depth;
-            seg_max_wed[ aij[1] ][ aij[0] ] = max_seg_depth;
-
-        }
-    }
-  
-    //Get max wed
-    for (int i=0; i!=ires2[1]; ++i)  {
-        for (int j=0; j!=ires2[0]; ++j)  {
-            if (seg_max_wed[i][j]>max_comp_depth)  {max_comp_depth = seg_max_wed[i][j];}
-        }
-    }
-
-    //Assign final values to volumes
-    for (int i=0; i!=ires[1]; ++i)  {
-        for (int j=0; j!=ires[0]; ++j)  {
-
-            output_idx = i*ires[0]+j;
-            //      output_idx = ((int) ((ires2[1]-ires[1])/2.) + i)*ires[0] + (int) ((ires2[0]-ires[0])/2.) + j;
-            //Fix the above line eventually, wrap up work to make output not tied to aperture size.
-      
-            //Assign aperture volume  //Check this in the future: >0 is a silly threshold.
-            if (seg_max_wed[i][j]>0) {aperture_img[output_idx] = 1;}
-            else {aperture_img[output_idx] = 0;}
-      
-            //Assign seg depth volume
-            segdepth_img[output_idx] = max_comp_depth - seg_max_wed[i][j];
-        }
-    }
-
-    //Extra code to determine max and min wed for seg volume + compensator, needs to be cleaned up ///////////////////////////////////////////
-  
-    for (int i=0; i!=ires2[1]; ++i) {
-        for (int j=0; j!=ires2[0]; ++j) {
-            output_idx = i*ires[0]+j;
-      
-            //Find max wed (should be same as max_comp_depth)
-            if (max_wed_print < seg_max_wed[i][j] + segdepth_img[output_idx])  {max_wed_print = seg_max_wed[i][j] + segdepth_img[output_idx];}
-        }
-    }
-
-    min_wed_print = max_wed_print; //start the minimum at the maximum, so we can go down from there
-
-    for (int i=0; i!=ires2[1]; ++i) {
-        for (int j=0; j!=ires2[0]; ++j) {
-
-            output_idx = i*ires[0]+j;
-     
-            //Find min wed.
-            if (aperture_img[output_idx]==1)  {
-                if (min_wed_print > seg_min_wed[i][j] + segdepth_img[output_idx])  {min_wed_print = seg_min_wed[i][j] + segdepth_img[output_idx];}	
+            if (distance_min == DBL_MAX)
+            {
+                min_distance_tmp[aidx] = 0;
+            }
+            else 
+            {
+                min_distance_tmp[aidx] = distance_min;
             }
+            max_distance_tmp[aidx] = distance_max;
         }
     }
 
-    std::cout<<"Max wed in the target is "<<max_wed_print<<" mm."<<std::endl;
-    std::cout<<"Min wed in the target is "<<min_wed_print<<" mm."<<std::endl;
-
-    /* Save these values in private data store */
-    d_ptr->max_wed = max_wed_print;
-    d_ptr->min_wed = min_wed_print;
-
-    printf("\n min & max: %lg %lg\n", d_ptr->min_wed, d_ptr->max_wed);
-
-    //End extra code //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    /* update the initial distance map */
+    for (int i = 0; i < map_min_distance.size(); i++)
+    {
+        map_min_distance[i] = min_distance_tmp[i];
+        map_max_distance[i] = max_distance_tmp[i];
+    }
 
-	/* The range compensator is made of Lucite */
-	for (int i=0; i!=ires2[1]; ++i) {
-		for (int j=0; j!=ires2[0]; ++j) {
-            output_idx = i*ires[0]+j;
-			segdepth_img[output_idx] = segdepth_img[output_idx] / (1.19*0.98); // rho * WER
-		}
-	}
+    /* Clean up */
+    delete[] strel;
 }
 
 void
 Rpl_volume::compute_volume_aperture(Aperture::Pointer ap)
 {
-	int dim[3] = {(int) this->get_vol()->dim[0], (int) this->get_vol()->dim[1], (int) this->get_vol()->dim[2]};
+    int dim[3] = {(int) this->get_vol()->dim[0], (int) this->get_vol()->dim[1], (int) this->get_vol()->dim[2]};
 	
-	float* ap_vol_img = (float*) this->get_vol()->img;
+    float* ap_vol_img = (float*) this->get_vol()->img;
 
-	Volume::Pointer ap_vol = ap->get_aperture_volume ();
+    Volume::Pointer ap_vol = ap->get_aperture_volume ();
     unsigned char *ap_img = (unsigned char*) ap_vol->img;
 
-	int idx = 0;
+    int idx = 0;
 
-	for(int i = 0; i < dim[0] * dim[1]; i++)
-	{
-		for(int j = 0; j < dim[2]; j++)
-		{
-			idx = j * dim[0] * dim[1] + i;
-			if ((float) ap_img[i] == 1)
-			{
-				ap_vol_img[idx] = 1;
-			}
-			else
-			{
-				ap_vol_img[idx] = 0;
-			}
-		}
-	}
+    for(int i = 0; i < dim[0] * dim[1]; i++)
+    {
+        for(int j = 0; j < dim[2]; j++)
+        {
+            idx = j * dim[0] * dim[1] + i;
+            if ((float) ap_img[i] == 1)
+            {
+                ap_vol_img[idx] = 1;
+            }
+            else
+            {
+                ap_vol_img[idx] = 0;
+            }
+        }
+    }
 }
 
 void 
@@ -1742,29 +1515,66 @@ Rpl_volume::apply_beam_modifiers () // In this new version the range compensator
 }
 
 void 
-Rpl_volume::compute_aperture (
-    Volume *tgt_vol, 
-    float background
-)
+Rpl_volume::compute_beam_modifiers_passive_scattering (Volume *seg_vol)
 {
-#if defined (commentout)
-    /* This assumes that dim & spacing are correctly set in aperture */
-    d_ptr->aperture->allocate_aperture_images ();
-
-    Volume *ap_vol = d_ptr->aperture->get_aperture_vol();
-    Volume *rc_vol = d_ptr->aperture->get_range_compensator_vol();
-    unsigned char *ap_img = (unsigned char*) ap_vol->img;
-    float *rc_img = (float*) rc_vol->img;
-
-    Proj_volume *proj_vol = d_ptr->proj_vol;
-    Volume *rvol = proj_vol->get_vol();
-
-    float *tgt_img = (float*) tgt_vol->img;
-#endif
+    std::vector<double> min;
+    std::vector<double> max;
+    compute_beam_modifiers_core (seg_vol, false, 0, 0, 0, min, max);
+    return;
 }
 
-Volume* 
-Rpl_volume::get_vol ()
+void 
+Rpl_volume::compute_beam_modifiers_active_scanning (Volume *seg_vol)
+{
+    std::vector<double> min;
+    std::vector<double> max;
+    compute_beam_modifiers_core (seg_vol, true, 0, 0, 0, min, max);
+    return;
+}
+
+void 
+Rpl_volume::compute_beam_modifiers_passive_scattering (Volume *seg_vol, float smearing, float proximal_margin, float distal_margin)
+{
+    std::vector<double> min;
+    std::vector<double> max;
+    compute_beam_modifiers_core (seg_vol, false, smearing, proximal_margin, distal_margin, min, max);
+    return;
+}
+
+void 
+Rpl_volume::compute_beam_modifiers_passive_scattering_slicerRt (Plm_image::Pointer& plmTgt, float smearing, float proximal_margin, float distal_margin)
+{
+    std::vector<double> min;
+    std::vector<double> max;
+    compute_beam_modifiers_core_slicerRt (plmTgt, false, smearing, proximal_margin, distal_margin, min, max);
+    return;
+}
+
+void 
+Rpl_volume::compute_beam_modifiers_active_scanning (Volume *seg_vol, float smearing, float proximal_margin, float distal_margin)
+{
+    std::vector<double> min;
+    std::vector<double> max;
+    compute_beam_modifiers_core (seg_vol, true, smearing, proximal_margin, distal_margin, min, max);
+    return;
+}
+
+void 
+Rpl_volume::compute_beam_modifiers_passive_scattering (Volume *seg_vol, float smearing, float proximal_margin, float distal_margin, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max)
+{
+    compute_beam_modifiers_core (seg_vol, false, smearing, proximal_margin, distal_margin, map_wed_min, map_wed_max);
+    return;
+}
+
+void 
+Rpl_volume::compute_beam_modifiers_active_scanning (Volume *seg_vol, float smearing, float proximal_margin, float distal_margin, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max)
+{
+    compute_beam_modifiers_core (seg_vol, true, smearing, proximal_margin, distal_margin, map_wed_min, map_wed_max);
+    return;
+}
+
+Volume* 
+Rpl_volume::get_vol ()
 {
     return d_ptr->proj_vol->get_vol ();
 }
@@ -1784,7 +1594,60 @@ Rpl_volume::get_proj_volume ()
 void
 Rpl_volume::save (const char *filename)
 {
-    d_ptr->proj_vol->save (filename);
+    std::string fn_base = strip_extension_if (filename, "rpl");
+    std::string rpl_vol_fn = fn_base + ".rpl";
+
+    /* Save proj volume */
+    std::string proj_vol_fn = fn_base + ".projv";
+    d_ptr->proj_vol->save_projv (proj_vol_fn);
+
+    /* Save ray data */
+    if (d_ptr->ray_data) {
+        std::string raydata_fn = fn_base + ".raydata";
+        FILE *fp = plm_fopen (raydata_fn, "wb");
+        const int *ires = d_ptr->proj_vol->get_image_dim();
+        for (int r = 0; r < ires[1]; r++) {
+            for (int c = 0; c < ires[0]; c++) {
+                int ap_idx = r * ires[0] + c;
+                Ray_data *rd = &d_ptr->ray_data[ap_idx];
+                fprintf (fp, 
+                    "%d %g %g %g %g %g %g %g %g %g "
+                    "%g %g %g %g %g %g %g %g %d\n",
+                    rd->ap_idx, 
+                    rd->ip1[0], rd->ip1[1], rd->ip1[2], 
+                    rd->ip2[0], rd->ip2[1], rd->ip2[2], 
+                    rd->p2[0], rd->p2[1], rd->p2[2], 
+                    rd->ray[0], rd->ray[1], rd->ray[2], 
+                    rd->front_dist, rd->back_dist, 
+                    rd->cp[0], rd->cp[1], rd->cp[2], 
+                    rd->step_offset);
+            }
+        }
+        fclose (fp);
+    }
+
+    /* Don't save aperture (right?) */
+
+    /* Don't save CT image (right?) */
+
+    /* Save remaining private data */
+    FILE *fp = plm_fopen (rpl_vol_fn, "wb");
+    fprintf (fp, "front_clipping_dist = %g\n", d_ptr->front_clipping_dist);
+    fprintf (fp, "back_clipping_dist = %g\n", d_ptr->back_clipping_dist);
+    fprintf (fp, "volume_limit = %g %g %g %g %g %g %d %d %d\n",
+        d_ptr->ct_limit.lower_limit[0],
+        d_ptr->ct_limit.lower_limit[1],
+        d_ptr->ct_limit.lower_limit[2],
+        d_ptr->ct_limit.upper_limit[0],
+        d_ptr->ct_limit.upper_limit[1],
+        d_ptr->ct_limit.upper_limit[2],
+        d_ptr->ct_limit.dir[0],
+        d_ptr->ct_limit.dir[1],
+        d_ptr->ct_limit.dir[2]);
+    fprintf (fp, "min_wed = %g\n", d_ptr->min_wed);
+    fprintf (fp, "max_wed = %g\n", d_ptr->max_wed);
+    fprintf (fp, "min_distance_target = %g\n", d_ptr->min_distance_target);
+    fclose (fp);
 }
 
 void
@@ -1793,9 +1656,51 @@ Rpl_volume::save (const std::string& filename)
     this->save (filename.c_str());
 }
 
+void
+Rpl_volume::save_img (const char *filename)
+{
+    d_ptr->proj_vol->save_img (filename);
+}
+
+void
+Rpl_volume::save_img (const std::string& filename)
+{
+    this->save_img (filename.c_str());
+}
+
+void
+Rpl_volume::load_rpl (const char *filename)
+{
+    printf ("Loading rpl\n");
+    std::string fn_base = strip_extension_if (filename, "rpl");
+    std::string proj_vol_fn = fn_base + ".projv";
+    printf ("-> %s\n-> %s-> %s\n", 
+        filename, fn_base.c_str(), proj_vol_fn.c_str());
+    d_ptr->proj_vol->load_projv (proj_vol_fn);
+    printf ("Done.\n");
+}
+
+void
+Rpl_volume::load_rpl (const std::string& filename)
+{
+    this->load_rpl (filename.c_str());
+}
+
+void
+Rpl_volume::load_img (const char *filename)
+{
+    d_ptr->proj_vol->load_img (filename);
+}
+
+void
+Rpl_volume::load_img (const std::string& filename)
+{
+    this->load_img (filename.c_str());
+}
+
 float compute_PrSTPR_from_HU(float CT_HU)
 {
-	return compute_PrSTPR_Schneider_weq_from_HU(CT_HU);
+    return compute_PrSTPR_Schneider_weq_from_HU(CT_HU);
 }
 
 float 
@@ -1869,34 +1774,23 @@ compute_PrSTRP_XiO_MGH_weq_from_HU (float CT_HU) //YKP, Linear interpolation
 
 float compute_PrWER_from_HU(float CT_HU)
 {
-	return compute_PrSTPR_from_HU(CT_HU) / compute_density_from_HU(CT_HU);
+    return compute_PrSTPR_from_HU(CT_HU) / compute_density_from_HU(CT_HU);
 }
 
 float compute_density_from_HU (float CT_HU) // from Schneider's paper: Phys. Med. Biol.41 (1996) 111�124
 {
-	if(CT_HU <= -1000)
-	{
-		return 0.001205;
-	}
-	else if (CT_HU > -1000 && CT_HU <= 65.64)
-	{
-		return (1-.001205)/1000 * CT_HU + 1;
-	}
-	else
-	{
-		return .0006481 * CT_HU + 1.0231;
-	}
-}
-
-void Rpl_volume::aprc_ray_trace (
-    Volume *tgt_vol,             /* I: CT volume */
-    Ray_data *ray_data,          /* I: Pre-computed data for this ray */
-    Volume_limit *vol_limit,     /* I: CT bounding region */
-    const double *src,           /* I: @ source */
-    double rc_thk,               /* I: range compensator thickness */
-    int* ires                    /* I: ray cast resolution */
-)
-{
+    if(CT_HU <= -1000)
+    {
+        return 0.001205;
+    }
+    else if (CT_HU > -1000 && CT_HU <= 65.64)
+    {
+        return (1-.001205)/1000 * CT_HU + 1;
+    }
+    else
+    {
+        return .0006481 * CT_HU + 1.0231;
+    }
 }
 
 void
@@ -2056,7 +1950,7 @@ rpl_ray_trace_callback_ct_density (
         return;
     }
 
-	depth_img[ap_area*step_num + ap_idx] = compute_density_from_HU(vox_value);
+    depth_img[ap_area*step_num + ap_idx] = compute_density_from_HU(vox_value);
 }
 
 static
@@ -2076,7 +1970,7 @@ rpl_ray_trace_callback_PrSTPR (
     int ap_area = cd->ires[0] * cd->ires[1];
     size_t step_num = vox_index + cd->step_offset;
 
-	cd->accum += vox_len * compute_PrSTPR_from_HU (vox_value); //vox_value = CT_HU
+    cd->accum += vox_len * compute_PrSTPR_from_HU (vox_value); //vox_value = CT_HU
 
 #if VERBOSE
     if (global_debug) {
@@ -2120,7 +2014,7 @@ rpl_ray_trace_callback_range_length (
     int ap_area = cd->ires[0] * cd->ires[1];
     size_t step_num = vox_index + cd->step_offset;
 
-	cd->accum += vox_len * compute_density_from_HU (vox_value); //vox_value = CT_HU
+    cd->accum += vox_len * compute_density_from_HU (vox_value); //vox_value = CT_HU
 
 #if VERBOSE
     if (global_debug) {
@@ -2193,6 +2087,458 @@ rpl_ray_trace_callback_RSP (
     depth_img[ap_area*step_num + ap_idx] = cd->accum;
 }
 
+void
+Rpl_volume::compute_beam_modifiers_core (Volume *seg_vol, bool active, float smearing, float proximal_margin, float distal_margin, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max)
+{
+    printf("Compute target distance limits...\n");
+    /* compute the target min and max distance (not wed!) map in the aperture */
+    compute_target_distance_limits (seg_vol, map_wed_min, map_wed_max);
+
+    printf("Apply smearing to the target...\n");
+    /* widen the min/max distance maps */
+    if (smearing > 0)
+    {
+        apply_smearing_to_target(smearing, map_wed_min, map_wed_max);
+    }
+
+    printf("Apply longitudinal margins...\n");
+    /* add the margins */
+    for (int i = 0; i < map_wed_min.size(); i++)
+    {
+        map_wed_min[i] -= proximal_margin;
+        if (map_wed_min[i] < 0) {
+            map_wed_min[i] = 0;
+        }
+        if (map_wed_max[i] > 0)
+        {
+            map_wed_max[i] += distal_margin;
+        }
+    }
+
+    printf("Compute max wed...\n");
+    /* compute wed limits from depth limits and compute max wed of the target + margins */
+    int idx = 0;
+    double max_wed = 0;
+    int i[2] = {0, 0};
+    for (i[0] = 0; i[0] < d_ptr->aperture->get_aperture_volume()->dim[0]; i[0]++){
+        for (i[1] = 0; i[1] < d_ptr->aperture->get_aperture_volume()->dim[1]; i[1]++){
+            idx = i[0] + i[1] * d_ptr->aperture->get_aperture_volume()->dim[0];
+            if (map_wed_max[idx] <= 0) 
+            {
+                continue;
+            }
+            map_wed_min[idx] = this->get_rgdepth(i, map_wed_min[idx]);
+            map_wed_max[idx] = this->get_rgdepth(i, map_wed_max[idx]);
+            if (map_wed_max[idx] > max_wed) {
+                max_wed = map_wed_max[idx];
+            }
+        }
+    }
+
+    printf("Compute the aperture...\n");
+    /* compute the aperture */
+    /* This assumes that dim & spacing are correctly set in aperture */
+    d_ptr->aperture->allocate_aperture_images ();
+
+    Volume::Pointer aperture_vol = d_ptr->aperture->get_aperture_volume ();
+    unsigned char *aperture_img = (unsigned char*) aperture_vol->img;
+    for (int i = 0; i < aperture_vol->dim[0] * aperture_vol->dim[0]; i++)
+    {
+        if (map_wed_min[i] > 0) {
+            aperture_img[i] = 1;
+        }
+        else {
+            aperture_img[i] = 0;
+        }
+    }
+	
+    /* compute the range compensator if passive beam line -- PMMA range compensator */
+    Volume::Pointer range_comp_vol = d_ptr->aperture->get_range_compensator_volume ();
+    float *range_comp_img = (float*) range_comp_vol->img;
+	
+    if (active == false)
+    {
+        printf("Compute range compensator...\n");
+    }
+
+    for (int i = 0; i < aperture_vol->dim[0] * aperture_vol->dim[1]; i++)
+    {
+        if (active == true)
+        {
+            range_comp_img[i] = 0;
+        }
+        else 
+        {
+            range_comp_img[i] = (max_wed - map_wed_max[i]) / (PMMA_STPR * PMMA_DENSITY);
+        }
+    }
+
+    /* compute the max/min wed of the entire target + margins + range_comp*/
+    double total_min_wed = 0;
+    double total_max_wed = 0;
+    // Max should be the same as the max in the target as for this ray rgcomp is null
+    for (int i = 0; i < aperture_vol->dim[0] * aperture_vol->dim[1]; i++)
+    {
+        if (range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_max[i] > total_max_wed) { // if active beam line, range comp is null
+            total_max_wed = range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_max[i];
+        }
+    }
+    total_min_wed = total_max_wed;
+    for (int i = 0; i < aperture_vol->dim[0] * aperture_vol->dim[1]; i++)
+    {
+        if ((range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_max[i] > 0) && (range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_min[i] < total_min_wed)) {
+            total_min_wed = range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_min[i];
+        }
+    }
+
+    printf("Max wed in the target is %lg mm.\n", total_max_wed);
+    printf("Min wed in the target is %lg mm.\n", total_min_wed);
+
+    /* Save these values in private data store */
+    d_ptr->max_wed = total_max_wed;
+    d_ptr->min_wed = total_min_wed;
+    return;
+}
+
+void
+Rpl_volume::compute_beam_modifiers_core_slicerRt (Plm_image::Pointer& plmTgt, bool active, float smearing, float proximal_margin, float distal_margin, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max)
+{
+    printf("Compute target distance limits...\n");
+
+    /* compute the target min and max distance (not wed!) map in the aperture */
+    compute_target_distance_limits_slicerRt(plmTgt, map_wed_min, map_wed_max);
+
+    printf("Apply smearing to the target...\n");
+    /* widen the min/max distance maps */
+    if (smearing > 0)
+    {
+        apply_smearing_to_target(smearing, map_wed_min, map_wed_max);
+    }
+
+    printf("Apply longitudinal margins...\n");
+    /* add the margins */
+    for (int i = 0; i < map_wed_min.size(); i++)
+    {
+        map_wed_min[i] -= proximal_margin;
+        if (map_wed_min[i] < 0) {
+            map_wed_min[i] = 0;
+        }
+        if (map_wed_max[i] > 0)
+        {
+            map_wed_max[i] += distal_margin;
+        }
+    }
+
+    printf("Compute max wed...\n");
+    /* compute wed limits from depth limits and compute max wed of the target + margins */
+    int idx = 0;
+    double max_wed = 0;
+    int i[2] = {0, 0};
+    for (i[0] = 0; i[0] < d_ptr->aperture->get_aperture_volume()->dim[0]; i[0]++){
+        for (i[1] = 0; i[1] < d_ptr->aperture->get_aperture_volume()->dim[1]; i[1]++){
+            idx = i[0] + i[1] * d_ptr->aperture->get_aperture_volume()->dim[0];
+            if (map_wed_max[idx] <= 0) 
+            {
+                continue;
+            }
+            map_wed_min[idx] = this->get_rgdepth(i, map_wed_min[idx]);
+            map_wed_max[idx] = this->get_rgdepth(i, map_wed_max[idx]);
+            if (map_wed_max[idx] > max_wed) {
+                max_wed = map_wed_max[idx];
+            }
+        }
+    }
+
+    printf("Compute the aperture...\n");
+    /* compute the aperture */
+    /* This assumes that dim & spacing are correctly set in aperture */
+    d_ptr->aperture->allocate_aperture_images ();
+
+    Volume::Pointer aperture_vol = d_ptr->aperture->get_aperture_volume ();
+    unsigned char *aperture_img = (unsigned char*) aperture_vol->img;
+    for (int i = 0; i < aperture_vol->dim[0] * aperture_vol->dim[0]; i++)
+    {
+        if (map_wed_min[i] > 0) {
+            aperture_img[i] = 1;
+        }
+        else {
+            aperture_img[i] = 0;
+        }
+    }
+	
+    /* compute the range compensator if passive beam line -- PMMA range compensator */
+    Volume::Pointer range_comp_vol = d_ptr->aperture->get_range_compensator_volume ();
+    float *range_comp_img = (float*) range_comp_vol->img;
+	
+    if (active == false)
+    {
+        printf("Compute range compensator...\n");
+    }
+
+    for (int i = 0; i < aperture_vol->dim[0] * aperture_vol->dim[1]; i++)
+    {
+        if (active == true)
+        {
+            range_comp_img[i] = 0;
+        }
+        else 
+        {
+            range_comp_img[i] = (max_wed - map_wed_max[i]) / (PMMA_STPR * PMMA_DENSITY);
+        }
+    }
+
+    /* compute the max/min wed of the entire target + margins + range_comp*/
+    double total_min_wed = 0;
+    double total_max_wed = 0;
+    // Max should be the same as the max in the target as for this ray rgcomp is null
+    for (int i = 0; i < aperture_vol->dim[0] * aperture_vol->dim[1]; i++)
+    {
+        if (range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_max[i] > total_max_wed) { // if active beam line, range comp is null
+            total_max_wed = range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_max[i];
+        }
+    }
+    total_min_wed = total_max_wed;
+    for (int i = 0; i < aperture_vol->dim[0] * aperture_vol->dim[1]; i++)
+    {
+        if ((range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_max[i] > 0) && (range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_min[i] < total_min_wed)) {
+            total_min_wed = range_comp_img[i] * PMMA_STPR * PMMA_DENSITY + map_wed_min[i];
+        }
+    }
+
+    printf("Max wed in the target is %lg mm.\n", total_max_wed);
+    printf("Min wed in the target is %lg mm.\n", total_min_wed);
+
+    /* Save these values in private data store */
+    d_ptr->max_wed = total_max_wed;
+    d_ptr->min_wed = total_min_wed;
+    return;
+}
+
+void
+Rpl_volume::compute_target_distance_limits(Volume* seg_vol, std::vector <double>& map_min_distance, std::vector <double>& map_max_distance)
+{
+    double threshold = .2;  //theshold for interpolated, segmented volume
+    d_ptr->aperture->allocate_aperture_images();
+
+    Volume::Pointer aperture_vol = d_ptr->aperture->get_aperture_volume ();
+    Proj_volume *proj_vol = d_ptr->proj_vol;
+    Volume *rvol = proj_vol->get_vol();
+    float *seg_img = (float*) seg_vol->img;
+
+    //const int *ires = proj_vol->get_image_dim();  //resolution of the 2-D proj vol aperture
+    int ires2[2];  //resolution of the output - user defined aperuture and segdepth_vol
+    ires2[0] = aperture_vol->dim[0];
+    ires2[1] = aperture_vol->dim[1];
+
+    //plm_long rijk[3]; /* Index with rvol */
+    double k = 0; /* index with rvol */
+    double seg_long_ray[3] = {0, 0, 0}; // vector in the rvol volume
+    float final_index[3]; //index of final vector
+
+    //Trilinear interpoloation variables
+    plm_long ijk_floor[3];  //floor of rounded
+    plm_long ijk_round[3];  //ceiling of rounded
+    float li_1[3], li_2[3]; //upper/lower fractions
+    plm_long idx_floor;
+
+    //Interpolated seg_volume value
+    double interp_seg_value;
+
+    double previous_depth; //previous wed depth
+    bool intersect_seg; //boolean that checks whether or not ray intersects with seg. volume
+    bool first_seg_check; //first point along a ray in the seg volume, to determine min energy
+
+    Ray_data *seg_ray;
+
+    for(int i = 0; i < ires2[0] * ires2[1]; i++)
+    {
+        map_min_distance.push_back(0);
+        map_max_distance.push_back(0);
+    }
+
+    for (int i = 0; i < ires2[0] * ires2[1]; i++)
+    {
+        seg_ray = &d_ptr->ray_data[i];
+        k = 0.;
+        vec3_copy(seg_long_ray, seg_ray->cp);
+
+        /* reset variables */
+        previous_depth = 0;
+        first_seg_check = true;
+        intersect_seg = false;
+
+        while (k < (double) rvol->dim[2])
+        {
+            if (k != 0)
+            {
+                vec3_add2(seg_long_ray, seg_ray->ray);
+            }
+
+            final_index[0] = (seg_long_ray[0]-seg_vol->origin[0])/seg_vol->spacing[0];
+            final_index[1] = (seg_long_ray[1]-seg_vol->origin[1])/seg_vol->spacing[1];
+            final_index[2] = (seg_long_ray[2]-seg_vol->origin[2])/seg_vol->spacing[2];
+			
+            li_clamp_3d (final_index, ijk_floor, ijk_round,li_1,li_2,seg_vol);
+            idx_floor = volume_index(seg_vol->dim, ijk_floor);
+            interp_seg_value = li_value(li_1[0], li_2[0],li_1[1], li_2[1],li_1[2], li_2[2],idx_floor,seg_img,seg_vol);
+
+            if (interp_seg_value > threshold)  
+            {
+                intersect_seg = true; //this ray intersects the segmentation volume
+
+                /* If point is within segmentation volume, set distance to the map_min matrix */	  
+                if (first_seg_check)  
+                {
+                    map_min_distance[i] = k;
+                    first_seg_check = false;
+                }
+                previous_depth = k;
+            }
+            else
+            {
+                if (intersect_seg)  
+                { 
+                    /* while we aren't currently in the seg. volume, this ray has been,
+                       so check if we just exited to set the max_seg_depth */
+                    if (previous_depth > 0)
+                    {
+                        map_max_distance[i] = previous_depth;
+                        previous_depth = 0;
+                    }
+                }
+            }
+            k++;
+        }
+    }
+    return;
+}
+
+void
+Rpl_volume::compute_target_distance_limits_slicerRt(Plm_image::Pointer& plmTgt, std::vector <double>& map_min_distance, std::vector <double>& map_max_distance)
+{
+    double threshold = .2;  //theshold for interpolated, segmented volume
+    d_ptr->aperture->allocate_aperture_images();
+
+    Volume::Pointer aperture_vol = d_ptr->aperture->get_aperture_volume ();
+    Proj_volume *proj_vol = d_ptr->proj_vol;
+    Volume *rvol = proj_vol->get_vol();
+    unsigned char* seg_img = (unsigned char*) plmTgt->get_volume_uchar()->img;
+
+    //const int *ires = proj_vol->get_image_dim();  //resolution of the 2-D proj vol aperture
+    int ires2[2];  //resolution of the output - user defined aperuture and segdepth_vol
+    ires2[0] = aperture_vol->dim[0];
+    ires2[1] = aperture_vol->dim[1];
+
+    //plm_long rijk[3]; /* Index with rvol */
+    double k = 0; /* index with rvol */
+    double seg_long_ray[3] = {0, 0, 0}; // vector in the rvol volume
+    float final_index[3]; //index of final vector
+
+    //Trilinear interpoloation variables
+    plm_long ijk_floor[3];  //floor of rounded
+    plm_long ijk_round[3];  //ceiling of rounded
+    float li_1[3], li_2[3]; //upper/lower fractions
+    plm_long idx_floor;
+    double A = 0; double B = 0; double C = 0; double D = 0; double E = 0; double F = 0; double G = 0; double H = 0;
+
+    //Interpolated seg_volume value
+    double interp_seg_value;
+
+    printf ("tgt dim = %d,%d,%d\n", plmTgt->dim(0), plmTgt->dim(1), plmTgt->dim(2));
+    printf ("tgt origin = %g,%g,%g\n", plmTgt->origin(0), plmTgt->origin(1), plmTgt->origin(2));
+    printf ("tgt spacing = %g,%g,%g\n", plmTgt->spacing(0), plmTgt->spacing(1), plmTgt->spacing(2));
+    fflush (stdout);
+    plm_long dim[3] = {(plm_long) plmTgt->dim(0), (plm_long) plmTgt->dim(1), (plm_long) plmTgt->dim(2)};
+
+    double previous_depth; //previous wed depth
+    bool intersect_seg; //boolean that checks whether or not ray intersects with seg. volume
+    bool first_seg_check; //first point along a ray in the seg volume, to determine min energy
+
+    Ray_data *seg_ray;
+
+    for(int i = 0; i < ires2[0] * ires2[1]; i++)
+    {
+        map_min_distance.push_back(0);
+        map_max_distance.push_back(0);
+    }
+
+    for (int i = 0; i < ires2[0] * ires2[1]; i++)
+    {
+        seg_ray = &d_ptr->ray_data[i];
+        k = 0.;
+        vec3_copy(seg_long_ray, seg_ray->cp);
+
+        /* reset variables */
+        previous_depth = 0;
+        first_seg_check = true;
+        intersect_seg = false;
+
+        while (k < (double) rvol->dim[2])
+        {
+            if (k != 0)
+            {
+                vec3_add2(seg_long_ray, seg_ray->ray);
+            }
+
+            final_index[0] = (seg_long_ray[0] - plmTgt->origin(0))/plmTgt->spacing(0);
+            final_index[1] = (seg_long_ray[1] - plmTgt->origin(1))/plmTgt->spacing(1);
+            final_index[2] = (seg_long_ray[2] - plmTgt->origin(2))/plmTgt->spacing(2);
+            if (final_index[0] < 0 || final_index[0] > (float) plmTgt->dim(0) || final_index[1] < 0 || final_index[1] > (float) plmTgt->dim(1) || final_index[2] < 0 || final_index[2] > (float) plmTgt->dim(2))
+            {
+                interp_seg_value = 0;
+            }
+            else
+            {
+                /* Li_clamp_3D */
+                li_clamp (final_index[0], plmTgt->dim(0)-1, &ijk_floor[0], &ijk_round[0], &li_1[0], &li_2[0]);
+                li_clamp (final_index[1], plmTgt->dim(1)-1, &ijk_floor[1], &ijk_round[1], &li_1[1], &li_2[1]);
+                li_clamp (final_index[2], plmTgt->dim(2)-1, &ijk_floor[2], &ijk_round[2], &li_1[2], &li_2[2]);
+			
+                idx_floor = volume_index (dim, ijk_floor);
+
+                /* li_value for Volume* */
+                A = li_1[0] * li_1[1] * li_1[2] * (double)seg_img[idx_floor];
+                B = li_2[0] * li_1[1] * li_1[2] * (double)seg_img[idx_floor+1];
+                C = li_1[0] * li_2[1] * li_1[2] * (double)seg_img[idx_floor+dim[0] ];
+                D = li_2[0] * li_2[1] * li_1[2] * (double)seg_img[idx_floor+dim[0]+1];
+                E = li_1[0] * li_1[1] * li_2[2] * (double)seg_img[idx_floor+dim[1]*dim[0] ];
+                F = li_2[0] * li_1[1] * li_2[2] * (double)seg_img[idx_floor+dim[1]*dim[0]+1];
+                G = li_1[0] * li_2[1] * li_2[2] * (double)seg_img[idx_floor+dim[1]*dim[0]+dim[0] ];
+                H = li_2[0] * li_2[1] * li_2[2] * (double)seg_img[idx_floor+dim[1]*dim[0]+dim[0]+1];
+                interp_seg_value = A + B + C + D + E + F + F + G + H;
+            }
+
+            if (interp_seg_value > threshold)  
+            {
+                intersect_seg = true; //this ray intersects the segmentation volume
+
+                /* If point is within segmentation volume, set distance to the map_min matrix */	  
+                if (first_seg_check)  
+                {
+                    map_min_distance[i] = k;
+                    first_seg_check = false;
+                }
+                previous_depth = k;
+            }
+            else
+            {
+                if (intersect_seg)  
+                { 
+                    /* while we aren't currently in the seg. volume, this ray has been,
+                       so check if we just exited to set the max_seg_depth */
+                    if (previous_depth > 0)
+                    {
+                        map_max_distance[i] = previous_depth;
+                        previous_depth = 0;
+                    }
+                }
+            }
+            k++;
+        }
+    }
+    return;
+}
+
 //20140827_YKP
 //col0 = HU, col1 = Relative stopping power
 //Table: XiO, ctedproton 2007 provided by Yoost
diff --git a/src/plastimatch/base/rpl_volume.h b/src/plastimatch/base/rpl_volume.h
index bf18093..02f1423 100644
--- a/src/plastimatch/base/rpl_volume.h
+++ b/src/plastimatch/base/rpl_volume.h
@@ -9,6 +9,10 @@
 #include "aperture.h"
 #include "plm_image.h"
 #include "ray_trace_callback.h"
+#include "smart_pointer.h"
+
+#define PMMA_DENSITY 1.19		// PMMA density in g
+#define PMMA_STPR 0.98		// PMMA Stopping Power Ratio, no dim
 
 PLMBASE_API float compute_PrSTPR_from_HU(float);
 PLMBASE_API float compute_PrSTPR_Schneider_weq_from_HU (float CT_HU); // Stopping Power Ratio - Schneider's model
@@ -28,11 +32,12 @@ class Volume_limit;
 class PLMBASE_API Rpl_volume 
 {
 public:
+    SMART_POINTER_SUPPORT (Rpl_volume);
+    Rpl_volume_private *d_ptr;
+public:
     Rpl_volume ();
     ~Rpl_volume ();
 public:
-    Rpl_volume_private *d_ptr;
-public:
     void set_geometry (
         const double src[3],           // position of source (mm)
         const double iso[3],           // position of isocenter (mm)
@@ -57,7 +62,6 @@ public:
     double get_rgdepth (int ap_ij[2], double dist);
     double get_rgdepth (double ap_ij[2], double dist);
     double get_rgdepth (const double *xyz);
-    double get_rgdepth2 (const double *xyz);
 
     void set_ct (const Plm_image::Pointer& ct_volume);
     Plm_image::Pointer get_ct();
@@ -69,44 +73,53 @@ public:
     double get_front_clipping_plane () const;
     void set_back_clipping_plane(double back_clip);
     double get_back_clipping_plane () const;
+    void set_minimum_distance_target(double min);
+    double get_minimum_distance_target();
 
     double get_max_wed ();
     double get_min_wed ();
 
-	void compute_rpl_ct_density (); // compute density volume
-	void compute_rpl_HU ();	// compute HU volume
+    void compute_rpl_ct_density (); // compute density volume
+    void compute_rpl_HU ();	// compute HU volume
     void compute_rpl_void ();	// compute void volume
 
-	void compute_rpl_range_length_rgc(); // range length volume creation taking into account the range compensator
-	void compute_rpl_PrSTRP_no_rgc (); // compute Proton Stopping Power Ratio volume without considering the range compensator
+    void compute_rpl_range_length_rgc(); // range length volume creation taking into account the range compensator
+    void compute_rpl_PrSTRP_no_rgc (); // compute Proton Stopping Power Ratio volume without considering the range compensator
 
     double compute_farthest_penetrating_ray_on_nrm(float range); // return the distance from aperture to the farthest which rg_lenght > range
 
     void compute_wed_volume (Volume *wed_vol, Volume *in_vol, float background);
     void compute_dew_volume (Volume *wed_vol, Volume *dew_vol, float background);
     void compute_proj_wed_volume (Volume *proj_wed_vol, float background);
-    void compute_beam_modifiers (Volume *seg_vol, float background);
-    void compute_aperture (Volume *tgt_vol, float background);
+    
+    void compute_beam_modifiers_passive_scattering (Volume *seg_vol);
+    void compute_beam_modifiers_active_scanning (Volume *seg_vol);
+    void compute_beam_modifiers_passive_scattering (Volume *seg_vol, float smearing, float proximal_margin, float distal_margin);
+    void compute_beam_modifiers_passive_scattering_slicerRt(Plm_image::Pointer& plmTgt, float smearing, float proximal_margin, float distal_margin);
+    void compute_beam_modifiers_active_scanning (Volume *seg_vol, float smearing, float proximal_margin, float distal_margin);
+    void compute_beam_modifiers_passive_scattering (Volume *seg_vol, float smearing, float proximal_margin, float distal_margin, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max); // returns also the wed max and min maps
+    void compute_beam_modifiers_active_scanning (Volume *seg_vol, float smearing, float proximal_margin, float distal_margin, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max); // returns also the wed max and min maps
 
-	void compute_volume_aperture(Aperture::Pointer ap);
+    void compute_volume_aperture(Aperture::Pointer ap);
 
     void apply_beam_modifiers ();
 
-    void save (const std::string& filename);
     void save (const char* filename);
+    void save (const std::string& filename);
+    void load_rpl (const char* filename);
+    void load_rpl (const std::string& filename);
+    void load_img (const char *filename);
+    void load_img (const std::string& filename);
 
     void compute_ray_data ();
+    void compute_beam_modifiers_core_slicerRt (Plm_image::Pointer& plmTgt, bool active, float smearing, float proximal_margin, float distal_margin, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max);
 
 protected:
+    void compute_beam_modifiers_core (Volume *seg_vol, bool active, float smearing, float proximal_margin, float distal_margin, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max);
+    void compute_target_distance_limits (Volume* seg_vol, std::vector <double>& map_min_distance, std::vector <double>& map_max_distance);
+    void compute_target_distance_limits_slicerRt (Plm_image::Pointer& plmTgt, std::vector <double>& map_min_distance, std::vector <double>& map_max_distance);
+    void apply_smearing_to_target (float smearing, std::vector <double>& map_min_distance, std::vector <double>& map_max_distance);
 
-    void aprc_ray_trace (
-        Volume *tgt_vol,             /* I: CT volume */
-        Ray_data *ray_data,          /* I: Pre-computed data for this ray */
-        Volume_limit *vol_limit,     /* I: CT bounding region */
-        const double *src,           /* I: @ source */
-        double rc_thk,               /* I: range compensator thickness */
-        int* ires                    /* I: ray cast resolution */
-    );
     void rpl_ray_trace (
         Volume *ct_vol,              /* I: CT volume */
         Ray_data *ray_data,          /* I: Pre-computed data for this ray */
@@ -116,6 +129,9 @@ protected:
         double rc_thk,               /* I: range compensator thickness */
         int* ires                    /* I: ray cast resolution */
     );
+protected:
+    void save_img (const char* filename);
+    void save_img (const std::string& filename);
 };
 
 #endif
diff --git a/src/plastimatch/base/rt_study.cxx b/src/plastimatch/base/rt_study.cxx
index 2cbdd24..0faeacb 100644
--- a/src/plastimatch/base/rt_study.cxx
+++ b/src/plastimatch/base/rt_study.cxx
@@ -249,6 +249,10 @@ Rt_study::load_xio (const char *xio_dir)
             d_ptr->m_drs->set_study_metadata (0x0010, 0x0020, 
                 demographic.m_patient_id);
         }
+        if (demographic.m_import_date != "") {
+            d_ptr->m_drs->set_study_date (demographic.m_import_date);
+            d_ptr->m_drs->set_study_time ("");
+        }
     }
 
     /* If referenced DICOM CT is provided,  the coordinates will be
@@ -426,6 +430,30 @@ Rt_study::save_dicom_dose (const char *dicom_dir)
 }
 
 void
+Rt_study::save_image (const std::string& fname)
+{
+    if (fname != "") {
+        d_ptr->m_img->save_image (fname);
+    }
+}
+
+void
+Rt_study::save_image (const char* fname)
+{
+    if (d_ptr->m_img) {
+        d_ptr->m_img->save_image (fname);
+    }
+}
+
+void
+Rt_study::save_image (const char* fname, Plm_image_type image_type)
+{
+    if (d_ptr->m_img) {
+        d_ptr->m_img->convert_and_save (fname, image_type);
+    }
+}
+
+void
 Rt_study::save_dose (const std::string& fname)
 {
     if (fname != "") {
@@ -470,28 +498,57 @@ Rt_study::get_rt_study_metadata ()
 }
 
 void 
-Rt_study::set_study_metadata (std::vector<std::string>& metadata)
+Rt_study::set_study_metadata (const std::vector<std::string>& metadata)
 {
     Metadata::Pointer& study_metadata = d_ptr->m_drs->get_study_metadata ();
+    study_metadata->set_metadata (metadata);
+    /* GCS FIX.  This is the wrong place for this. */
+    d_ptr->m_xio_transform->set (d_ptr->m_drs->get_image_metadata());
+}
 
-    std::vector<std::string>::iterator it = metadata.begin();
-    while (it < metadata.end()) {
-        const std::string& str = (*it);
-        size_t eq_pos = str.find_first_of ('=');
-        if (eq_pos != std::string::npos) {
-            std::string key = str.substr (0, eq_pos);
-            std::string val = str.substr (eq_pos+1);
-#if defined (commentout)
-            /* Set older-style metadata, used by gdcm */
-            d_ptr->m_meta->set_metadata (key, val);
-#endif
-            /* Set newer-style metadata, used by dcmtk */
-            study_metadata->set_metadata (key, val);
-        }
-        ++it;
-    }
+Metadata::Pointer&
+Rt_study::get_study_metadata (void)
+{
+    return d_ptr->m_drs->get_study_metadata();
+}
 
-    d_ptr->m_xio_transform->set (d_ptr->m_drs->get_image_metadata());
+void 
+Rt_study::set_image_metadata (const std::vector<std::string>& metadata)
+{
+    Metadata::Pointer& image_metadata = d_ptr->m_drs->get_image_metadata ();
+    image_metadata->set_metadata (metadata);
+}
+
+Metadata::Pointer&
+Rt_study::get_image_metadata (void)
+{
+    return d_ptr->m_drs->get_image_metadata();
+}
+
+void 
+Rt_study::set_dose_metadata (const std::vector<std::string>& metadata)
+{
+    Metadata::Pointer& dose_metadata = d_ptr->m_drs->get_dose_metadata ();
+    dose_metadata->set_metadata (metadata);
+}
+
+Metadata::Pointer&
+Rt_study::get_dose_metadata (void)
+{
+    return d_ptr->m_drs->get_dose_metadata();
+}
+
+void 
+Rt_study::set_rtss_metadata (const std::vector<std::string>& metadata)
+{
+    Metadata::Pointer& rtss_metadata = d_ptr->m_drs->get_rtss_metadata ();
+    rtss_metadata->set_metadata (metadata);
+}
+
+Metadata::Pointer&
+Rt_study::get_rtss_metadata (void)
+{
+    return d_ptr->m_drs->get_rtss_metadata();
 }
 
 bool
@@ -613,12 +670,6 @@ Rt_study::get_xio_dose_filename (void) const
     return d_ptr->m_xio_dose_filename;
 }
 
-Metadata::Pointer&
-Rt_study::get_metadata (void)
-{
-    return d_ptr->m_drs->get_study_metadata();
-}
-
 Volume::Pointer
 Rt_study::get_image_volume_short ()
 {
diff --git a/src/plastimatch/base/rt_study.h b/src/plastimatch/base/rt_study.h
index 5c103c0..a177d14 100644
--- a/src/plastimatch/base/rt_study.h
+++ b/src/plastimatch/base/rt_study.h
@@ -17,7 +17,6 @@ class Metadata;
 class Plm_image;
 class Rt_plan;
 class Rt_study_private;
-class Slice_index;
 class Volume;
 class Xio_ct_transform;
 
@@ -64,6 +63,10 @@ public:
         bool filenames_with_uid = true);
     void save_dicom_dose (const char *output_dir);
 
+    void save_image (const std::string& fname);
+    void save_image (const char* fname);
+    void save_image (const char* fname, Plm_image_type image_type);
+
     void save_dose (const std::string& fname);
     void save_dose (const char* fname);
     void save_dose (const char* fname, Plm_image_type image_type);
@@ -71,10 +74,25 @@ public:
     void save_prefix (const std::string& output_prefix, 
         const std::string& extension = "mha");
 
+    /*! \brief Get the Rt_study_metadata */
     const Rt_study_metadata::Pointer& get_rt_study_metadata () const;
     Rt_study_metadata::Pointer& get_rt_study_metadata ();
-    void set_study_metadata (std::vector<std::string>& metadata);
-    Metadata::Pointer& get_metadata ();
+    /*! \brief Set metadata items into study_metadata portion of Rt_study_metadata */
+    void set_study_metadata (const std::vector<std::string>& metadata);
+    /*! \brief Get the study_metadata portion of Rt_study_metadata */
+    Metadata::Pointer& get_study_metadata ();
+    /*! \brief Set metadata items into image portion of Rt_study_metadata */
+    void set_image_metadata (const std::vector<std::string>& metadata);
+    /*! \brief Get the image portion of Rt_study_metadata */
+    Metadata::Pointer& get_image_metadata ();
+    /*! \brief Set metadata items into dose portion of Rt_study_metadata */
+    void set_dose_metadata (const std::vector<std::string>& metadata);
+    /*! \brief Get the dose portion of Rt_study_metadata */
+    Metadata::Pointer& get_dose_metadata ();
+    /*! \brief Set metadata items into rtss portion of Rt_study_metadata */
+    void set_rtss_metadata (const std::vector<std::string>& metadata);
+    /*! \brief Get the rtss portion of Rt_study_metadata */
+    Metadata::Pointer& get_rtss_metadata ();
 
     bool have_image ();
     void set_image (ShortImageType::Pointer& itk_image);
diff --git a/src/plastimatch/base/rt_study_metadata.cxx b/src/plastimatch/base/rt_study_metadata.cxx
index e9babf8..4b299ab 100755
--- a/src/plastimatch/base/rt_study_metadata.cxx
+++ b/src/plastimatch/base/rt_study_metadata.cxx
@@ -16,14 +16,16 @@ class Rt_study_metadata_private {
 public:
     std::string date_string;
     std::string time_string;
+
+    std::string study_uid;
+    std::string for_uid;
+
     std::string ct_series_uid;
     std::string dose_instance_uid;
     std::string dose_series_uid;
-    std::string for_uid;
     std::string plan_instance_uid;
     std::string rtss_instance_uid;
     std::string rtss_series_uid;
-    std::string study_uid;
     Slice_list slice_list;
 
     Metadata::Pointer study_metadata;
@@ -34,14 +36,6 @@ public:
 public:
     Rt_study_metadata_private () {
         dicom_get_date_time (&date_string, &time_string);
-        study_uid = dicom_uid (PLM_UID_PREFIX);
-        for_uid = dicom_uid (PLM_UID_PREFIX);
-        ct_series_uid = dicom_uid (PLM_UID_PREFIX);
-        plan_instance_uid = dicom_uid (PLM_UID_PREFIX);
-        rtss_instance_uid = dicom_uid (PLM_UID_PREFIX);
-        rtss_series_uid = dicom_uid (PLM_UID_PREFIX);
-        dose_instance_uid = dicom_uid (PLM_UID_PREFIX);
-        dose_series_uid = dicom_uid (PLM_UID_PREFIX);
 
         study_metadata = Metadata::New ();
         image_metadata = Metadata::New ();
@@ -52,18 +46,24 @@ public:
         image_metadata->set_parent (study_metadata);
         rtss_metadata->set_parent (study_metadata);
         dose_metadata->set_parent (study_metadata);
+
+        this->generate_new_study_uids ();
+        this->generate_new_series_uids ();
     }
 public:
     void
-    generate_new_uids () {
+    generate_new_study_uids () {
         study_uid = dicom_uid (PLM_UID_PREFIX);
         for_uid = dicom_uid (PLM_UID_PREFIX);
+    }
+    void
+    generate_new_series_uids () {
         ct_series_uid = dicom_uid (PLM_UID_PREFIX);
+        dose_instance_uid = dicom_uid (PLM_UID_PREFIX);
+        dose_series_uid = dicom_uid (PLM_UID_PREFIX);
         plan_instance_uid = dicom_uid (PLM_UID_PREFIX);
         rtss_instance_uid = dicom_uid (PLM_UID_PREFIX);
         rtss_series_uid = dicom_uid (PLM_UID_PREFIX);
-        dose_instance_uid = dicom_uid (PLM_UID_PREFIX);
-        dose_series_uid = dicom_uid (PLM_UID_PREFIX);
     }
 };
 
@@ -160,6 +160,12 @@ Rt_study_metadata::set_study_date (const char* date)
     d_ptr->date_string = date;
 }
 
+void
+Rt_study_metadata::set_study_date (const std::string& date)
+{
+    d_ptr->date_string = date;
+}
+
 const char*
 Rt_study_metadata::get_study_time () const
 {
@@ -173,6 +179,12 @@ Rt_study_metadata::set_study_time (const char* time)
     d_ptr->time_string = time;
 }
 
+void
+Rt_study_metadata::set_study_time (const std::string& time)
+{
+    d_ptr->time_string = time;
+}
+
 const char*
 Rt_study_metadata::get_study_uid () const
 {
@@ -310,6 +322,23 @@ Rt_study_metadata::get_image_metadata () const
     return d_ptr->image_metadata;
 }
 
+const std::string& 
+Rt_study_metadata::get_image_metadata (
+    unsigned short key1, 
+    unsigned short key2
+) {
+    return d_ptr->image_metadata->get_metadata (key1, key2);
+}
+
+void
+Rt_study_metadata::set_image_metadata (
+    unsigned short key1, 
+    unsigned short key2,
+    const std::string& val
+) {
+    d_ptr->image_metadata->set_metadata (key1, key2, val);
+}
+
 Metadata::Pointer&
 Rt_study_metadata::get_rtss_metadata ()
 {
@@ -322,6 +351,15 @@ Rt_study_metadata::get_rtss_metadata () const
     return d_ptr->rtss_metadata;
 }
 
+void
+Rt_study_metadata::set_rtss_metadata (
+    unsigned short key1, 
+    unsigned short key2,
+    const std::string& val
+) {
+    d_ptr->rtss_metadata->set_metadata (key1, key2, val);
+}
+
 Metadata::Pointer&
 Rt_study_metadata::get_dose_metadata ()
 {
@@ -335,7 +373,22 @@ Rt_study_metadata::get_dose_metadata () const
 }
 
 void
-Rt_study_metadata::generate_new_uids () 
+Rt_study_metadata::set_dose_metadata (
+    unsigned short key1, 
+    unsigned short key2,
+    const std::string& val
+) {
+    d_ptr->dose_metadata->set_metadata (key1, key2, val);
+}
+
+void
+Rt_study_metadata::generate_new_study_uids () 
+{
+    d_ptr->generate_new_study_uids ();
+}
+
+void
+Rt_study_metadata::generate_new_series_uids () 
 {
-    d_ptr->generate_new_uids ();
+    d_ptr->generate_new_series_uids ();
 }
diff --git a/src/plastimatch/base/rt_study_metadata.h b/src/plastimatch/base/rt_study_metadata.h
index 972197a..e262cf0 100755
--- a/src/plastimatch/base/rt_study_metadata.h
+++ b/src/plastimatch/base/rt_study_metadata.h
@@ -18,6 +18,12 @@ class Plm_image_header;
 class Slice_list;
 class Volume;
 
+/*! \brief 
+ * The Rt_study_metadata encapsulate DICOM metadata for an Rt_study. 
+ * The Rt_study_metadata includes separate metadata for image, dose, 
+ * and structure set (rtss), as well as a study_metadata which is 
+ * shared by all components.  Items such as Patient Name or Study Description
+ * will be held in study_metadata. */
 class PLMBASE_API Rt_study_metadata {
 public:
     SMART_POINTER_SUPPORT (Rt_study_metadata);
@@ -41,8 +47,10 @@ public:
     const char* get_rtss_series_uid () const;
     const char* get_study_date () const;
     void set_study_date (const char* date);
+    void set_study_date (const std::string& date);
     const char* get_study_time () const;
     void set_study_time (const char* time);
+    void set_study_time (const std::string& time);
     const char* get_study_uid () const;
     void set_study_uid (const char* uid);
     const std::string& get_patient_name ();
@@ -69,11 +77,20 @@ public:
         const std::string& val);
     Metadata::Pointer& get_image_metadata ();
     const Metadata::Pointer& get_image_metadata () const;
+    const std::string& get_image_metadata (unsigned short key1, 
+        unsigned short key2);
+    void set_image_metadata (unsigned short key1, unsigned short key2,
+        const std::string& val);
     Metadata::Pointer& get_rtss_metadata ();
     const Metadata::Pointer& get_rtss_metadata () const;
+    void set_rtss_metadata (unsigned short key1, unsigned short key2,
+        const std::string& val);
     Metadata::Pointer& get_dose_metadata ();
     const Metadata::Pointer& get_dose_metadata () const;
-    void generate_new_uids ();
+    void set_dose_metadata (unsigned short key1, unsigned short key2,
+        const std::string& val);
+    void generate_new_study_uids ();
+    void generate_new_series_uids ();
 };
 
 #endif
diff --git a/src/plastimatch/base/rtog_io.cxx b/src/plastimatch/base/rtog_io.cxx
index 16a40fa..e5a28a7 100644
--- a/src/plastimatch/base/rtog_io.cxx
+++ b/src/plastimatch/base/rtog_io.cxx
@@ -47,16 +47,8 @@
 #include <string.h>
 #include <math.h>
 
-#if (defined(_WIN32) || defined(WIN32))
-#include <direct.h>
-#define snprintf _snprintf
-#define mkdir(a,b) _mkdir(a)
-#else
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
 #include "exchkeys.h"
+#include "file_util.h"
 
 #define BUFLEN 2048
 
@@ -613,7 +605,7 @@ load_ct (RTOG_Header* rtog_header, Program_Parms* parms)
 void
 make_output_dir (Program_Parms* parms)
 {
-    mkdir (parms->outdir, 0777);
+    make_directory_recursive (parms->outdir);
 }
 
 void
diff --git a/src/plastimatch/base/rtss.cxx b/src/plastimatch/base/rtss.cxx
index ba653a7..0dfd076 100644
--- a/src/plastimatch/base/rtss.cxx
+++ b/src/plastimatch/base/rtss.cxx
@@ -9,6 +9,7 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "logfile.h"
 #include "plm_image_header.h"
 #include "plm_int.h"
 #include "plm_math.h"
@@ -199,21 +200,21 @@ Rtss::debug (void)
     Rtss_roi* curr_structure;
 
     if (this->have_geometry) {
-	printf ("rps::dim = %u %u %u\n", 
+	lprintf ("rps::dim = %u %u %u\n", 
 	    (unsigned int) this->m_dim[0], 
 	    (unsigned int) this->m_dim[1], 
 	    (unsigned int) this->m_dim[2]);
-	printf ("rps::offset = %g %g %g\n", 
+	lprintf ("rps::offset = %g %g %g\n", 
 	    this->m_offset[0], this->m_offset[1], this->m_offset[2]);
-	printf ("rps::spacing = %g %g %g\n", 
+	lprintf ("rps::spacing = %g %g %g\n", 
 	    this->m_spacing[0], this->m_spacing[1], this->m_spacing[2]);
     } else {
-	printf ("rps has no geometry\n");
+	lprintf ("rps has no geometry\n");
     }
 
     for (size_t i = 0; i < this->num_structures; i++) {
         curr_structure = this->slist[i];
-	printf ("%u %d %s [%s] (%p) (%d contours)", 
+	lprintf ("%u %d %s [%s] (%p) (%d contours)", 
 	    (unsigned int) i, 
 	    curr_structure->id, 
 	    curr_structure->name.c_str(), 
@@ -224,15 +225,15 @@ Rtss::debug (void)
 	);
 	if (curr_structure->num_contours) {
 	    if (curr_structure->pslist[0]->num_vertices) {
-		printf (" [%f,%f,%f,...]",
+		lprintf (" [%f,%f,%f,...]",
 		    curr_structure->pslist[0]->x[0],
 		    curr_structure->pslist[0]->y[0],
 		    curr_structure->pslist[0]->z[0]);
 	    } else {
-		printf (" <no vertices>");
+		lprintf (" <no vertices>");
 	    }
 	}
-	printf ("\n");
+	lprintf ("\n");
     }
 }
 
@@ -415,8 +416,8 @@ Rtss::find_rasterization_geometry (
 	    have_spacing = 1;
 	} else {
 	    if (fabs (diff - z_spacing) > SPACING_TOL) {
-		printf ("Warning, slice spacing of RTSS may be unequal\n");
-		printf ("%g - %g = %g vs. %g\n", 
+		lprintf ("Warning, slice spacing of RTSS may be unequal\n");
+		lprintf ("%g - %g = %g vs. %g\n", 
 		    this_z, last_z, diff, z_spacing);
 	    }
 	}
@@ -456,13 +457,13 @@ Rtss::set_rasterization_geometry (void)
 	this->rast_dim,
         this->rast_dc
     );
-    printf ("rast_dim = %u %u %u\n", 
+    lprintf ("rast_dim = %u %u %u\n", 
 	(unsigned int) this->rast_dim[0], 
 	(unsigned int) this->rast_dim[1], 
 	(unsigned int) this->rast_dim[2]);
-    printf ("rast_offset = %g %g %g\n", 
+    lprintf ("rast_offset = %g %g %g\n", 
 	this->rast_offset[0], this->rast_offset[1], this->rast_offset[2]);
-    printf ("rast_spacing = %g %g %g\n", 
+    lprintf ("rast_spacing = %g %g %g\n", 
 	this->rast_spacing[0], this->rast_spacing[1], this->rast_spacing[2]);
 }
 
@@ -481,11 +482,9 @@ Rtss::apply_slice_list (const Slice_list *slice_list)
 
     const Plm_image_header *pih = slice_list->get_image_header ();
     /* Geometry */
-    for (int d = 0; d < 3; d++) {
-        this->m_offset[d] = pih->m_origin[d];
-        this->m_dim[d] = pih->Size(d);
-        this->m_spacing[d] = pih->m_spacing[d];
-    }
+    pih->get_dim (this->m_dim);
+    pih->get_origin (this->m_offset);
+    pih->get_spacing (this->m_spacing);
 
     /* Slice numbers and slice uids */
     for (size_t i = 0; i < this->num_structures; i++) {
@@ -561,7 +560,7 @@ void
 Rtss::keyholize (void)
 {
 #if defined (PLM_CONFIG_KEYHOLIZE)
-    printf ("Keyholizing...\n");
+    lprintf ("Keyholizing...\n");
 
     /* Loop through structures */
     for (int i = 0; i < this->num_structures; i++) {
@@ -597,11 +596,11 @@ Rtss::keyholize (void)
 	    }
 
 	    /* We have now found a group */
-	    printf ("Keyholizing group:");
+	    lprintf ("Keyholizing group:");
 	    for (unsigned int k = 0; k < group_contours.size(); k++) {
-		printf (" %d", group_contours[k]);
+		lprintf (" %d", group_contours[k]);
 	    }
-	    printf ("\n");
+	    lprintf ("\n");
 
 	    /* Find an outermost contour in group */
 	    int cidx_xmin = -1;
diff --git a/src/plastimatch/base/rtss_contour.h b/src/plastimatch/base/rtss_contour.h
index e8b4317..8fd5e45 100644
--- a/src/plastimatch/base/rtss_contour.h
+++ b/src/plastimatch/base/rtss_contour.h
@@ -11,7 +11,7 @@ class PLMBASE_API Rtss_contour {
 public:
     int slice_no;           /* Can be "-1" */
     std::string ct_slice_uid;
-    int num_vertices;
+    size_t num_vertices;
     float* x;
     float* y;
     float* z;
diff --git a/src/plastimatch/base/rtss_roi.cxx b/src/plastimatch/base/rtss_roi.cxx
index 1df2227..b0b5e8d 100644
--- a/src/plastimatch/base/rtss_roi.cxx
+++ b/src/plastimatch/base/rtss_roi.cxx
@@ -55,6 +55,19 @@ Rtss_roi::add_polyline ()
     return new_polyline;
 }
 
+Rtss_contour*
+Rtss_roi::add_polyline (size_t num_vertices)
+{
+    Rtss_contour* rtss_contour = this->add_polyline();
+    rtss_contour->num_vertices = num_vertices;
+    rtss_contour->slice_no = -1;
+    rtss_contour->ct_slice_uid = "";
+    rtss_contour->x = (float*) malloc (num_vertices * sizeof(float));
+    rtss_contour->y = (float*) malloc (num_vertices * sizeof(float));
+    rtss_contour->z = (float*) malloc (num_vertices * sizeof(float));
+    return rtss_contour;
+}
+
 std::string
 Rtss_roi::adjust_name (const std::string& name_in)
 {
diff --git a/src/plastimatch/base/rtss_roi.h b/src/plastimatch/base/rtss_roi.h
index aacd9a8..daf7c38 100644
--- a/src/plastimatch/base/rtss_roi.h
+++ b/src/plastimatch/base/rtss_roi.h
@@ -23,6 +23,7 @@ public:
 
     void clear ();
     Rtss_contour* add_polyline ();
+    Rtss_contour* add_polyline (size_t num_vertices);
     void set_color (const char* color_string);
     std::string get_dcm_color_string () const;
     void get_rgb (int *r, int *g, int *b) const;
diff --git a/src/plastimatch/base/segmentation.cxx b/src/plastimatch/base/segmentation.cxx
index a508123..4258513 100644
--- a/src/plastimatch/base/segmentation.cxx
+++ b/src/plastimatch/base/segmentation.cxx
@@ -41,7 +41,7 @@ class Segmentation_private {
 public:
     Plm_image::Pointer m_labelmap; /* Structure set lossy bitmap form */
     Plm_image::Pointer m_ss_img;   /* Structure set in lossless bitmap form */
-    Rtss::Pointer m_cxt;        /* Structure set in polyline form */
+    Rtss::Pointer m_rtss;          /* Structure set in polyline form */
 
     bool m_rtss_valid;
     bool m_ss_img_valid;
@@ -82,7 +82,7 @@ Segmentation::~Segmentation ()
 void
 Segmentation::clear ()
 {
-    d_ptr->m_cxt.reset();
+    d_ptr->m_rtss.reset();
     d_ptr->m_ss_img.reset();
     d_ptr->m_labelmap.reset();
     d_ptr->m_rtss_valid = false;
@@ -101,16 +101,16 @@ Segmentation::load (const char *ss_img, const char *ss_list)
     }
 
     /* Load ss_list */
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt.reset();
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss.reset();
     }
     if (ss_list && file_exists (ss_list)) {
         lprintf ("Trying to load ss_list: %s\n", ss_list);
-        d_ptr->m_cxt.reset (ss_list_load (0, ss_list));
+        d_ptr->m_rtss.reset (ss_list_load (0, ss_list));
     }
 
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt->free_all_polylines ();
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss->free_all_polylines ();
     }
     d_ptr->m_rtss_valid = false;
     d_ptr->m_ss_img_valid = true;
@@ -180,15 +180,16 @@ Segmentation::load_prefix (const char *prefix_dir)
 
             first = false;
         } else {
+            ss_img_pih.print();
             if (!Plm_image_header::compare (&pih, &ss_img_pih)) {
-                print_and_exit ("Image size mismatch when loading prefix_dir");
+                print_and_exit ("Image size mismatch when loading prefix_dir\n");
             }
         }
 
         /* Add name to ss_list */
-        d_ptr->m_cxt->add_structure (
+        d_ptr->m_rtss->add_structure (
             structure_name, "", 
-            d_ptr->m_cxt->num_structures + 1,
+            d_ptr->m_rtss->num_structures + 1,
             bit);
         free (structure_name);
 
@@ -197,7 +198,7 @@ Segmentation::load_prefix (const char *prefix_dir)
         unsigned int bit_no = bit % 8;
         unsigned char bit_mask = 1 << bit_no;
         if (uchar_no > ss_img->GetVectorLength()) {
-            print_and_exit ("Error.  Ss_img vector is too small.");
+            print_and_exit ("Error.  Ss_img vector is too small.\n");
         }
 
         /* Set up iterators for looping through images */
@@ -232,8 +233,8 @@ Segmentation::load_prefix (const char *prefix_dir)
         bit++;
     }
 
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt->free_all_polylines ();
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss->free_all_polylines ();
     }
     d_ptr->m_rtss_valid = false;
     d_ptr->m_ss_img_valid = true;
@@ -256,7 +257,7 @@ Segmentation::add_structure (
         /* Make sure image size is the same */
         Plm_image_header ss_img_pih (d_ptr->m_ss_img);
         if (!Plm_image_header::compare (&pih, &ss_img_pih)) {
-            print_and_exit ("Image size mismatch when adding structure");
+            print_and_exit ("Image size mismatch when adding structure\n");
         }
     }
 
@@ -267,63 +268,61 @@ Segmentation::add_structure (
     if (!structure_color) {
         structure_color = "";
     }
-    int bit = d_ptr->m_cxt->num_structures; /* GCS FIX: I hope this is ok */
+    int bit = d_ptr->m_rtss->num_structures; /* GCS FIX: I hope this is ok */
 
     /* Add structure to rtss */
-    d_ptr->m_cxt->add_structure (
+    d_ptr->m_rtss->add_structure (
         structure_name, structure_color,
-        d_ptr->m_cxt->num_structures + 1,
+        d_ptr->m_rtss->num_structures + 1,
         bit);
 
-#if defined (commentout)
-    /* Expand vector length if needed */
-    UCharVecImageType::Pointer ss_img = d_ptr->m_ss_img->itk_uchar_vec ();
-    if (uchar_no > ss_img->GetVectorLength()) {
-        this->broaden_ss_image (uchar_no);
-    }
-
-    /* Set up iterators for looping through images */
-    typedef itk::ImageRegionConstIterator< UCharImageType > 
-        UCharIteratorType;
-    typedef itk::ImageRegionIterator< UCharVecImageType > 
-        UCharVecIteratorType;
-    UCharIteratorType uchar_img_it (itk_image, 
-        itk_image->GetLargestPossibleRegion());
-    UCharVecIteratorType ss_img_it (ss_img, 
-        ss_img->GetLargestPossibleRegion());
-
-    /* Loop through voxels, or'ing them into ss_img */
-    /* GCS FIX: This is inefficient, due to undesirable construct 
-       and destruct of itk::VariableLengthVector of each pixel */
-    for (uchar_img_it.GoToBegin(), ss_img_it.GoToBegin();
-        !uchar_img_it.IsAtEnd();
-        ++uchar_img_it, ++ss_img_it
-    ) {
-        unsigned char u = uchar_img_it.Get ();
-        if (!u) continue;
-
-        itk::VariableLengthVector<unsigned char> v 
-            = ss_img_it.Get ();
-        v[uchar_no] |= bit_mask;
-        ss_img_it.Set (v);
-    }
-#endif
-
     /* Set bit within ss_img */
     this->set_structure_image (itk_image, bit);
 
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt->free_all_polylines ();
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss->free_all_polylines ();
     }
     d_ptr->m_rtss_valid = false;
     d_ptr->m_ss_img_valid = true;
 }
 
+Rtss_roi *
+Segmentation::add_rtss_roi (
+    const char *structure_name,
+    const char *structure_color)
+{
+    /* Allocate rtss if first time called */
+    if (!d_ptr->m_rtss_valid) {
+        /* GCS FIX: In principle, I should convert existing ss_image 
+           planes into rtss format first */
+        d_ptr->m_rtss = Rtss::New();
+        d_ptr->m_ss_img = Plm_image::Pointer();
+        d_ptr->m_rtss_valid = true;
+        d_ptr->m_ss_img_valid = false;
+    }
+
+    /* Figure out basic structure info */
+    if (!structure_name) {
+        structure_name = "";
+    }
+    if (!structure_color) {
+        structure_color = "";
+    }
+    int bit = d_ptr->m_rtss->num_structures;
+
+    /* Add structure to rtss */
+    Rtss_roi *rtss_roi = d_ptr->m_rtss->add_structure (
+        structure_name, structure_color,
+        d_ptr->m_rtss->num_structures + 1,
+        bit);
+    return rtss_roi;
+}
+
 void
 Segmentation::load_cxt (const std::string& input_fn, Rt_study_metadata *rsm)
 {
-    d_ptr->m_cxt = Rtss::New();
-    cxt_load (d_ptr->m_cxt.get(), rsm, input_fn.c_str());
+    d_ptr->m_rtss = Rtss::New();
+    cxt_load (d_ptr->m_rtss.get(), rsm, input_fn.c_str());
 
     d_ptr->m_rtss_valid = true;
     d_ptr->m_ss_img_valid = false;
@@ -333,8 +332,8 @@ void
 Segmentation::load_gdcm_rtss (const char *input_fn, Rt_study_metadata *rsm)
 {
 #if PLM_DCM_USE_GDCM1
-    d_ptr->m_cxt = Rtss::New();
-    gdcm_rtss_load (d_ptr->m_cxt.get(), rsm, input_fn);
+    d_ptr->m_rtss = Rtss::New();
+    gdcm_rtss_load (d_ptr->m_rtss.get(), rsm, input_fn);
 
     d_ptr->m_rtss_valid = true;
     d_ptr->m_ss_img_valid = false;
@@ -344,9 +343,9 @@ Segmentation::load_gdcm_rtss (const char *input_fn, Rt_study_metadata *rsm)
 void
 Segmentation::load_xio (const Xio_studyset& studyset)
 {
-    d_ptr->m_cxt = Rtss::New();
+    d_ptr->m_rtss = Rtss::New();
     lprintf ("calling xio_structures_load\n");
-    xio_structures_load (d_ptr->m_cxt.get(), studyset);
+    xio_structures_load (d_ptr->m_rtss.get(), studyset);
 
     d_ptr->m_rtss_valid = true;
     d_ptr->m_ss_img_valid = false;
@@ -355,8 +354,8 @@ Segmentation::load_xio (const Xio_studyset& studyset)
 size_t
 Segmentation::get_num_structures ()
 {
-    if (d_ptr->m_cxt) {
-        return d_ptr->m_cxt->num_structures;
+    if (d_ptr->m_rtss) {
+        return d_ptr->m_rtss->num_structures;
     }
     return 0;
 }
@@ -364,8 +363,8 @@ Segmentation::get_num_structures ()
 std::string
 Segmentation::get_structure_name (size_t index)
 {
-    if (d_ptr->m_cxt) {
-        return d_ptr->m_cxt->get_structure_name (index);
+    if (d_ptr->m_rtss) {
+        return d_ptr->m_rtss->get_structure_name (index);
     }
     return 0;
 }
@@ -373,10 +372,10 @@ Segmentation::get_structure_name (size_t index)
 void 
 Segmentation::set_structure_name (size_t index, const std::string& name)
 {
-    if (!d_ptr->m_cxt) {
+    if (!d_ptr->m_rtss) {
         return;
     }
-    d_ptr->m_cxt->set_structure_name (index, name);
+    d_ptr->m_rtss->set_structure_name (index, name);
 }
 
 UCharImageType::Pointer
@@ -387,12 +386,12 @@ Segmentation::get_structure_image (int index)
             "Error extracting unknown structure image (no ssi %d)\n", index);
     }
 
-    if (!d_ptr->m_cxt) {
+    if (!d_ptr->m_rtss) {
         print_and_exit (
             "Error extracting unknown structure image (no cxt %d)\n", index);
     }
 
-    Rtss_roi *curr_structure = d_ptr->m_cxt->slist[index];
+    Rtss_roi *curr_structure = d_ptr->m_rtss->slist[index];
     int bit = curr_structure->bit;
 
     if (bit == -1) {
@@ -408,7 +407,7 @@ Segmentation::get_structure_image (int index)
 void
 Segmentation::save_colormap (const std::string& colormap_fn)
 {
-    ss_list_save_colormap (d_ptr->m_cxt.get(), colormap_fn.c_str());
+    ss_list_save_colormap (d_ptr->m_rtss.get(), colormap_fn.c_str());
 }
 
 void
@@ -418,7 +417,7 @@ Segmentation::save_cxt (
     bool prune_empty
 )
 {
-    cxt_save (d_ptr->m_cxt.get(), rsm, cxt_fn.c_str(), prune_empty);
+    cxt_save (d_ptr->m_rtss.get(), rsm, cxt_fn.c_str(), prune_empty);
 }
 
 void
@@ -432,11 +431,11 @@ Segmentation::save_gdcm_rtss (
     /* Perform destructive keyholization of the cxt.  This is necessary 
        because DICOM-RT requires that structures with holes be defined 
        using a single structure */
-    d_ptr->m_cxt->keyholize ();
+    d_ptr->m_rtss->keyholize ();
 
     /* Some systems (GE ADW) do not allow special characters in 
        structure names.  */
-    d_ptr->m_cxt->adjust_structure_names ();
+    d_ptr->m_rtss->adjust_structure_names ();
 
     if (rsm) {
         this->apply_dicom_dir (rsm);
@@ -445,7 +444,7 @@ Segmentation::save_gdcm_rtss (
     fn = string_format ("%s/%s", output_dir, "rtss.dcm");
 
 #if PLM_DCM_USE_GDCM1
-    gdcm_rtss_save (d_ptr->m_cxt.get(), rsm, fn.c_str());
+    gdcm_rtss_save (d_ptr->m_rtss.get(), rsm, fn.c_str());
 #else
     /* GDCM 2 not implemented -- you're out of luck. */
 #endif
@@ -473,14 +472,14 @@ Segmentation::save_fcsv (
 void
 Segmentation::save_prefix_fcsv (const std::string& output_prefix)
 {
-    if (!d_ptr->m_cxt) {
+    if (!d_ptr->m_rtss) {
         print_and_exit (
             "Error: save_prefix_fcsv() tried to save a RTSS without a CXT\n");
     }
 
-    for (size_t i = 0; i < d_ptr->m_cxt->num_structures; i++)
+    for (size_t i = 0; i < d_ptr->m_rtss->num_structures; i++)
     {
-        Rtss_roi *curr_structure = d_ptr->m_cxt->slist[i];
+        Rtss_roi *curr_structure = d_ptr->m_rtss->slist[i];
 
         std::string fn = 
             compose_prefix_fn (output_prefix, curr_structure->name, "fcsv");
@@ -493,7 +492,7 @@ Segmentation::save_ss_image (const std::string& ss_img_fn)
 {
     if (!d_ptr->m_ss_img) {
         print_and_exit (
-            "Error: save_ss_image() tried to write a non-existant ss_img");
+            "Error: save_ss_image() tried to write a non-existant ss_img\n");
     }
     if (d_ptr->m_ss_img->m_type == PLM_IMG_TYPE_GPUIT_UCHAR_VEC
         || d_ptr->m_ss_img->m_type == PLM_IMG_TYPE_ITK_UCHAR_VEC) 
@@ -523,14 +522,14 @@ Segmentation::save_prefix (const std::string &output_prefix,
         return;
     }
 
-    if (!d_ptr->m_cxt) {
+    if (!d_ptr->m_rtss) {
         printf ("WTF???\n");
     }
 
-    for (size_t i = 0; i < d_ptr->m_cxt->num_structures; i++)
+    for (size_t i = 0; i < d_ptr->m_rtss->num_structures; i++)
     {
         std::string fn;
-        Rtss_roi *curr_structure = d_ptr->m_cxt->slist[i];
+        Rtss_roi *curr_structure = d_ptr->m_rtss->slist[i];
         int bit = curr_structure->bit;
 
         if (bit == -1) continue;
@@ -554,7 +553,7 @@ Segmentation::save_prefix (const char *output_prefix)
 void
 Segmentation::save_ss_list (const std::string& ss_list_fn)
 {
-    ss_list_save (d_ptr->m_cxt.get(), ss_list_fn.c_str());
+    ss_list_save (d_ptr->m_rtss.get(), ss_list_fn.c_str());
 }
 
 void
@@ -565,7 +564,7 @@ Segmentation::save_xio (
     const std::string &output_dir
 )
 {
-    xio_structures_save (rsm, d_ptr->m_cxt.get(), xio_transform,
+    xio_structures_save (rsm, d_ptr->m_rtss.get(), xio_transform,
         xio_version, output_dir.c_str());
 }
 
@@ -592,7 +591,7 @@ Segmentation::get_ss_img_uchar_vec (void)
 void
 Segmentation::apply_dicom_dir (const Rt_study_metadata::Pointer& rsm)
 {
-    if (!d_ptr->m_cxt) {
+    if (!d_ptr->m_rtss) {
         return;
     }
 
@@ -600,7 +599,7 @@ Segmentation::apply_dicom_dir (const Rt_study_metadata::Pointer& rsm)
         return;
     }
 
-    d_ptr->m_cxt->apply_slice_index (rsm);
+    d_ptr->m_rtss->apply_slice_index (rsm);
 }
 
 void
@@ -613,16 +612,16 @@ Segmentation::convert_ss_img_to_cxt (void)
 
     /* Allocate memory for cxt */
     bool use_existing_bits;
-    if (d_ptr->m_cxt) {
+    if (d_ptr->m_rtss) {
         use_existing_bits = true;
     }
     else {
-        d_ptr->m_cxt = Rtss::New();
+        d_ptr->m_rtss = Rtss::New();
         use_existing_bits = false;
     }
 
     /* Copy geometry from ss_img to cxt */
-    d_ptr->m_cxt->set_geometry (d_ptr->m_ss_img);
+    d_ptr->m_rtss->set_geometry (d_ptr->m_ss_img);
 
     if (d_ptr->m_ss_img->m_type == PLM_IMG_TYPE_GPUIT_UCHAR_VEC
         || d_ptr->m_ss_img->m_type == PLM_IMG_TYPE_ITK_UCHAR_VEC) 
@@ -632,7 +631,7 @@ Segmentation::convert_ss_img_to_cxt (void)
 
         /* Do extraction */
         lprintf ("Doing extraction\n");
-        ::cxt_extract (d_ptr->m_cxt.get(), d_ptr->m_ss_img->m_itk_uchar_vec, 
+        ::cxt_extract (d_ptr->m_rtss.get(), d_ptr->m_ss_img->m_itk_uchar_vec, 
             -1, use_existing_bits);
     }
     else {
@@ -641,7 +640,7 @@ Segmentation::convert_ss_img_to_cxt (void)
 
         /* Do extraction */
         lprintf ("Doing extraction\n");
-        ::cxt_extract (d_ptr->m_cxt.get(), d_ptr->m_ss_img->m_itk_uint32, -1, 
+        ::cxt_extract (d_ptr->m_rtss.get(), d_ptr->m_ss_img->m_itk_uint32, -1, 
             use_existing_bits);
     }
 
@@ -653,7 +652,7 @@ Segmentation::convert_to_uchar_vec (void)
 {
     if (!d_ptr->m_ss_img) {
         print_and_exit (
-            "Error: convert_to_uchar_vec() requires an image");
+            "Error: convert_to_uchar_vec() requires an image\n");
     }
     d_ptr->m_ss_img->convert (PLM_IMG_TYPE_ITK_UCHAR_VEC);
 }
@@ -669,18 +668,18 @@ Segmentation::cxt_extract (void)
 void
 Segmentation::cxt_re_extract (void)
 {
-    d_ptr->m_cxt->free_all_polylines ();
+    d_ptr->m_rtss->free_all_polylines ();
     if (d_ptr->m_ss_img->m_type == PLM_IMG_TYPE_GPUIT_UCHAR_VEC
         || d_ptr->m_ss_img->m_type == PLM_IMG_TYPE_ITK_UCHAR_VEC) 
     {
         d_ptr->m_ss_img->convert (PLM_IMG_TYPE_ITK_UCHAR_VEC);
-        ::cxt_extract (d_ptr->m_cxt.get(), d_ptr->m_ss_img->m_itk_uchar_vec, 
-            d_ptr->m_cxt->num_structures, true);
+        ::cxt_extract (d_ptr->m_rtss.get(), d_ptr->m_ss_img->m_itk_uchar_vec, 
+            d_ptr->m_rtss->num_structures, true);
     }
     else {
         d_ptr->m_ss_img->convert (PLM_IMG_TYPE_ITK_ULONG);
-        ::cxt_extract (d_ptr->m_cxt.get(), d_ptr->m_ss_img->m_itk_uint32, 
-            d_ptr->m_cxt->num_structures, true);
+        ::cxt_extract (d_ptr->m_rtss.get(), d_ptr->m_ss_img->m_itk_uint32, 
+            d_ptr->m_rtss->num_structures, true);
     }
 
     d_ptr->m_rtss_valid = true;
@@ -689,8 +688,8 @@ Segmentation::cxt_re_extract (void)
 void
 Segmentation::prune_empty (void)
 {
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt->prune_empty ();
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss->prune_empty ();
     }
 }
 
@@ -704,15 +703,10 @@ Segmentation::rasterize (
     /* Rasterize structure sets */
     Rasterizer rasterizer;
 
-#if (PLM_CONFIG_USE_SS_IMAGE_VEC)
-    printf ("Setting use_ss_img_vec to true!\n");
     bool use_ss_img_vec = true;
-#else
-    bool use_ss_img_vec = false;
-#endif
 
     printf ("Rasterizing...\n");
-    rasterizer.rasterize (d_ptr->m_cxt.get(), pih, false, want_labelmap, true,
+    rasterizer.rasterize (d_ptr->m_rtss.get(), pih, false, want_labelmap, true,
         use_ss_img_vec, xor_overlapping);
 
     /* Convert rasterized structure sets from vol to plm_image */
@@ -739,16 +733,16 @@ Segmentation::rasterize (
 void
 Segmentation::set_geometry (const Plm_image_header *pih)
 {
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt->set_geometry (pih);
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss->set_geometry (pih);
     }
 }
 
 void
 Segmentation::find_rasterization_geometry (Plm_image_header *pih)
 {
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt->find_rasterization_geometry (pih);
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss->find_rasterization_geometry (pih);
     }
 }
 
@@ -760,8 +754,8 @@ Segmentation::warp_nondestructive (
 {
     Segmentation::Pointer rtss_warped = Segmentation::New ();
 
-    rtss_warped->d_ptr->m_cxt = Rtss::New (
-        Rtss::clone_empty (0, d_ptr->m_cxt.get()));
+    rtss_warped->d_ptr->m_rtss = Rtss::New (
+        Rtss::clone_empty (0, d_ptr->m_rtss.get()));
     rtss_warped->d_ptr->m_rtss_valid = false;
 
     if (d_ptr->m_labelmap) {
@@ -804,8 +798,8 @@ Segmentation::warp (
     }
 
     /* The cxt polylines are now obsolete */
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt->free_all_polylines ();
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss->free_all_polylines ();
     }
     d_ptr->m_rtss_valid = false;
 }
@@ -831,8 +825,8 @@ Segmentation::set_ss_img (UCharImageType::Pointer ss_img)
     d_ptr->m_ss_img = Plm_image::New();
     d_ptr->m_ss_img->set_itk (ss_img);
 
-    if (d_ptr->m_cxt) {
-        d_ptr->m_cxt->free_all_polylines ();
+    if (d_ptr->m_rtss) {
+        d_ptr->m_rtss->free_all_polylines ();
     }
     d_ptr->m_rtss_valid = false;
     d_ptr->m_ss_img_valid = true;
@@ -847,25 +841,25 @@ Segmentation::get_ss_img ()
 bool
 Segmentation::have_structure_set ()
 {
-    return d_ptr->m_cxt != 0;
+    return d_ptr->m_rtss != 0;
 }
 
 Rtss::Pointer&
 Segmentation::get_structure_set ()
 {
-    return d_ptr->m_cxt;
+    return d_ptr->m_rtss;
 }
 
 Rtss *
 Segmentation::get_structure_set_raw ()
 {
-    return d_ptr->m_cxt.get();
+    return d_ptr->m_rtss.get();
 }
 
 void
 Segmentation::set_structure_set (Rtss::Pointer& rtss_ss)
 {
-    d_ptr->m_cxt = rtss_ss;
+    d_ptr->m_rtss = rtss_ss;
 
     d_ptr->m_rtss_valid = true;
     d_ptr->m_ss_img_valid = false;
@@ -874,7 +868,7 @@ Segmentation::set_structure_set (Rtss::Pointer& rtss_ss)
 void
 Segmentation::set_structure_set (Rtss *rtss_ss)
 {
-    d_ptr->m_cxt.reset (rtss_ss);
+    d_ptr->m_rtss.reset (rtss_ss);
 
     d_ptr->m_rtss_valid = true;
     d_ptr->m_ss_img_valid = false;
@@ -960,8 +954,8 @@ Segmentation::initialize_ss_image (
     Plm_image_header::clone (&ss_img_pih, &pih);
 
     /* Create ss_list to hold strucure names */
-    d_ptr->m_cxt = Rtss::New();
-    d_ptr->m_cxt->set_geometry (d_ptr->m_ss_img);
+    d_ptr->m_rtss = Rtss::New();
+    d_ptr->m_rtss->set_geometry (d_ptr->m_ss_img);
 }
 
 void
@@ -988,8 +982,10 @@ Segmentation::broaden_ss_image (int new_vector_length)
     /* Loop through image */
     typedef itk::ImageRegionIterator< 
         UCharVecImageType > UCharVecIteratorType;
-    UCharVecIteratorType it_old (old_ss_img, pih.m_region);
-    UCharVecIteratorType it_new (new_ss_img, pih.m_region);
+    UCharVecIteratorType it_old (
+        old_ss_img, old_ss_img->GetLargestPossibleRegion());
+    UCharVecIteratorType it_new (
+        new_ss_img, new_ss_img->GetLargestPossibleRegion());
     for (it_old.GoToBegin(), it_new.GoToBegin(); 
          !it_old.IsAtEnd(); 
          ++it_old, ++it_new)
diff --git a/src/plastimatch/base/segmentation.h b/src/plastimatch/base/segmentation.h
index 395df5e..92b5b48 100644
--- a/src/plastimatch/base/segmentation.h
+++ b/src/plastimatch/base/segmentation.h
@@ -17,7 +17,6 @@ class Plm_image_header;
 class Rt_study;
 class Segmentation_private;
 class Rtss_roi;
-class Slice_index;
 class Xio_ct_transform;
 class Warp_parms;
 
@@ -88,6 +87,9 @@ public:
         UCharImageType::Pointer itk_image, 
         const char *structure_name = 0,
         const char *structure_color = 0);
+    Rtss_roi* add_rtss_roi (
+        const char *structure_name = 0,
+        const char *structure_color = 0);
 
     bool have_ss_img ();
     void set_ss_img (UCharImageType::Pointer ss_img);
diff --git a/src/plastimatch/base/slice_list.cxx b/src/plastimatch/base/slice_list.cxx
index 0618e35..7f2f314 100755
--- a/src/plastimatch/base/slice_list.cxx
+++ b/src/plastimatch/base/slice_list.cxx
@@ -123,9 +123,9 @@ Slice_list::get_slice_index (float z) const
     }
 
     /* NOTE: This algorithm doesn't work if there are duplicate slices */
-    int slice_no = ROUND_INT ((z - d_ptr->m_pih.m_origin[2]) 
-	/ d_ptr->m_pih.m_spacing[2]);
-    if (slice_no < 0 || slice_no >= d_ptr->m_pih.Size(2)) {
+    int slice_no = ROUND_INT ((z - d_ptr->m_pih.origin(2)) 
+	/ d_ptr->m_pih.spacing(2));
+    if (slice_no < 0 || slice_no >= d_ptr->m_pih.dim(2)) {
 	return -1;
     }
     return slice_no;
diff --git a/src/plastimatch/base/volume.cxx b/src/plastimatch/base/volume.cxx
index a752f2b..2076076 100755
--- a/src/plastimatch/base/volume.cxx
+++ b/src/plastimatch/base/volume.cxx
@@ -215,12 +215,20 @@ Volume::create (
 }
 
 const float*
-Volume::get_origin ()
+Volume::get_origin () const
 {
     return this->origin;
 }
 
 void
+Volume::get_origin (float *origin) const
+{
+    for (int d = 0; d < 3; d++) {
+        origin[d] = this->origin[d];
+    }
+}
+
+void
 Volume::set_origin (const float origin[3])
 {
     for (int d = 0; d < 3; d++) {
@@ -790,7 +798,6 @@ Volume::get_ijk_value (const float ijk[3])
         idx_floor,
         img, this
     );
-
     return val;
 }
 
@@ -846,6 +853,14 @@ Volume::is_inside (const float ijk[3]) const
 }
 
 void
+Volume::move_origin_to_idx (const plm_long ijk[3])
+{
+    float new_origin[3];
+    this->position (new_origin, ijk);
+    this->set_origin (new_origin);
+}
+
+void
 Volume::scale_inplace (float scale)
 {
     float *img;
diff --git a/src/plastimatch/base/volume.h b/src/plastimatch/base/volume.h
index da7addf..91eff55 100755
--- a/src/plastimatch/base/volume.h
+++ b/src/plastimatch/base/volume.h
@@ -79,12 +79,19 @@ public:
     );
     ~Volume ();
 public:
+    /*! \brief Return a linear index to a voxel */
     plm_long index (plm_long i, plm_long j, plm_long k) {
         return volume_index (this->dim, i, j, k);
     }
+    /*! \brief Return a linear index to a voxel */
     plm_long index (plm_long ijk[3]) {
         return volume_index (this->dim, ijk);
     }
+    /*! \brief Return a world coordinates of a voxel */
+    void position (float xyz[3], const plm_long ijk[3]) {
+        POSITION_FROM_COORDS (xyz, ijk, this->origin, this->step);
+    }
+    /*! \brief Initialize and allocate memory for the image */
     void create (
         const plm_long new_dim[3], 
         const float origin[3], 
@@ -93,6 +100,7 @@ public:
         enum Volume_pixel_type vox_type, 
         int vox_planes = 1
     );
+    /*! \brief Initialize and allocate memory for the image */
     void create (
         const Volume_header& vh, 
         enum Volume_pixel_type vox_type, 
@@ -117,7 +125,8 @@ public:
       The origin is defined as the location in world coordinates 
       of the center of the first voxel in the volume.
     */
-    const float* get_origin (void);
+    const float* get_origin (void) const;
+    void get_origin (float *) const;
     /*! \brief Set the origin.
       The origin is defined as the location in world coordinates 
       of the center of the first voxel in the volume.
@@ -178,6 +187,11 @@ public:
     /*! \brief Return true if continuous index ijk is inside volume */
     bool is_inside (const float ijk[3]) const;
 
+    /*! \brief Move the origin to the coordinate at a given index.
+      This is used to correct itk images which have a non-zero 
+      region index. */
+    void move_origin_to_idx (const plm_long ijk[3]);
+
     /*! \brief In-place (destructive) scaling of the image according to the 
       supplied scale factor */
     void scale_inplace (float scale);
diff --git a/src/plastimatch/base/volume_fill.cxx b/src/plastimatch/base/volume_fill.cxx
new file mode 100755
index 0000000..a2a4656
--- /dev/null
+++ b/src/plastimatch/base/volume_fill.cxx
@@ -0,0 +1,23 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plmbase_config.h"
+#include "string.h"
+#include "volume_fill.h"
+
+template<class T> 
+void
+volume_fill (
+    Volume* vol,
+    T val
+)
+{
+    T* img = vol->get_raw<T> ();
+
+    for (plm_long i = 0; i < vol->npix; i++) {
+        img[i] = val;
+    }
+}
+
+/* Explicit instantiations */
+template PLMBASE_API void volume_fill (Volume* vol, float val);
diff --git a/src/plastimatch/base/plm_image_p.h b/src/plastimatch/base/volume_fill.h
old mode 100644
new mode 100755
similarity index 66%
copy from src/plastimatch/base/plm_image_p.h
copy to src/plastimatch/base/volume_fill.h
index bf611b3..1aa3c2f
--- a/src/plastimatch/base/plm_image_p.h
+++ b/src/plastimatch/base/volume_fill.h
@@ -1,16 +1,17 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-#ifndef _plm_image_p_h_
-#define _plm_image_p_h_
-
-#include "plmbase_config.h"
-#include "volume.h"
-
-class Plm_image_private {
-public:
-    Metadata::Pointer m_meta;
-    Volume::Pointer m_vol;
-};
-
-#endif
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#ifndef _volume_fill_h_
+#define _volume_fill_h_
+
+#include "plmbase_config.h"
+#include "volume.h"
+
+template<class T> 
+PLMBASE_API 
+void volume_fill (
+    Volume* vol,
+    T val
+);
+
+#endif
diff --git a/src/plastimatch/base/volume_macros.h b/src/plastimatch/base/volume_macros.h
index 59c430a..63af6c3 100755
--- a/src/plastimatch/base/volume_macros.h
+++ b/src/plastimatch/base/volume_macros.h
@@ -79,4 +79,20 @@ index_in_volume (const plm_long dims[3], const plm_long ijk[3])
 #define PROJECT_X(xyz,proj)                                             \
     (xyz[0] * proj[0*3+0] + xyz[1] * proj[0*3+1] + xyz[2] * proj[0*3+2])
 
+#define POSITION_FROM_COORDS(xyz, ijk, origin, step)                    \
+    do {                                                                \
+        xyz[0] = origin[0]                                              \
+            + ijk[0]*step[3*0+0]                                        \
+            + ijk[1]*step[3*0+1]                                        \
+            + ijk[2]*step[3*0+2];                                       \
+        xyz[1] = origin[1]                                              \
+            + ijk[0]*step[3*1+0]                                        \
+            + ijk[1]*step[3*1+1]                                        \
+            + ijk[2]*step[3*1+2];                                       \
+        xyz[2] = origin[2]                                              \
+            + ijk[0]*step[3*2+0]                                        \
+            + ijk[1]*step[3*2+1]                                        \
+            + ijk[2]*step[3*2+2];                                       \
+    } while (0);
+
 #endif
diff --git a/src/plastimatch/base/xform.cxx b/src/plastimatch/base/xform.cxx
index 1829161..2202c6c 100644
--- a/src/plastimatch/base/xform.cxx
+++ b/src/plastimatch/base/xform.cxx
@@ -15,7 +15,9 @@
 
 #include "bspline_interpolate.h"
 #include "bspline_xform.h"
+#include "file_util.h"
 #include "itk_directions.h"
+#include "itk_image_create.h"
 #include "itk_image_load.h"
 #include "itk_image_save.h"
 #include "itk_resample.h"
@@ -74,6 +76,7 @@ Xform& Xform::operator= (const Xform& xf) {
     m_trn = xf.m_trn;
     m_vrs = xf.m_vrs;
     m_quat = xf.m_quat;
+    m_similarity = xf.m_similarity;
     m_aff = xf.m_aff;
     m_itk_vf = xf.m_itk_vf;
     m_itk_bsp = xf.m_itk_bsp;
@@ -93,6 +96,7 @@ Xform::clear ()
     m_vrs = 0;
     m_quat = 0;
     m_aff = 0;
+    m_similarity= 0;
     m_itk_bsp = 0;
     m_itk_tps = 0;
     m_itk_vf = 0;
@@ -138,6 +142,16 @@ Xform::get_aff () const
     return m_aff;
 }
 
+
+SimilarityTransformType::Pointer
+Xform::get_similarity() const
+{
+    if (m_type != XFORM_ITK_SIMILARITY) {
+        print_and_exit ("Typecast error in get_similarity()\n");
+    }
+    return m_similarity;
+}
+
 BsplineTransformType::Pointer
 Xform::get_itk_bsp () const
 {
@@ -254,6 +268,23 @@ Xform::set_aff (const itk::Array<double>& aff)
 }
 
 void
+Xform::set_similarity(SimilarityTransformType::Pointer sim)
+{
+    clear ();
+    m_type = XFORM_ITK_SIMILARITY;
+    m_similarity = sim;
+}
+
+void
+Xform::set_similarity (const itk::Array<double>& sim)
+{
+    typedef SimilarityTransformType XfType;
+    XfType::Pointer transform = XfType::New ();
+    transform->SetParametersByValue (sim);
+    this->set_similarity (transform);
+}
+
+void
 Xform::set_itk_bsp (BsplineTransformType::Pointer bsp)
 {
     /* Do not clear */
@@ -386,10 +417,11 @@ itk_xform_save (T transform, const char *filename)
 {
     typedef itk::TransformFileWriter TransformWriterType;
     TransformWriterType::Pointer outputTransformWriter;
-        
+
+    make_parent_directories (filename);
     outputTransformWriter = TransformWriterType::New();
-    outputTransformWriter->SetFileName( filename );
-    outputTransformWriter->SetInput( transform );
+    outputTransformWriter->SetFileName(filename);
+    outputTransformWriter->SetInput(transform);
     try
     {
         outputTransformWriter->Update();
@@ -486,6 +518,16 @@ itk_xform_load (Xform *xf, const char* fn)
 }
 
 void
+Xform::save_gpuit_vf (const char* fn)
+{
+    //write_mha (fn, this->get_gpuit_vf().get());
+
+    DeformationFieldType::Pointer itk_vf = 
+        xform_gpuit_vf_to_itk_vf (this->get_gpuit_vf().get(), 0);
+    itk_image_save (itk_vf, fn);
+}
+
+void
 Xform::save (const char* fn)
 {
     switch (this->m_type) {
@@ -501,6 +543,9 @@ Xform::save (const char* fn)
     case XFORM_ITK_AFFINE:
         itk_xform_save (this->get_aff(), fn);
         break;
+    case XFORM_ITK_SIMILARITY:
+        itk_xform_save (this->get_similarity(), fn);
+        break;
     case XFORM_ITK_BSPLINE:
         itk_xform_save (this->get_itk_bsp(), fn);
         break;
@@ -508,10 +553,10 @@ Xform::save (const char* fn)
         itk_image_save (this->get_itk_vf(), fn);
         break;
     case XFORM_GPUIT_BSPLINE:
-        bspline_xform_save (this->get_gpuit_bsp(), fn);
+        this->get_gpuit_bsp()->save(fn);
         break;
     case XFORM_GPUIT_VECTOR_FIELD:
-        write_mha (fn, this->get_gpuit_vf().get());
+        this->save_gpuit_vf (fn);
         break;
     case XFORM_NONE:
         print_and_exit ("Error trying to save null transform\n");
@@ -571,6 +616,13 @@ init_affine_default (Xform *xf_out)
     xf_out->set_aff (aff);
 }
 
+static void
+init_similarity_default (Xform *xf_out)
+{
+    SimilarityTransformType::Pointer sim = SimilarityTransformType::New();
+    xf_out->set_similarity (sim);
+}
+
 void
 xform_itk_bsp_init_default (Xform *xf)
 {
@@ -595,6 +647,13 @@ xform_trn_to_aff (Xform *xf_out, const Xform* xf_in)
     xf_out->get_aff()->SetOffset(xf_in->get_trn()->GetOffset());
 }
 
+void
+xform_trn_to_sim (Xform *xf_out, const Xform* xf_in)
+{
+    init_similarity_default (xf_out);
+    xf_out->get_similarity()->SetOffset(xf_in->get_trn()->GetOffset());
+}
+
 static void
 xform_vrs_to_quat (Xform *xf_out, const Xform* xf_in)
 {
@@ -619,6 +678,30 @@ xform_vrs_to_aff (Xform *xf_out, const Xform* xf_in)
     xf_out->get_aff()->SetOffset(xf_in->get_vrs()->GetOffset());
 }
 
+static void
+xform_vrs_to_sim (Xform *xf_out, const Xform* xf_in)
+{
+    init_similarity_default (xf_out);
+#if ITK_VERSION_MAJOR == 3
+    xf_out->get_similarity()->SetMatrix(xf_in->get_vrs()->GetRotationMatrix());
+#else /* ITK 4 */
+    xf_out->get_similarity()->SetMatrix(xf_in->get_vrs()->GetMatrix());
+#endif
+    xf_out->get_similarity()->SetOffset(xf_in->get_vrs()->GetOffset());
+}
+
+void
+xform_sim_to_aff (Xform *xf_out, const Xform* xf_in)
+{
+    init_affine_default (xf_out);
+#if ITK_VERSION_MAJOR == 3
+    xf_out->get_aff()->SetMatrix(xf_in->get_similarity()->GetRotationMatrix());
+#else /* ITK 4 */
+    xf_out->get_aff()->SetMatrix(xf_in->get_similarity()->GetMatrix());
+#endif
+    xf_out->get_aff()->SetOffset(xf_in->get_similarity()->GetOffset());
+}
+
 /* -----------------------------------------------------------------------
    Conversion to itk_bsp
    ----------------------------------------------------------------------- */
@@ -660,18 +743,18 @@ bsp_grid_from_img_grid (
 
     /* Convert image specifications to grid specifications */
     for (int d=0; d<3; d++) {
-        float img_ext = (pih->Size(d) - 1) * fabs (pih->m_spacing[d]);
-        bsp_origin[d] = pih->m_origin[d];
+        float img_ext = (pih->dim(d) - 1) * fabs (pih->spacing(d));
+        bsp_origin[d] = pih->origin(d);
         bsp_spacing[d] = grid_spac[d];
         bsp_size[d] = 4 + (int) floor (img_ext / grid_spac[d]);
     }
-    bsp_direction = pih->m_direction;
+    bsp_direction = pih->GetDirection();
     bsp_region.SetSize (bsp_size);
 
     /* Adjust origin based on direction cosines */
     for (int d1=0; d1<3; d1++) {
         for (int d2=0; d2<3; d2++) {
-            bsp_origin[d2] -= grid_spac[d1] * pih->m_direction[d1][d2];
+            bsp_origin[d2] -= grid_spac[d1] * bsp_direction[d1][d2];
         }
     }
 }
@@ -775,28 +858,21 @@ xform_any_to_itk_bsp_nobulk (
         bsp_region, bsp_direction, pih, grid_spac);
 
     /* Make a vector field at bspline grid spacing */
-    pih_bsp.m_origin = bsp_origin;
-    pih_bsp.m_spacing = bsp_spacing;
-    pih_bsp.m_region = bsp_region;
-    pih_bsp.m_direction = bsp_direction;
+    pih_bsp.set (bsp_region, bsp_origin, bsp_spacing, bsp_direction);
     xform_to_itk_vf (&xf_tmp, xf_in, &pih_bsp);
 
     /* Vector field is interleaved.  We need planar for decomposition. */
-    FloatImageType::Pointer img = FloatImageType::New();
-    img->SetOrigin (pih_bsp.m_origin);
-    img->SetSpacing (pih_bsp.m_spacing);
-    img->SetRegions (pih_bsp.m_region);
-    img->SetDirection (pih_bsp.m_direction);
-    img->Allocate ();
+    FloatImageType::Pointer img = itk_image_create<float> (pih_bsp);
 
     /* Loop through planes */
     unsigned int counter = 0;
+    DeformationFieldType::Pointer vf = xf_tmp.get_itk_vf();
     for (d = 0; d < 3; d++) {
         /* Copy a single VF plane into img */
         typedef itk::ImageRegionIterator< FloatImageType > FloatIteratorType;
         typedef itk::ImageRegionIterator< DeformationFieldType > VFIteratorType;
-        FloatIteratorType img_it (img, pih_bsp.m_region);
-        VFIteratorType vf_it (xf_tmp.get_itk_vf(), pih_bsp.m_region);
+        FloatIteratorType img_it (img, img->GetLargestPossibleRegion());
+        VFIteratorType vf_it (vf, vf->GetLargestPossibleRegion());
         for (img_it.GoToBegin(), vf_it.GoToBegin(); 
              !img_it.IsAtEnd(); 
              ++img_it, ++vf_it) 
@@ -829,32 +905,41 @@ xform_any_to_itk_bsp_nobulk (
     bsp_out->SetParametersByValue (bsp_coeff);
 }
 
-/* GCS Jun 3, 2008.  When going from a lower image resolution to a 
-    higher image resolution, the origin pixel moves outside of the 
-    valid region defined by the bspline grid.  To fix this, we re-form 
-    the b-spline with extra coefficients such that all pixels fall 
-    within the valid region. */
-void
-itk_bsp_extend_to_region (Xform* xf,                
-    const Plm_image_header* pih,
-    const ImageRegionType* roi)
+/* This function extends the B-spline grid, padding with zeros, 
+   so that the grid contains the specified Region "roi." 
+   This is sometimes needed so that the B-spline can warp an 
+   image or be rendered into a vector field. 
+   It may sometimes be needed for going from lower resolution 
+   to higher resolutions as well.  */
+static void
+itk_bsp_extend_to_region (
+    Xform* xf, 
+    const Plm_image_header* pih, 
+    const RegionType* roi)
 {
-    int d, old_idx;
     unsigned long i, j, k;
     int extend_needed = 0;
     BsplineTransformType::Pointer bsp = xf->get_itk_bsp();
     BsplineTransformType::OriginType bsp_origin = bsp->GetGridOrigin();
     BsplineTransformType::RegionType bsp_region = bsp->GetGridRegion();
     BsplineTransformType::RegionType::SizeType bsp_size = bsp->GetGridRegion().GetSize();
-    int eb[3], ea[3];  /* # of control points to "extend before" and "extend after" existing grid */
+    BsplineTransformType::SpacingType bsp_spacing = bsp->GetGridSpacing();
+    BsplineTransformType::DirectionType bsp_direction = bsp->GetGridDirection();
+    Plm_image_header bsp_pih (bsp_region, bsp_origin, bsp_spacing, 
+        bsp_direction);
 
-    /* Figure out if we need to extend the bspline grid.  If so, compute ea & eb, 
-       as well as new values of bsp_region and bsp_origin. */
-    for (d = 0; d < 3; d++) {
+    /* # of control points to "extend before" and "extend after" 
+       existing grid */
+    int eb[3], ea[3];
+
+#if PLM_CONFIG_LEGACY_BSPLINE_EXTEND
+    /* Figure out if we need to extend the bspline grid.  If so, compute 
+       ea & eb, as well as new values of bsp_region and bsp_origin. */
+    for (int d = 0; d < 3; d++) {
         float old_roi_origin = bsp->GetGridOrigin()[d] + bsp->GetGridSpacing()[d];
         float old_roi_corner = old_roi_origin + (bsp->GetGridRegion().GetSize()[d] - 3) * bsp->GetGridSpacing()[d];
-        float new_roi_origin = pih->m_origin[d] + roi->GetIndex()[d] * pih->m_spacing[d];
-        float new_roi_corner = new_roi_origin + (roi->GetSize()[d] - 1) * pih->m_spacing[d];
+        float new_roi_origin = pih->origin(d) + roi->GetIndex()[d] * pih->spacing(d);
+        float new_roi_corner = new_roi_origin + (roi->GetSize()[d] - 1) * pih->spacing(d);
         ea[d] = eb[d] = 0;
         if (old_roi_origin > new_roi_origin) {
             float diff = old_roi_origin - new_roi_origin;
@@ -870,8 +955,68 @@ itk_bsp_extend_to_region (Xform* xf,
             extend_needed = 1;
         }
     }
+
+#else
+    /* Figure out if we need to extend the bspline grid.  If so, compute 
+       ea & eb, as well as new values of bsp_region and bsp_origin. */
+    float new_roi_origin_idx[3], new_roi_corner_idx[3];
+    for (int d = 0; d < 3; d++) {
+        new_roi_origin_idx[d] = 0.f;
+        new_roi_corner_idx[d] = roi->GetSize()[d] - 1;
+    }
+    FloatPoint3DType new_roi_origin = pih->get_position (new_roi_origin_idx);
+    FloatPoint3DType new_roi_corner = pih->get_position (new_roi_corner_idx);
+
+    FloatPoint3DType new_roi_origin_idx_in_old = 
+        bsp_pih.get_index (new_roi_origin);
+    FloatPoint3DType new_roi_corner_idx_in_old = 
+        bsp_pih.get_index (new_roi_corner);
+
+#if defined (commentout)
+    printf ("-- BSP PIH\n");
+    bsp_pih.print();
+    printf ("-- PIH\n");
+    pih->print();
+    printf ("New ROI Or Idx: %g %g %g\n",
+        new_roi_origin_idx[0], new_roi_origin_idx[1], new_roi_origin_idx[2]);
+    printf ("New ROI Co Idx: %g %g %g\n",
+        new_roi_corner_idx[0], new_roi_corner_idx[1], new_roi_corner_idx[2]);
+    printf ("New ROI Or: %g %g %g\n",
+        new_roi_origin[0], new_roi_origin[1], new_roi_origin[2]);
+    printf ("New ROI Co: %g %g %g\n",
+        new_roi_corner[0], new_roi_corner[1], new_roi_corner[2]);
+    printf ("New ROI Or Idx (inold): %g %g %g\n",
+        new_roi_origin_idx_in_old[0], new_roi_origin_idx_in_old[1], new_roi_origin_idx_in_old[2]);
+    printf ("New ROI Co Idx (inold): %g %g %g\n",
+        new_roi_corner_idx_in_old[0], new_roi_corner_idx_in_old[1], new_roi_corner_idx_in_old[2]);
+#endif
+
+    for (int d = 0; d < 3; d++) {
+        eb[d] = ea[d] = 0;
+        new_roi_origin_idx_in_old[d] = floorf (new_roi_origin_idx_in_old[d]);
+        new_roi_corner_idx_in_old[d] = ceilf (new_roi_corner_idx_in_old[d]);
+        if (new_roi_origin_idx_in_old[d] < 1) {
+            eb[d] = 1 - (int) new_roi_origin_idx_in_old[d];
+            new_roi_corner_idx[d] -= eb[d];
+            extend_needed = 1;
+        }
+        if (new_roi_corner_idx_in_old[d] > bsp_size[d] - 2) {
+            ea[d] = new_roi_corner_idx_in_old[d] - (bsp_size[d] - 1);
+            extend_needed = 1;
+        }
+    }
+
     if (extend_needed) {
+        /* Figure out new origin */
+        bsp_origin = bsp_pih.get_position (new_roi_corner_idx);
+        /* Figure out new size */
+        for (int d = 0; d < 3; d++) {
+            bsp_size[d] += eb[d] + ea[d];
+        }
+    }
+#endif
 
+    if (extend_needed) {
         /* Allocate new parameter array */
         BsplineTransformType::Pointer bsp_new = BsplineTransformType::New();
         BsplineTransformType::RegionType old_region = bsp->GetGridRegion();
@@ -879,13 +1024,14 @@ itk_bsp_extend_to_region (Xform* xf,
         bsp_new->SetGridOrigin (bsp_origin);
         bsp_new->SetGridRegion (bsp_region);
         bsp_new->SetGridSpacing (bsp->GetGridSpacing());
+        bsp_new->SetGridDirection (bsp->GetGridDirection());
 
         /* Copy current parameters in... */
         const unsigned int num_parms = bsp_new->GetNumberOfParameters();
         BsplineTransformType::ParametersType bsp_coeff;
         bsp_coeff.SetSize (num_parms);
         bsp_coeff.Fill (0.f);
-        for (old_idx = 0, d = 0; d < 3; d++) {
+        for (int old_idx = 0, d = 0; d < 3; d++) {
             for (k = 0; k < old_region.GetSize()[2]; k++) {
                 for (j = 0; j < old_region.GetSize()[1]; j++) {
                     for (i = 0; i < old_region.GetSize()[0]; i++, old_idx++) {
@@ -1036,7 +1182,7 @@ gpuit_bsp_to_itk_bsp_raw (
     BsplineTransformType::OriginType bsp_origin;
     BsplineTransformType::SpacingType bsp_spacing;
     BsplineTransformType::RegionType bsp_region;
-    BsplineTransformType::DirectionType bsp_direction = pih->m_direction;
+    BsplineTransformType::DirectionType bsp_direction = pih->GetDirection();
 
     /* Convert bspline grid geometry from gpuit to itk */
     gpuit_bsp_grid_to_itk_bsp_grid (bsp_origin, bsp_spacing, bsp_region, 
@@ -1074,7 +1220,6 @@ xform_gpuit_bsp_to_itk_bsp (
     Xform *xf_out, 
     const Xform* xf_in,
     const Plm_image_header* pih,
-    const ImageRegionType* roi, /* Not yet used */
     const float* grid_spac)
 {
     Xform xf_tmp;
@@ -1099,13 +1244,16 @@ xform_itk_any_to_itk_vf (
     itk::Transform<double,3,3>* xf,
     const Plm_image_header* pih)
 {
+#if defined (commentout)
     DeformationFieldType::Pointer itk_vf = DeformationFieldType::New();
-
     itk_vf->SetOrigin (pih->m_origin);
     itk_vf->SetSpacing (pih->m_spacing);
     itk_vf->SetRegions (pih->m_region);
     itk_vf->SetDirection (pih->m_direction);
     itk_vf->Allocate ();
+#endif
+    DeformationFieldType::Pointer itk_vf 
+        = itk_image_create<FloatVector3DType> (*pih);
 
     typedef itk::ImageRegionIteratorWithIndex< 
         DeformationFieldType > FieldIterator;
@@ -1148,7 +1296,8 @@ xform_itk_bsp_to_itk_vf (Xform* xf_in, const Plm_image_header* pih)
     xform_itk_bsp_to_itk_bsp (&xf_tmp, xf_in, pih, grid_spac);
 
     /* Extend bsp control point grid */
-    itk_bsp_extend_to_region (&xf_tmp, pih, &pih->m_region);
+    RegionType region = pih->GetRegion();
+    itk_bsp_extend_to_region (&xf_tmp, pih, &region);
 
     /* Convert extended bsp to vf */
     return xform_itk_any_to_itk_vf (xf_tmp.get_itk_bsp(), pih);
@@ -1179,13 +1328,14 @@ xform_gpuit_bsp_to_itk_vf (Xform* xf_in, Plm_image_header* pih)
     Xform xf_tmp;
     OriginType img_origin;
     SpacingType img_spacing;
-    ImageRegionType img_region;
+    RegionType img_region;
 
     /* Copy from GPUIT coefficient array to ITK coefficient array */
     gpuit_bsp_to_itk_bsp_raw (&xf_tmp, xf_in, pih);
 
     /* Resize itk array to span image */
-    itk_bsp_extend_to_region (&xf_tmp, pih, &pih->m_region);
+    RegionType region = pih->GetRegion();
+    itk_bsp_extend_to_region (&xf_tmp, pih, &region);
 
     /* Render to vector field */
     itk_vf = xform_itk_any_to_itk_vf (xf_tmp.get_itk_bsp(), pih);
@@ -1275,7 +1425,7 @@ create_gpuit_bxf (Plm_image_header* pih, const float* grid_spac)
             vox_per_rgn[d] = 4;
         }
     }
-    bspline_xform_initialize (bxf, img_origin, img_spacing, img_dim, 
+    bxf->initialize (img_origin, img_spacing, img_dim, 
         roi_offset, roi_dim, vox_per_rgn, direction_cosines);
     return bxf;
 }
@@ -1287,14 +1437,13 @@ xform_any_to_gpuit_bsp (
     const float* grid_spac)
 {
     Xform xf_tmp;
-    ImageRegionType roi;
 
     /* Initialize gpuit bspline data structure */
     Bspline_xform* bxf_new = create_gpuit_bxf (pih, grid_spac);
 
     if (xf_in->m_type != XFORM_NONE) {
         /* Output ROI is going to be whole image */
-        roi = pih->m_region;
+        RegionType roi = pih->GetRegion ();
 
         /* Create itk_bsp xf using image specifications */
         xform_any_to_itk_bsp_nobulk (&xf_tmp, xf_in, pih, bxf_new->grid_spac);
@@ -1322,16 +1471,12 @@ xform_gpuit_bsp_to_gpuit_bsp (
 )
 {
     Xform xf_tmp;
-    ImageRegionType roi;
 
     /* Initialize gpuit bspline data structure */
     Bspline_xform* bxf_new = create_gpuit_bxf (pih, grid_spac);
 
-    /* Output ROI is going to be whole image */
-    roi = pih->m_region;
-
     /* Create itk_bsp xf using image specifications */
-    xform_gpuit_bsp_to_itk_bsp (&xf_tmp, xf_in, pih, &roi, bxf_new->grid_spac);
+    xform_gpuit_bsp_to_itk_bsp (&xf_tmp, xf_in, pih, bxf_new->grid_spac);
 
     /* Copy from ITK coefficient array to gpuit coefficient array */
     int k = 0;
@@ -1526,6 +1671,7 @@ xform_to_quat (
         *xf_out = *xf_in;
         break;
     case XFORM_ITK_AFFINE:
+    case XFORM_ITK_SIMILARITY:
     case XFORM_ITK_BSPLINE:
     case XFORM_ITK_TPS:
     case XFORM_ITK_VECTOR_FIELD:
@@ -1555,6 +1701,9 @@ xform_to_aff (
     case XFORM_ITK_VERSOR:
         xform_vrs_to_aff (xf_out, xf_in);
         break;
+    case XFORM_ITK_SIMILARITY:
+        xform_sim_to_aff (xf_out, xf_in);
+        break;
     case XFORM_ITK_QUATERNION:
         print_and_exit ("Sorry, couldn't convert to aff\n");
         break;
@@ -1577,6 +1726,42 @@ xform_to_aff (
 }
 
 void
+xform_to_similarity (
+    Xform *xf_out,
+    const Xform *xf_in,
+    Plm_image_header *pih)
+{
+    switch (xf_in->m_type) {
+    case XFORM_NONE:
+        init_similarity_default (xf_out);
+        break;
+    case XFORM_ITK_TRANSLATION:
+        xform_trn_to_sim (xf_out, xf_in);
+        break;
+    case XFORM_ITK_VERSOR:
+        xform_vrs_to_sim(xf_out,xf_in);
+        break;
+    case XFORM_ITK_SIMILARITY:
+        *xf_out = *xf_in;
+        break;
+    case XFORM_ITK_QUATERNION:
+    case XFORM_ITK_AFFINE:
+    case XFORM_ITK_BSPLINE:
+    case XFORM_ITK_TPS:
+    case XFORM_ITK_VECTOR_FIELD:
+        print_and_exit ("Sorry, couldn't convert to aff\n");
+        break;
+    case XFORM_GPUIT_BSPLINE:
+    case XFORM_GPUIT_VECTOR_FIELD:
+        print_and_exit ("Sorry, gpuit xforms not fully implemented\n");
+        break;
+    default:
+        print_and_exit ("Program error.  Bad xform type.\n");
+        break;
+    }
+}
+
+void
 xform_to_itk_bsp (
     Xform *xf_out, 
     const Xform *xf_in, 
@@ -1612,9 +1797,8 @@ xform_to_itk_bsp (
     case XFORM_ITK_VECTOR_FIELD:
         print_and_exit ("Sorry, couldn't convert itk_vf to itk_bsp\n");
         break;
-    case XFORM_GPUIT_BSPLINE:
-        xform_gpuit_bsp_to_itk_bsp (xf_out, xf_in, pih, 
-            &pih->m_region, grid_spac);
+    case XFORM_GPUIT_BSPLINE: 
+        xform_gpuit_bsp_to_itk_bsp (xf_out, xf_in, pih, grid_spac);
         break;
     case XFORM_GPUIT_VECTOR_FIELD:
         print_and_exit ("Sorry, couldn't convert gpuit_vf to itk_bsp\n");
@@ -1714,6 +1898,9 @@ xform_to_itk_vf (Xform* xf_out, Xform *xf_in, Plm_image_header* pih)
     case XFORM_ITK_AFFINE:
         vf = xform_itk_any_to_itk_vf (xf_in->get_aff(), pih);
         break;
+    case XFORM_ITK_SIMILARITY:
+        vf = xform_itk_any_to_itk_vf (xf_in->get_similarity(), pih);
+        break;
     case XFORM_ITK_BSPLINE:
         vf = xform_itk_bsp_to_itk_vf (xf_in, pih);
         break;
diff --git a/src/plastimatch/base/xform.h b/src/plastimatch/base/xform.h
index e3b56b7..7b463f3 100644
--- a/src/plastimatch/base/xform.h
+++ b/src/plastimatch/base/xform.h
@@ -11,6 +11,7 @@
 #include "itkAffineTransform.h"
 #include "itkBSplineDeformableTransform.h"
 #include "itkThinPlateSplineKernelTransform.h"
+#include "itkSimilarity3DTransform.h"
 
 #include "itk_image_type.h"
 #include "smart_pointer.h"
@@ -32,7 +33,8 @@ enum Xform_type {
     XFORM_ITK_TPS               = 6,
     XFORM_ITK_VECTOR_FIELD      = 7,
     XFORM_GPUIT_BSPLINE         = 8,
-    XFORM_GPUIT_VECTOR_FIELD    = 9
+    XFORM_GPUIT_VECTOR_FIELD    = 9,
+    XFORM_ITK_SIMILARITY        = 10
 };
 
 /* itk basic transforms */
@@ -40,6 +42,7 @@ typedef itk::TranslationTransform < double, 3 > TranslationTransformType;
 typedef itk::VersorRigid3DTransform < double > VersorTransformType;
 typedef itk::QuaternionRigidTransform < double > QuaternionTransformType;
 typedef itk::AffineTransform < double, 3 > AffineTransformType;
+typedef itk::Similarity3DTransform<double> SimilarityTransformType;
 
 /* itk B-spline transforms */
 const unsigned int SplineDimension = 3;
@@ -75,6 +78,7 @@ public:
     DeformationFieldType::Pointer m_itk_vf;
     BsplineTransformType::Pointer m_itk_bsp;
     TpsTransformType::Pointer m_itk_tps;
+    SimilarityTransformType::Pointer m_similarity;
 
 public:
     void clear ();
@@ -88,6 +92,7 @@ public:
     VersorTransformType::Pointer get_vrs () const;
     QuaternionTransformType::Pointer get_quat () const;
     AffineTransformType::Pointer get_aff () const;
+    SimilarityTransformType::Pointer get_similarity() const;
     BsplineTransformType::Pointer get_itk_bsp () const;
     TpsTransformType::Pointer get_itk_tps () const;
     DeformationFieldType::Pointer get_itk_vf () const;
@@ -104,6 +109,8 @@ public:
     void set_quat (QuaternionTransformType::Pointer quat);
     void set_aff (const itk::Array<double>& aff);
     void set_aff (AffineTransformType::Pointer aff);
+    void set_similarity(SimilarityTransformType::Pointer sim);
+    void set_similarity(const itk::Array<double>& sim);
     void set_itk_bsp (BsplineTransformType::Pointer bsp);
     void set_itk_tps (TpsTransformType::Pointer tps);
     void set_itk_vf (DeformationFieldType::Pointer vf);
@@ -117,6 +124,9 @@ public:
 
     void print ();
 
+protected:
+    void save_gpuit_vf (const char* fn);
+
 public:
     Xform& operator= (const Xform& xf);
 };
@@ -142,6 +152,8 @@ PLMBASE_API void xform_to_quat (
     Xform *xf_out, const Xform *xf_in, Plm_image_header* pih);
 PLMBASE_API void xform_to_aff (
     Xform *xf_out, const Xform *xf_in, Plm_image_header* pih);
+PLMBASE_API void xform_to_similarity (
+    Xform *xf_out, const Xform *xf_in, Plm_image_header* pih);
 PLMBASE_API DeformationFieldType::Pointer xform_gpuit_vf_to_itk_vf (
     Volume* vf,              /* Input */
     Plm_image_header* pih    /* Input, can be null */
diff --git a/src/plastimatch/base/xio_ct.h b/src/plastimatch/base/xio_ct.h
index a8c5e11..f552e3d 100644
--- a/src/plastimatch/base/xio_ct.h
+++ b/src/plastimatch/base/xio_ct.h
@@ -7,7 +7,6 @@
 #include "plmbase_config.h"
 
 class Plm_image;
-class Slice_index;
 class Xio_ct_transform;
 class Xio_studyset;
 
diff --git a/src/plastimatch/base/xio_ct_transform.cxx b/src/plastimatch/base/xio_ct_transform.cxx
index 5d3d189..4d37085 100755
--- a/src/plastimatch/base/xio_ct_transform.cxx
+++ b/src/plastimatch/base/xio_ct_transform.cxx
@@ -124,8 +124,8 @@ Xio_ct_transform::set_from_rdd (
     if (patient_pos == "HFS" ||	patient_pos == "") {
 
 	/* Offsets */
-	this->x_offset = v->origin[0] - pih->m_origin[0];
-	this->y_offset = v->origin[1] - pih->m_origin[1];
+	this->x_offset = v->origin[0] - pih->origin(0);
+	this->y_offset = v->origin[1] - pih->origin(1);
 
 	/* Direction cosines */
 	this->direction_cosines[0] = 1.0f;
@@ -135,8 +135,8 @@ Xio_ct_transform::set_from_rdd (
     } else if (patient_pos == "HFP") {
 
 	/* Offsets */
-	this->x_offset = v->origin[0] + pih->m_origin[0];
-	this->y_offset = v->origin[1] + pih->m_origin[1];
+	this->x_offset = v->origin[0] + pih->origin(0);
+	this->y_offset = v->origin[1] + pih->origin(1);
 
 	/* Direction cosines */
 	this->direction_cosines[0] = -1.0f;
@@ -146,8 +146,8 @@ Xio_ct_transform::set_from_rdd (
     } else if (patient_pos == "FFS") {
 
 	/* Offsets */
-	this->x_offset = v->origin[0] + pih->m_origin[0];
-	this->y_offset = v->origin[1] - pih->m_origin[1];
+	this->x_offset = v->origin[0] + pih->origin(0);
+	this->y_offset = v->origin[1] - pih->origin(1);
 
 	/* Direction cosines */
 	this->direction_cosines[0] = -1.0f;
@@ -157,8 +157,8 @@ Xio_ct_transform::set_from_rdd (
     } else if (patient_pos == "FFP") {
 
 	/* Offsets */
-	this->x_offset = v->origin[0] - pih->m_origin[0];
-	this->y_offset = v->origin[1] + pih->m_origin[1];
+	this->x_offset = v->origin[0] - pih->origin(0);
+	this->y_offset = v->origin[1] + pih->origin(1);
 
 	/* Direction cosines */
 	this->direction_cosines[0] = 1.0f;
diff --git a/src/plastimatch/base/xio_demographic.cxx b/src/plastimatch/base/xio_demographic.cxx
index 8461ac1..b0fdc52 100755
--- a/src/plastimatch/base/xio_demographic.cxx
+++ b/src/plastimatch/base/xio_demographic.cxx
@@ -23,11 +23,14 @@ Xio_demographic::Xio_demographic (const char *filename)
     std::string version;
     getline (ifs, version);
 
-    /* date (for what?) */
-    std::string date;
-    getline (ifs, date);
-
     /* important stuff here */
+    getline (ifs, m_import_date);
+    m_import_date = string_trim (m_import_date);
+    if (m_import_date.length() >= 8) {
+        m_import_date = m_import_date.substr(0,8);
+    } else {
+        m_import_date = "";
+    }
     getline (ifs, m_patient_name);
     m_patient_name = string_trim (m_patient_name);
     getline (ifs, m_patient_id);
diff --git a/src/plastimatch/base/xio_demographic.h b/src/plastimatch/base/xio_demographic.h
index 6ce71cc..4bd1fd4 100755
--- a/src/plastimatch/base/xio_demographic.h
+++ b/src/plastimatch/base/xio_demographic.h
@@ -12,6 +12,7 @@ class PLMBASE_API Xio_demographic
 public:
     std::string m_patient_name;
     std::string m_patient_id;
+    std::string m_import_date;
 public:
     Xio_demographic (const char *filename);
     ~Xio_demographic ();
diff --git a/src/plastimatch/base/xio_studyset.cxx b/src/plastimatch/base/xio_studyset.cxx
index a8326d7..68373b5 100644
--- a/src/plastimatch/base/xio_studyset.cxx
+++ b/src/plastimatch/base/xio_studyset.cxx
@@ -1,127 +1,170 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-#include "plmbase_config.h"
-#include <algorithm>
-#include <fstream>
-#include <iostream>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "xio_studyset.h"
-
-Xio_studyset::Xio_studyset (const std::string& input_dir)
-{
-    this->studyset_dir = input_dir;
-
-    // Open index.dat file in input_dir
-    std::string indexdat(input_dir);
-    indexdat += "/index.dat";
-    std::ifstream index (indexdat.c_str());
-
-    int all_number_slices = 0;
-    std::vector<Xio_studyset_slice> all_slices;
-
-    if (index.is_open()) {
-	// Get total number of slices
-	index >> all_number_slices;
-
-	// Loop through slices getting filename and location
-	std::string slice_filename_scan;
-	std::string slice_name;
-	float slice_location;
-
-	for (int i = 0; i < all_number_slices; i++) {
-	    index >> slice_filename_scan;
-	    index >> slice_location;
-
-	    Xio_studyset_slice slice (slice_filename_scan, slice_location);
-	    all_slices.push_back (slice);
-	}
-
-	// Sort slices in positive direction
-	std::sort (all_slices.begin(), all_slices.end());
-    } else {
-	all_number_slices = 0;
-    }
-
-    // Plastimatch only supports volumes with uniform voxel sizes
-    // If slice thickness is not uniform, extract the largest uniform chunk
-
-    float best_chunk_diff = 0.f;
-    int best_chunk_start = 0;
-    int best_chunk_len = 0;
-
-    if (all_number_slices > 1) {
-
-	float z_diff;
-	float this_chunk_diff = 0.f;
-	int this_chunk_start = 0;
-	int this_chunk_len = 0;
-
-	for (int i = 1; i < all_number_slices; i++) {
-	    z_diff = all_slices[i].location - all_slices[i-1].location;
-
-	    if (i == 1) {
-		// First chunk
-		this_chunk_start = best_chunk_start = 0;
-		this_chunk_diff = best_chunk_diff = z_diff;
-		this_chunk_len = best_chunk_len = 2;
-	    } else if (fabs (this_chunk_diff - z_diff) > 0.11) {
-		// Start a new chunk if difference in thickness is more than 0.1 millimeter
-		this_chunk_start = i - 1;
-		this_chunk_len = 2;
-		this_chunk_diff = z_diff;
-	    } else {
-		// Same thickness, increase size of this chunk
-		this_chunk_diff = ((this_chunk_len * this_chunk_diff) + z_diff)
-		    / (this_chunk_len + 1);
-		this_chunk_len++;
-
-		// Check if this chunk is now the best chunk
-		if (this_chunk_len > best_chunk_len) {
-		    best_chunk_start = this_chunk_start;
-		    best_chunk_len = this_chunk_len;
-		    best_chunk_diff = this_chunk_diff;
-		}
-	    }
-	}
-    } else {
-	best_chunk_start = 0;
-	best_chunk_len = 1;
-	best_chunk_diff = 0;
-    }
-
-    // Extract best chunk
-    number_slices = best_chunk_len;
-    thickness = best_chunk_diff;
-    for (int i = 0; i < best_chunk_len; i++) {
-	slices.push_back(all_slices[best_chunk_start + i]);
-    }
-
-    // Initialize pixel spacing to zero.  This get set when the 
-    // CT is loaded
-    this->ct_pixel_spacing[0] = this->ct_pixel_spacing[1] = 0.f;
-}
-
-Xio_studyset::~Xio_studyset ()
-{
-}
-
-
-Xio_studyset_slice::Xio_studyset_slice (std::string slice_filename_scan, const float slice_location)
-{
-    filename_scan = slice_filename_scan;
-    location = slice_location;
-
-    // Get name from slice filename
-    size_t extension_dot = filename_scan.find_last_of("."); 
-    name = filename_scan.substr(0, extension_dot);
-
-    filename_contours = name + ".WC";
-}
-
-Xio_studyset_slice::~Xio_studyset_slice ()
-{
-}
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plmbase_config.h"
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "print_and_exit.h"
+#include "xio_studyset.h"
+
+int Xio_studyset::gcd(int a, int b) {
+
+    // Euclidean algorithm
+
+    while (b != 0) {
+	int t = b;
+	b = a % b;
+	a = t;
+    }
+
+    return a;
+
+}
+
+Xio_studyset::Xio_studyset (const std::string& input_dir)
+{
+    this->studyset_dir = input_dir;
+
+    // Open index.dat file in input_dir
+    std::string indexdat(input_dir);
+    indexdat += "/index.dat";
+    std::ifstream index (indexdat.c_str());
+
+    int all_number_slices = 0;
+    std::vector<Xio_studyset_slice> all_slices;
+
+    if (index.is_open()) {
+	// Get total number of slices
+	index >> all_number_slices;
+
+	// Loop through slices getting filename and location
+	std::string slice_filename_scan;
+	std::string slice_name;
+	float slice_location;
+
+	for (int i = 0; i < all_number_slices; i++) {
+	    index >> slice_filename_scan;
+	    index >> slice_location;
+
+	    Xio_studyset_slice slice (slice_filename_scan, slice_location);
+	    all_slices.push_back (slice);
+	}
+
+	// Sort slices in positive direction
+	std::sort (all_slices.begin(), all_slices.end());
+    } else {
+	all_number_slices = 0;
+    }
+
+    // Workaround for multiple slice thickness
+    // Create volume with uniform voxel sizes by finding greatest common divisor of slice thicknesses,
+    // and duplicating slices to obtain a uniform Z axis.
+
+    // Get slices thicknesses from CT files
+
+    std::vector<float> slice_thickness;
+
+    for (size_t i = 0; i < all_slices.size(); i++) {
+
+	std::string ct_file = this->studyset_dir + "/" + all_slices[i].filename_scan;
+	std::string line;
+
+	// Open file
+	std::ifstream ifs(ct_file.c_str(), std::ifstream::in);
+	if (ifs.fail()) {
+	    print_and_exit("Error opening CT file %s for read\n", ct_file.c_str());
+	} else {
+	    // Skip 14 lines
+	    for (int i = 0; i < 14; i++) {
+		getline(ifs, line);
+	    }
+
+	    getline(ifs, line);
+
+	    int dummy;
+	    float highres_thickness;
+
+	    if (sscanf(line.c_str(), "%d,%g", &dummy, &highres_thickness) != 2) {
+		print_and_exit("Error parsing slice thickness (%s)\n", line.c_str());
+	    }
+
+	    slice_thickness.push_back(highres_thickness);
+	}
+	
+    }
+
+    // Find greatest common divisor
+
+    std::vector<int> slice_thickness_int;
+    int slice_thickness_gcd = 1;
+
+    for (size_t i = 0; i < all_slices.size(); i++) {
+	// 1/1000 mm resolution
+	int rounded_thickness = static_cast<int> (slice_thickness[i] * 1000.);
+	if (rounded_thickness == 0) rounded_thickness = 1;
+	slice_thickness_int.push_back(rounded_thickness);
+    }
+
+    if (all_slices.size() == 1) {
+	slice_thickness_gcd = slice_thickness_int[0];
+    }
+    else if (all_slices.size() > 0) {
+	slice_thickness_gcd = gcd(slice_thickness_int[0], slice_thickness_int[1]);
+	for (size_t i = 2; i < all_slices.size(); i++) {
+		slice_thickness_gcd = gcd(slice_thickness_gcd, slice_thickness_int[i]);
+	}
+    }
+
+    // Build new slice list, determining duplication needed for each slice
+
+    thickness = slice_thickness_gcd / 1000.;
+    number_slices = 0;
+
+    if (all_slices.size() > 0) {
+	float location = all_slices[0].location - (slice_thickness[0] / 2.) + (thickness / 2.);
+
+	for (size_t i = 0; i < all_slices.size(); i++) {
+	    int duplicate = slice_thickness_int[i] / slice_thickness_gcd;
+
+	    for (int j = 0; j < duplicate; j++) {
+		    Xio_studyset_slice slice(all_slices[i].filename_scan, location);
+		    slices.push_back(slice);
+		    location += thickness;
+		    number_slices++;
+	    }
+	}
+    }
+
+    // Initialize pixel spacing to zero.  This get set when the 
+    // CT is loaded
+    this->ct_pixel_spacing[0] = this->ct_pixel_spacing[1] = 0.f;
+}
+
+Xio_studyset::~Xio_studyset ()
+{
+}
+
+
+Xio_studyset_slice::Xio_studyset_slice (std::string slice_filename_scan, const float slice_location)
+{
+    filename_scan = slice_filename_scan;
+    location = slice_location;
+
+    // Get name from slice filename
+    size_t extension_dot = filename_scan.find_last_of("."); 
+    name = filename_scan.substr(0, extension_dot);
+
+    filename_contours = name + ".WC";
+}
+
+Xio_studyset_slice::~Xio_studyset_slice ()
+{
+}
diff --git a/src/plastimatch/base/xio_studyset.h b/src/plastimatch/base/xio_studyset.h
index 2c3b7fb..b9ad6c7 100644
--- a/src/plastimatch/base/xio_studyset.h
+++ b/src/plastimatch/base/xio_studyset.h
@@ -46,6 +46,9 @@ public:
 public:
     Xio_studyset (const std::string& studyset_dir);
     ~Xio_studyset ();
+
+private:
+    int gcd(int, int);
 };
 
 #endif
diff --git a/src/plastimatch/cli/pcmd_dice.cxx b/src/plastimatch/cli/pcmd_dice.cxx
index ecd724b..cbb1720 100755
--- a/src/plastimatch/cli/pcmd_dice.cxx
+++ b/src/plastimatch/cli/pcmd_dice.cxx
@@ -25,21 +25,6 @@ public:
     }
 };
 
-/* For differing resolutions, resamples image_2 to image_1 */
-void check_resolution (
-    UCharImageType::Pointer *image_1,
-    UCharImageType::Pointer *image_2
-)
-{
-    if ((*image_1)->GetLargestPossibleRegion().GetSize() !=
-        (*image_2)->GetLargestPossibleRegion().GetSize())
-    {
-        Plm_image_header pih;
-        pih.set_from_itk_image (*image_1);
-        *image_2 = resample_image (*image_2, &pih, 0, false);
-    }
-}
-
 static void
 usage_fn (dlib::Plm_clp *parser, int argc, char *argv[])
 {
@@ -118,8 +103,6 @@ do_command_dice (int argc, char *argv[])
     UCharImageType::Pointer image_2 = itk_image_load_uchar (
         parms.test_image_fn, 0);
 
-    check_resolution (&image_1, &image_2);
-
     if (parms.have_dice_option) {
         Dice_statistics ds;
         ds.set_reference_image (image_1);
diff --git a/src/plastimatch/cli/pcmd_filter.cxx b/src/plastimatch/cli/pcmd_filter.cxx
index 5fdf599..17ee0ee 100755
--- a/src/plastimatch/cli/pcmd_filter.cxx
+++ b/src/plastimatch/cli/pcmd_filter.cxx
@@ -15,6 +15,7 @@
 #include "synthetic_mha.h"
 #include "volume_conv.h"
 #include "volume_gaussian.h"
+#include "volume_grad.h"
 
 static Plm_image::Pointer
 create_gabor_kernel (const Filter_parms *parms, const Plm_image::Pointer& img)
@@ -101,6 +102,11 @@ filter_main (Filter_parms* parms)
             parms->gauss_width, 
             2.0);
     }
+    else if (parms->filter_type == Filter_parms::FILTER_TYPE_GRADIENT_MAGNITUDE)
+    {
+        volume_out = volume_gradient_magnitude (
+            img->get_volume_float());
+    }
     else if (parms->filter_type == Filter_parms::FILTER_TYPE_KERNEL)
     {
         /* Not yet implemented */
@@ -161,7 +167,7 @@ parse_fn (
     /* Main pattern */
     parser->add_long_option ("", "pattern",
         "filter type: {"
-        "gabor, gauss, kernel"
+        "gabor, gauss, gm, kernel"
         "}, default is gauss", 
         1, "gauss");
 
@@ -207,6 +213,9 @@ parse_fn (
     else if (arg == "gauss") {
         parms->filter_type = Filter_parms::FILTER_TYPE_GAUSSIAN_SEPARABLE;
     }
+    else if (arg == "gm") {
+        parms->filter_type = Filter_parms::FILTER_TYPE_GRADIENT_MAGNITUDE;
+    }
     else if (arg == "kernel") {
         parms->filter_type = Filter_parms::FILTER_TYPE_KERNEL;
     }
diff --git a/src/plastimatch/cli/pcmd_filter.h b/src/plastimatch/cli/pcmd_filter.h
index 8be17d5..ec24c10 100755
--- a/src/plastimatch/cli/pcmd_filter.h
+++ b/src/plastimatch/cli/pcmd_filter.h
@@ -13,6 +13,7 @@ public:
         FILTER_TYPE_GABOR,
         FILTER_TYPE_GAUSSIAN_COMBINED,
         FILTER_TYPE_GAUSSIAN_SEPARABLE,
+        FILTER_TYPE_GRADIENT_MAGNITUDE,
         FILTER_TYPE_KERNEL
     };
 
diff --git a/src/plastimatch/cli/pcmd_gamma.cxx b/src/plastimatch/cli/pcmd_gamma.cxx
index d0488f4..33b55b5 100755
--- a/src/plastimatch/cli/pcmd_gamma.cxx
+++ b/src/plastimatch/cli/pcmd_gamma.cxx
@@ -32,29 +32,29 @@ gamma_main (Gamma_parms* parms)
 
     if (file_type_ref == PLM_FILE_FMT_DICOM_DOSE) {
         rt_study_ref.load(parms->ref_image_fn.c_str(), file_type_ref);
-        if (rt_study_ref.has_dose()){
-            gdc.set_reference_image(rt_study_ref.get_dose()->clone());
+        if (rt_study_ref.has_dose()) {
+            gdc.set_reference_image (rt_study_ref.get_dose());
         }
-        else{
-            gdc.set_reference_image(parms->ref_image_fn.c_str());
+        else {
+            gdc.set_reference_image (parms->ref_image_fn.c_str());
         }
     }
     else {
-        gdc.set_reference_image(parms->ref_image_fn.c_str());
+        gdc.set_reference_image (parms->ref_image_fn.c_str());
     }
 
     if (file_type_comp == PLM_FILE_FMT_DICOM_DOSE) {
         rt_study_comp.load(parms->cmp_image_fn.c_str(), file_type_comp);
         if (rt_study_comp.has_dose()) {
-            gdc.set_compare_image(rt_study_comp.get_dose()->clone());
+            gdc.set_compare_image (rt_study_comp.get_dose());
         }
         else {
-            gdc.set_compare_image(parms->cmp_image_fn.c_str());
+            gdc.set_compare_image (parms->cmp_image_fn.c_str());
         }
 
     }
     else {
-        gdc.set_compare_image(parms->cmp_image_fn.c_str());
+        gdc.set_compare_image (parms->cmp_image_fn.c_str());
     }
     //End DICOM-RD    
 
@@ -74,6 +74,8 @@ gamma_main (Gamma_parms* parms)
     gdc.set_local_gamma(parms->b_local_gamma);//default: false
     gdc.set_compute_full_region(parms->b_compute_full_region);//default: false
     gdc.set_resample_nn(parms->b_resample_nn); //default: false
+    //gdc.set_include_high_dose_in_comp(parms->b_include_high_dose_comp); //default: false
+    gdc.set_ref_only_threshold(parms->b_ref_only_threshold); //default: false
 
     if (parms->f_inherent_resample_mm > 0.0) {
         gdc.set_inherent_resample_mm(parms->f_inherent_resample_mm);
@@ -153,6 +155,7 @@ parse_fn (
         "(default is 2.0)", 1, "2.0");
 
     /* extended by YK*/
+    parser->add_long_option("", "ref-only-threshold", "With this option, analysis threshold is applied only to ref dose, regardless of the corresponding dose of compare image. ", 0);
     parser->add_long_option("", "interp-search", "With this option, smart interpolation search will be used in points near the reference point. This will eliminate the needs of fine resampling. However, it will take longer time to compute. ", 0);
     parser->add_long_option("", "local-gamma", "With this option, dose difference is calculated based on local dose difference. Otherwise, a given reference dose will be used, which is called global-gamma. ",0);
     parser->add_long_option("", "compute-full-region", "With this option, full gamma map will be generated over the entire image region (even for low-dose region). It is recommended not to use this option to speed up the computation. It has no effect on gamma pass-rate. ", 0);
@@ -209,6 +212,9 @@ parse_fn (
     if (parser->option("interp-search")) {
         parms->b_interp_search = true;
     }
+    if (parser->option("ref-only-threshold")) {
+        parms->b_ref_only_threshold = true;
+    }
 	
     if (parser->option("local-gamma")) {
         parms->b_local_gamma = true;
diff --git a/src/plastimatch/cli/pcmd_gamma.h b/src/plastimatch/cli/pcmd_gamma.h
index 5ebd1dc..6cdbdd8 100755
--- a/src/plastimatch/cli/pcmd_gamma.h
+++ b/src/plastimatch/cli/pcmd_gamma.h
@@ -33,6 +33,8 @@ public:
     bool b_resample_nn; //with this on, nearest resample will be used for comp-to-ref image resampling (as well as inherent resampling for ref image)
 
     bool b_interp_search;
+    /*With this option, analysis threshold is applied only to ref dose, regardless of the corresponding dose of compare image.*/
+    bool b_ref_only_threshold;
 
 public:
     Gamma_parms () {
@@ -47,7 +49,7 @@ public:
         f_analysis_threshold = 0.1;
         b_resample_nn = false;
         b_interp_search = false;
-
+        b_ref_only_threshold = false;
     }
 };
 
diff --git a/src/plastimatch/cli/pcmd_mabs.cxx b/src/plastimatch/cli/pcmd_mabs.cxx
index 56c465a..dc6e061 100755
--- a/src/plastimatch/cli/pcmd_mabs.cxx
+++ b/src/plastimatch/cli/pcmd_mabs.cxx
@@ -57,7 +57,7 @@ parse_fn (
 
     /* Parameters */
     parser->add_long_option ("", "atlas-selection", 
-        "run just atlas selection", 0);
+        "run just atlas selection", 1, "");
     parser->add_long_option ("", "train-atlas-selection", 
         "run just train atlas selection", 0);
     parser->add_long_option ("", "convert", 
@@ -97,6 +97,7 @@ parse_fn (
     /* Parameters */
     if (parser->have_option ("atlas-selection")) {
         parms->atlas_selection = true;
+        parms->input_fn = parser->get_string ("atlas-selection");
     }
     if (parser->have_option ("train-atlas-selection")) {
         parms->train_atlas_selection = true;
@@ -140,20 +141,31 @@ do_command_mabs (int argc, char *argv[])
 
     Mabs mabs;
     mabs.set_parms (&mabs_parms);
+    
+    /* If defined set input ROI file name */
+    if (parms.input_roi_fn != "") {
+        mabs.set_prealign_roi_cmd_name (parms.input_roi_fn);
+    }
 
+    /* Run the right function */
     if (parms.train_atlas_selection) {
+        mabs.set_executed_command("train_atlas_selection");
         mabs.train_atlas_selection ();
     }
     else if (parms.convert) {
+        mabs.set_executed_command("convert");
         mabs.atlas_convert ();
     }
     else if (parms.prealign) {
+        mabs.set_executed_command("prealign");
         mabs.atlas_prealign ();
     }
     else if (parms.train_registration) {
+        mabs.set_executed_command("train_registration");
         mabs.train_registration ();
     }
     else if (parms.train) {
+        mabs.set_executed_command("train");
         mabs.train ();
     }
     else { // can be mabs.atlas_selection() or mabs.segment()
@@ -168,15 +180,14 @@ do_command_mabs (int argc, char *argv[])
         if (parms.output_dicom_dir != "") {
             mabs.set_segment_output_dicom (parms.output_dicom_dir);
         }
-        if (parms.input_roi_fn != "") {
-            mabs.set_segment_input_roi (parms.input_roi_fn);
-        }
        
         /* Run function */
         if (parms.atlas_selection) {
+            mabs.set_executed_command("atlas_selection");
             mabs.atlas_selection ();
         }
         else {
+            mabs.set_executed_command("segment");
             mabs.segment ();
         }
     }
diff --git a/src/plastimatch/cli/pcmd_resample.cxx b/src/plastimatch/cli/pcmd_resample.cxx
index 49b217b..2c5eee5 100644
--- a/src/plastimatch/cli/pcmd_resample.cxx
+++ b/src/plastimatch/cli/pcmd_resample.cxx
@@ -52,6 +52,7 @@ public:
 	m_have_origin = false;
 	m_have_spacing = false;
 	m_have_subsample = false;
+	m_have_direction_cosines = false;
 	default_val = 0.0;
 	have_default_val = false;
 	adjust = 0;
@@ -155,22 +156,34 @@ resample_main (Resample_parms* parms)
 	plm_image.m_itk_uchar 
 	    = do_resample_itk (parms, plm_image.m_itk_uchar);
 	break;
+    case PLM_IMG_TYPE_ITK_CHAR:
+	plm_image.m_itk_char 
+	    = do_resample_itk (parms, plm_image.m_itk_char);
+	break;
+    case PLM_IMG_TYPE_ITK_USHORT:
+	plm_image.m_itk_ushort 
+	    = do_resample_itk (parms, plm_image.m_itk_ushort);
+	break;
     case PLM_IMG_TYPE_ITK_SHORT:
 	plm_image.m_itk_short 
 	    = do_resample_itk (parms, plm_image.m_itk_short);
 	break;
-    case PLM_IMG_TYPE_ITK_LONG:
-	plm_image.m_itk_int32 
-	    = do_resample_itk (parms, plm_image.m_itk_int32);
-	break;
     case PLM_IMG_TYPE_ITK_ULONG:
 	plm_image.m_itk_uint32 
 	    = do_resample_itk (parms, plm_image.m_itk_uint32);
 	break;
+    case PLM_IMG_TYPE_ITK_LONG:
+	plm_image.m_itk_int32 
+	    = do_resample_itk (parms, plm_image.m_itk_int32);
+	break;
     case PLM_IMG_TYPE_ITK_FLOAT:
 	plm_image.m_itk_float 
 	    = do_resample_itk (parms, plm_image.m_itk_float);
 	break;
+    case PLM_IMG_TYPE_ITK_DOUBLE:
+	plm_image.m_itk_double 
+	    = do_resample_itk (parms, plm_image.m_itk_double);
+	break;
     default:
 	print_and_exit ("Unhandled image type in resample_main()\n");
 	break;
diff --git a/src/plastimatch/cli/pcmd_stats.cxx b/src/plastimatch/cli/pcmd_stats.cxx
index 6245eb3..2fd3bbb 100755
--- a/src/plastimatch/cli/pcmd_stats.cxx
+++ b/src/plastimatch/cli/pcmd_stats.cxx
@@ -95,8 +95,8 @@ stats_proj_image_main (Stats_parms* parms, const std::string& current_fn)
     Proj_image *proj;
 
     proj = new Proj_image (current_fn, "");
-    proj_image_debug_header (proj);
-    proj_image_stats (proj);
+    proj->debug_header ();
+    proj->stats ();
     delete proj;
 }
 
diff --git a/src/plastimatch/cli/pcmd_synth.cxx b/src/plastimatch/cli/pcmd_synth.cxx
index 1e9ca83..653b9a0 100755
--- a/src/plastimatch/cli/pcmd_synth.cxx
+++ b/src/plastimatch/cli/pcmd_synth.cxx
@@ -21,6 +21,7 @@ class Synthetic_mha_main_parms
 public:
     std::string output_fn;
     std::string output_dose_img_fn;
+    std::string output_prefix;
     std::string output_ss_img_fn;
     std::string output_ss_list_fn;
     std::string output_dicom;
@@ -41,6 +42,7 @@ do_synthetic_mha (Synthetic_mha_main_parms *parms)
     /* Create image */
     Rt_study rtds;
     if (parms->output_dicom != "" 
+        || parms->output_prefix != ""
         || parms->output_ss_img_fn != ""
         || parms->output_ss_list_fn != "")
     {
@@ -64,12 +66,15 @@ do_synthetic_mha (Synthetic_mha_main_parms *parms)
 
     /* ss_img */
     if (parms->output_ss_img_fn != "") {
-#if (PLM_CONFIG_USE_SS_IMAGE_VEC)
         rtds.get_rtss()->convert_to_uchar_vec ();
-#endif
         rtds.get_rtss()->save_ss_image (parms->output_ss_img_fn);
     }
 
+    /* prefix */
+    if (parms->output_prefix != "") {
+        rtds.get_rtss()->save_prefix (parms->output_prefix);
+    }
+
     /* dose_img */
     if (parms->output_dose_img_fn != "") {
         rtds.save_dose (parms->output_dose_img_fn);
@@ -121,6 +126,8 @@ parse_fn (
         "output dicom directory", 1, "");
     parser->add_long_option ("", "output-dose-img", 
         "filename for output dose image", 1, "");
+    parser->add_long_option ("", "output-prefix", 
+        "create a directory with a separate image for each structure", 1, "");
     parser->add_long_option ("", "output-ss-img", 
         "filename for output structure set image", 1, "");
     parser->add_long_option ("", "output-ss-list", 
@@ -253,6 +260,7 @@ parse_fn (
     parms->output_fn = parser->get_string("output");
     parms->output_dicom = parser->get_string("output-dicom");
     parms->output_dose_img_fn = parser->get_string("output-dose-img");
+    parms->output_prefix = parser->get_string("output-prefix");
     parms->output_ss_img_fn = parser->get_string("output-ss-img");
     parms->output_ss_list_fn = parser->get_string("output-ss-list");
     sm_parms->output_type = plm_image_type_parse (
diff --git a/src/plastimatch/cli/pcmd_warp.cxx b/src/plastimatch/cli/pcmd_warp.cxx
index c48668c..ac74d48 100755
--- a/src/plastimatch/cli/pcmd_warp.cxx
+++ b/src/plastimatch/cli/pcmd_warp.cxx
@@ -61,10 +61,6 @@ parse_fn (
         "input an astroid dose volume", 1, "");
     parser->add_long_option ("", "input-dose-mc", 
         "input an monte carlo volume", 1, "");
-    
-    /* Dij input files */
-    parser->add_long_option ("", "ctatts", 
-        "ct attributes file (used by dij warper)", 1, "");
     parser->add_long_option ("", "dif", 
         "dif file (used by dij warper)", 1, "");
 
@@ -112,6 +108,9 @@ parse_fn (
     parser->add_long_option ("", "dicom-with-uids", 
         "set to false to remove uids from created dicom filenames, "
         "default is true", 1, "true");
+    parser->add_long_option ("", "dij-dose-volumes",
+        "set to true to output nrrd files corresponding to Dij matrix "
+        " beamlets, default is false", 1, "true");
 
     /* Algorithm options */
     parser->add_long_option ("", "algorithm", 
@@ -158,8 +157,20 @@ parse_fn (
         "patient name metadata: string", 1);
     parser->add_long_option ("", "patient-pos",
         "patient position metadata: one of {hfs,hfp,ffs,ffp}", 1, "hfs");
+    parser->add_long_option ("", "study-description",
+        "study description: string", 1);
     parser->add_long_option ("", "series-description",
-        "series description metadata: string", 1);
+        "series description for image metadata: string", 1);
+    parser->add_long_option ("", "dose-series-description",
+        "series description for dose metadata: string", 1);
+    parser->add_long_option ("", "rtss-series-description",
+        "series description for structure metadata: string", 1);
+    parser->add_long_option ("", "series-number",
+        "series number for image metadata: integer", 1);
+    parser->add_long_option ("", "dose-series-number",
+        "series number for dose metadata: integer", 1);
+    parser->add_long_option ("", "rtss-series-number",
+        "series number for structure metadata: integer", 1);
 
     /* Parse options */
     parser->parse (argc,argv);
@@ -202,7 +213,6 @@ parse_fn (
     parms->input_dose_mc_fn = parser->get_string("input-dose-mc").c_str();
 
     /* Dij input files */
-    parms->ctatts_in_fn = parser->get_string("ctatts").c_str();
     parms->dif_in_fn = parser->get_string("dif").c_str();
 
     /* Output files */
@@ -240,6 +250,10 @@ parse_fn (
         parms->dicom_with_uids = string_value_true (
             parser->get_string ("dicom-with-uids"));
     }
+    if (parser->option("dij-dose-volumes")) {
+        parms->output_dij_dose_volumes = string_value_true (
+            parser->get_string ("dij-dose-volumes"));
+    }
 
     /* Algorithm options */
     std::string arg = parser->get_string ("algorithm");
@@ -311,35 +325,65 @@ parse_fn (
 
     /* Metadata options */
     for (unsigned int i = 0; i < parser->option("metadata").count(); i++) {
-        parms->m_metadata.push_back (
+        parms->m_study_metadata.push_back (
             parser->option("metadata").argument(0,i));
     }
     if (parser->option ("modality")) {
         std::string arg = parser->get_string ("modality");
         std::string metadata_string = "0008,0060=" + arg;
-        parms->m_metadata.push_back (metadata_string);
+        parms->m_image_metadata.push_back (metadata_string);
     }
     if (parser->option ("patient-name")) {
         std::string arg = parser->get_string ("patient-name");
         std::string metadata_string = "0010,0010=" + arg;
-        parms->m_metadata.push_back (metadata_string);
+        parms->m_study_metadata.push_back (metadata_string);
     }
     if (parser->option ("patient-id")) {
         std::string arg = parser->get_string ("patient-id");
         std::string metadata_string = "0010,0020=" + arg;
-        parms->m_metadata.push_back (metadata_string);
+        parms->m_study_metadata.push_back (metadata_string);
     }
     if (parser->option ("patient-pos")) {
         std::string arg = parser->get_string ("patient-pos");
         std::transform (arg.begin(), arg.end(), arg.begin(), 
             (int(*)(int)) toupper);
         std::string metadata_string = "0018,5100=" + arg;
-        parms->m_metadata.push_back (metadata_string);
+        parms->m_study_metadata.push_back (metadata_string);
+    }
+    if (parser->option ("study-description")) {
+        std::string arg = parser->get_string ("study-description");
+        std::string metadata_string = "0008,1030=" + arg;
+        parms->m_study_metadata.push_back (metadata_string);
     }
     if (parser->option ("series-description")) {
         std::string arg = parser->get_string ("series-description");
         std::string metadata_string = "0008,103e=" + arg;
-        parms->m_metadata.push_back (metadata_string);
+        parms->m_image_metadata.push_back (metadata_string);
+    }
+    if (parser->option ("dose-series-description")) {
+        std::string arg = parser->get_string ("dose-series-description");
+        std::string metadata_string = "0008,103e=" + arg;
+        parms->m_dose_metadata.push_back (metadata_string);
+    }
+    if (parser->option ("rtss-series-description")) {
+        std::string arg = parser->get_string ("rtss-series-description");
+        std::string metadata_string = "0008,103e=" + arg;
+        parms->m_rtss_metadata.push_back (metadata_string);
+    }
+    if (parser->option ("series-number")) {
+        std::string arg = parser->get_string ("series-number");
+        std::string metadata_string = "0020,0011=" + arg;
+        parms->m_image_metadata.push_back (metadata_string);
+    }
+    if (parser->option ("dose-series-number")) {
+        std::string arg = parser->get_string ("dose-series-number");
+        std::string metadata_string = "0020,0011=" + arg;
+        parms->m_dose_metadata.push_back (metadata_string);
+    }
+    if (parser->option ("rtss-series-number")) {
+        std::string arg = parser->get_string ("rtss-series-number");
+        std::string metadata_string = "0020,0011=" + arg;
+        parms->m_rtss_metadata.push_back (metadata_string);
     }
 }
 
@@ -355,12 +399,12 @@ do_command_warp (int argc, char* argv[])
 
     /* Dij matrices are a special case */
     if (parms.output_dij_fn != "") {
-        if (parms.ctatts_in_fn != "" && parms.dif_in_fn != "")
+        if (parms.dif_in_fn != "")
         {
             warp_dij_main (&parms);
             return;
         } else {
-            print_and_exit ("Sorry, you need to specify --ctatts and --dif for dij warping.\n");
+            print_and_exit ("Sorry, you need to specify --dif for dij warping.\n");
         }
     }
 
diff --git a/src/plastimatch/cli/pcmd_warp_dij.cxx b/src/plastimatch/cli/pcmd_warp_dij.cxx
index 62fefb1..3886d53 100644
--- a/src/plastimatch/cli/pcmd_warp_dij.cxx
+++ b/src/plastimatch/cli/pcmd_warp_dij.cxx
@@ -3,49 +3,43 @@
    ----------------------------------------------------------------------- */
 /*  Warp one or more dij matrices based on a vector field */
 #include "plmcli_config.h"
+#include <fstream>
 #include <math.h>
 #include <time.h>
 #include "itkImage.h"
+#include "itkImageRegionIteratorWithIndex.h"
 #include "itkInterpolateImagePointsFilter.h"
 #include "itkLinearInterpolateImageFunction.h"
 #include "itkVectorLinearInterpolateImageFunction.h"
 
+#include "itk_image_create.h"
+#include "itk_image_save.h"
 #include "itk_image_type.h"
+#include "itk_warp.h"
+#include "logfile.h"
 #include "pcmd_warp.h"
+#include "plm_image_header.h"
 #include "print_and_exit.h"
+#include "string_util.h"
+#include "warp_parms.h"
+#include "xform.h"
 
 typedef unsigned short ushort;
 typedef unsigned long ulong;
 
-typedef struct __Ctatts Ctatts;
-struct __Ctatts {
-    int slice_dimension;
-    int slice_number;
-    float pixel_size;
-    float slice_distance;
-    float pos_isocenter_x;
-    float pos_isocenter_y;
-    float pos_isocenter_z;
-    int num_of_voxels_in_ct_cube_z;
-};
-
-typedef struct __Dif Dif;
-struct __Dif {
-    float delta_x;
-    float delta_y;
-    float delta_z;
-    int dimension_ct_x;
-    int dimension_ct_y;
-    int dimension_ct_z;
-    int dimension_dose_x;
-    int dimension_dose_y;
-    int dimension_dose_z;
-    int iso_index_ct_x;
-    int iso_index_ct_y;
-    int iso_index_ct_z;
-    int iso_index_dose_x;
-    int iso_index_dose_y;
-    int iso_index_dose_z;
+class Dij_header {
+public:
+    plm_long dose_dim[3];
+    float dose_origin[3];
+    float dose_spacing[3];
+public:
+    Dij_header () {
+        for (int d = 0; d < 3; d++) {
+            dose_dim[d] = 0;
+            dose_origin[d] = 0.;
+            dose_spacing[d] = 0.;
+        }
+    }
 };
 
 typedef struct __Pencil_Beam Pencil_Beam;
@@ -101,7 +95,7 @@ dif_parse_error (void)
 }
 
 void
-load_dif (Dif* dif, const char* dif_in)
+load_dif (Dij_header* dijh, const char* dif_in)
 {
     int i;
     float f;
@@ -116,6 +110,8 @@ load_dif (Dif* dif, const char* dif_in)
     }
 
     /* GCS FIX: I should give an error if not all lines are found */
+    /* N.b. This converts from IEC coordinats to DICOM coordinates 
+       during read */
     while (1) {
 	if (!fgets (buf, BUFLEN, fp)) {
 	    break;
@@ -124,203 +120,101 @@ load_dif (Dif* dif, const char* dif_in)
 	    /* Empty lines are ok */
 	}
 	else if (sscanf (buf, "Delta-X %f", &f)) {
-	    dif->delta_x = f;
+	    dijh->dose_spacing[0] = f;
 	}
 	else if (sscanf (buf, "Delta-Y %f", &f)) {
-	    dif->delta_y = f;
+	    dijh->dose_spacing[2] = f;
 	}
 	else if (sscanf (buf, "Delta-Z %f", &f)) {
-	    dif->delta_z = f;
-	}
-	else if (sscanf (buf, "Dimension-CT-X %d", &i)) {
-	    dif->dimension_ct_x = i;
-	}
-	else if (sscanf (buf, "Dimension-CT-Y %d", &i)) {
-	    dif->dimension_ct_y = i;
-	}
-	else if (sscanf (buf, "Dimension-CT-Z %d", &i)) {
-	    dif->dimension_ct_z = i;
+	    dijh->dose_spacing[1] = f;
 	}
 	else if (sscanf (buf, "Dimension-Dose-X %d", &i)) {
-	    dif->dimension_dose_x = i;
+	    dijh->dose_dim[0] = i;
 	}
 	else if (sscanf (buf, "Dimension-Dose-Y %d", &i)) {
-	    dif->dimension_dose_y = i;
+	    dijh->dose_dim[2] = i;
 	}
 	else if (sscanf (buf, "Dimension-Dose-Z %d", &i)) {
-	    dif->dimension_dose_z = i;
-	}
-	else if (sscanf (buf, "ISO-Index-CT-X %d", &i)) {
-	    dif->iso_index_ct_x = i;
-	}
-	else if (sscanf (buf, "ISO-Index-CT-Y %d", &i)) {
-	    dif->iso_index_ct_y = i;
-	}
-	else if (sscanf (buf, "ISO-Index-CT-Z %d", &i)) {
-	    dif->iso_index_ct_z = i;
-	}
-	else if (sscanf (buf, "ISO-Index-Dose-X %d", &i)) {
-	    dif->iso_index_dose_x = i;
-	}
-	else if (sscanf (buf, "ISO-Index-Dose-Y %d", &i)) {
-	    dif->iso_index_dose_y = i;
-	}
-	else if (sscanf (buf, "ISO-Index-Dose-Z %d", &i)) {
-	    dif->iso_index_dose_z = i;
-	}
-	else {
-	    fprintf (stdout, "Bogus Line = %s\n", buf);
-	    dif_parse_error ();
-	}
-    }
-    fclose (fp);
-}
-
-void
-load_ctatts (Ctatts* ctatts, const char* ctatts_in)
-{
-    int i;
-    float f;
-    FILE* fp;
-    const int BUFLEN = 1024;
-    char buf[BUFLEN];
-
-    fp = fopen (ctatts_in, "rt");
-    if (!fp) {
-	fprintf (stderr, "Error opening ctatts file for read: %s\n", ctatts_in);
-	exit (-1);
-    }
-
-    /* Skip first line */
-    fgets (buf, BUFLEN, fp);
-
-    /* GCS FIX: I should give an error if not all lines are found */
-    while (1) {
-	if (!fgets (buf, BUFLEN, fp)) {
-	    break;
-	}
-	if (buf[0] == '\0' || buf[0] == '\n') {
-	    /* Empty lines are ok */
-	}
-	else if (sscanf (buf, "slice_dimension %d", &i)) {
-	    ctatts->slice_dimension = i;
+	    dijh->dose_dim[1] = i;
 	}
-	else if (sscanf (buf, "slice_number %d", &i)) {
-	    ctatts->slice_number = i;
+	else if (sscanf (buf, "Position-Dose-X %f", &f)) {
+	    dijh->dose_origin[0] = f;
 	}
-	else if (sscanf (buf, "pixel_size %f", &f)) {
-	    ctatts->pixel_size = f;
+	else if (sscanf (buf, "Position-Dose-Y %f", &f)) {
+	    dijh->dose_origin[2] = f;
 	}
-	else if (sscanf (buf, "slice_distance %f", &f)) {
-	    ctatts->slice_distance = f;
-	}
-	else if (sscanf (buf, "Pos-Isocenter-X %f", &f)) {
-	    ctatts->pos_isocenter_x = f;
-	}
-	else if (sscanf (buf, "Pos-Isocenter-Y %f", &f)) {
-	    ctatts->pos_isocenter_y = f;
-	}
-	else if (sscanf (buf, "Pos-Isocenter-Z %f", &f)) {
-	    ctatts->pos_isocenter_z = f;
-	}
-	else if (sscanf (buf, "Number-Of-Voxels-in-CT-Cube-Z %d", &i)) {
-	    ctatts->num_of_voxels_in_ct_cube_z = i;
+	else if (sscanf (buf, "Position-Dose-Z %f", &f)) {
+	    dijh->dose_origin[1] = -f;
 	}
 	else {
-	    ctatts_parse_error ();
+            /* Ignore other lines */
 	}
     }
     fclose (fp);
 }
 
-/* This is a "dumb array".  It doesn't know about pixel sizes etc. 
-   (x,y,z) are in beamlet coordinate system (IEC, I think).  */
 FloatImageType::Pointer
-make_output_image (Dij_Matrix* dij_matrix)
+make_dose_image (
+    Dij_header *dijh,
+    Dij_Matrix *dij_matrix)
 {
-    FloatImageType::Pointer image = FloatImageType::New();
-
-    FloatImageType::IndexType start;
-    start[0] =   0;
-    start[1] =   0;
-    start[2] =   0;
-
+    /* GCS FIX: Should check that this value matches the one in 
+       the dij header */
+#if defined (commentout)
     FloatImageType::SizeType size;
     size[0]  = dij_matrix->dose_cube_size[0];
     size[1]  = dij_matrix->dose_cube_size[1];
     size[2]  = dij_matrix->dose_cube_size[2];
-
-    FloatImageType::RegionType region;
-    region.SetSize (size);
-    region.SetIndex (start);
-    image->SetRegions (region);
-
-    image->Allocate ();
-
-    return image;
+#endif
+    return itk_image_create<float> (Plm_image_header (
+            dijh->dose_dim, 
+            dijh->dose_origin,
+            dijh->dose_spacing));
 }
 
 void
-clear_output_image (FloatImageType::Pointer img)
+set_pencil_beam_to_image (
+    FloatImageType::Pointer& img,
+    const Pencil_Beam *pb)
 {
-    typedef itk::ImageRegionIterator< FloatImageType > ImageIterator;
+    typedef itk::ImageRegionIteratorWithIndex< FloatImageType > ImageIterator;
+
+    /* Clear image */
     ImageIterator it1 (img, img->GetBufferedRegion());
     it1.GoToBegin();
     while (!it1.IsAtEnd()) {
+        FloatImageType::IndexType idx = it1.GetIndex();
+	//it1.Set ((float) idx[0]);
 	it1.Set (0.0);
 	++it1;
     }
-}
 
-/* Resample using trilinear interpolation */
-void
-update_output_image (FloatImageType::Pointer img,
-		     ulong* discarded,
-		     Dij_Matrix* dij_matrix,
-		     float dose_wx, float dose_wy, float dose_wz, 
-		     ushort value)
-{
-    int x, y, z;
-    int x0 = (int) floor(dose_wx);
-    int y0 = (int) floor(dose_wy);
-    int z0 = (int) floor(dose_wz);
-    float x0f = 1.0 - (dose_wx - floor(dose_wx));
-    float y0f = 1.0 - (dose_wy - floor(dose_wy));
-    float z0f = 1.0 - (dose_wz - floor(dose_wz));
-    ushort vx[2], vy[2], vz[2];
-
-    FloatImageType::IndexType idx;
-
-    vx[0] = (ushort) (value * x0f + 0.5);
-    vx[1] = value - vx[0];
-    for (x = 0; x < 2; x++) {
-	if (vx[x] == 0) continue;
-	idx[0] = x0 + x;
-	if (idx[0] < 0 || idx[0] >= dij_matrix->dose_cube_size[0]) {
-	    *discarded += vx[x];
-	    continue;
-	}
-	vy[0] = (ushort) (vx[x] * y0f + 0.5);
-	vy[1] = vx[x] - vy[0];
-        for (y = 0; y < 2; y++) {
-	    if (vy[y] == 0) continue;
-	    idx[1] = y0 + y;
-	    if (idx[1] < 0 || idx[1] >= dij_matrix->dose_cube_size[1]) {
-		*discarded += vy[y];
-		continue;
-	    }
-	    vz[0] = (ushort) (vy[y] * z0f + 0.5);
-	    vz[1] = vy[y] - vz[0];
-	    for (z = 0; z < 2; z++) {
-		if (vz[z] == 0) continue;
-		idx[2] = z0 + z;
-		if (idx[2] < 0 || idx[2] >= dij_matrix->dose_cube_size[2]) {
-		    *discarded += vz[z];
-		    continue;
-		}
-		img->SetPixel(idx, img->GetPixel(idx)+vz[z]);
-	    }
-	}
+    /* Set non-zero voxels into image */
+    SizeType sz = img->GetLargestPossibleRegion().GetSize();
+    FloatImageType::IndexType itk_index;
+    for (long i = 0; i < pb->nvox; i++) {
+	/* Get index and value; convert index to DICOM */
+	long pb_index = (pb->vox[i*3+1] << 16) + pb->vox[i*3];
+	ushort value = pb->vox[i*3+2];
+        long iec_index[3];
+        iec_index[0] = pb_index % sz[0];
+        long tmp = pb_index / sz[0];
+        iec_index[1] = tmp % sz[2];
+        iec_index[2] = tmp / sz[2];
+
+	/* Convert index to DICOM */
+        itk_index[2] = (sz[2] - iec_index[1] - 1);
+        itk_index[1] = iec_index[2];
+        itk_index[0] = iec_index[0];
+
+#if defined (commentout)
+        printf ("%d -> %d %d %d\n", pb_index, 
+            itk_index[0],  itk_index[1], itk_index[2]);
+        break;
+#endif
+
+        /* Set voxel */
+        img->SetPixel (itk_index, (float) value);
     }
 }
 
@@ -361,15 +255,25 @@ write_pencil_beam (Pencil_Beam* pb, FloatImageType::Pointer img, FILE* fp)
     rc = fwrite (&pb->nvox, sizeof(int), 1, fp);
     if (rc != 1) dij_write_error();
 
-    typedef itk::ImageRegionIterator< FloatImageType > ImageIterator;
+    SizeType sz = img->GetLargestPossibleRegion().GetSize();
+    typedef itk::ImageRegionIteratorWithIndex< FloatImageType > ImageIterator;
     ImageIterator it1 (img, img->GetBufferedRegion());
     it1.GoToBegin();
     while (!it1.IsAtEnd()) {
 	ushort vox[3];
 	vox[2] = (ushort) it1.Get();
 	if (vox[2] > 0) {
-	    vox[0] = (ushort) index & 0xFF;
-	    vox[1] = (ushort) index << 16;
+            ImageIterator::IndexType idx = it1.GetIndex();
+            long iec_index
+                = idx[1]*sz[2]*sz[0] + (sz[2]-idx[2]-1)*sz[0] + idx[0];
+	    vox[0] = (ushort) (iec_index & 0xFFFF);
+	    vox[1] = (ushort) (iec_index >> 16);
+#if defined (commentout)
+            printf ("%d %d %d -> %d, 0x%x -> 0x%02x 0x%02x\n", 
+                idx[0], idx[1], idx[2], 
+                iec_index, iec_index, vox[0], vox[1]);
+            break;
+#endif
 	    rc = fwrite (vox, sizeof(ushort), 3, fp);
 	    if (rc != 3) dij_write_error();
 	    nvox++;
@@ -442,110 +346,38 @@ write_dij_header (Dij_Matrix* dij_matrix, FILE* fp)
     if (rc != 1) dij_write_error();
 }
 
-void
-warp_pencil_beam (DeformationFieldType::Pointer vf, 
-		  FloatImageType::Pointer oimg, 
-		  Dij_Matrix* dij_matrix, Ctatts* ctatts, 
-		  Dif* dif, Pencil_Beam* pb)
+FloatImageType::Pointer
+warp_pencil_beam (
+    DeformationFieldType::Pointer& vf, 
+    FloatImageType::Pointer& pb_img, 
+    Dij_Matrix *dij_matrix,
+    Dij_header *dijh, 
+    Pencil_Beam *pb)
 {
-    int i;
-    float dose_offset_x, dose_offset_y, dose_offset_z;
-    float vf_origin_x, vf_origin_y, vf_origin_z;
-    ulong total = 0, discarded = 0;
-
-    typedef itk::VectorLinearInterpolateImageFunction <
-			DeformationFieldType, double > InterpolatorType;
-    typedef InterpolatorType::PointType PointType;
-    typedef InterpolatorType::OutputType OutputType;
-    PointType pt;
-    InterpolatorType::Pointer interpolator = InterpolatorType::New();
-    interpolator->SetInputImage(vf);
-
-    /* Compute shift from (pixel-centered) origin in original CT coordinates 
-       to (pixel-centered) origin in konrad dose cube.  The (x,y,z) refer to 
-       konrad coordinates, which conform to IEC. */
-    dose_offset_x = ctatts->pos_isocenter_x - (dif->iso_index_dose_x * dif->delta_x)
-			- (ctatts->pixel_size / 2.0);
-    dose_offset_y = ctatts->pos_isocenter_y - (dif->iso_index_ct_y * dif->delta_y)
-			- (ctatts->slice_distance / 2.0);
-    dose_offset_z = ((ctatts->num_of_voxels_in_ct_cube_z + dif->iso_index_dose_z)
-			* dif->delta_z) - ctatts->pos_isocenter_z
-			- (ctatts->pixel_size / 2.0);
-
-    /* Get origin location within vector field */
-    const DeformationFieldType::PointType& ori = vf->GetOrigin();
-    vf_origin_x = ori[0];
-    vf_origin_y = ori[1];
-    vf_origin_z = ori[2];
-
-    /* Loop: For each voxel of this pencil beam */
-    for (i = 0; i < pb->nvox; i++) {
-	long dose_x, dose_y, dose_z, tmp;
-	float ct_x, ct_y, ct_z;
-	float dose_wx, dose_wy, dose_wz;
-	long index = (pb->vox[i*3+1] << 16) + pb->vox[i*3];
-	ushort value = pb->vox[i*3+2];
-
-	/* Get coordinate of voxel within dose grid (in konrad-IEC coords) */
-	dose_x = index % dif->dimension_dose_x;
-	tmp = (index - dose_x) / dif->dimension_dose_x;
-	dose_y = tmp % dif->dimension_dose_y;
-	dose_z = (tmp - dose_y) / dif->dimension_dose_y;
-
-	/* Compute voxel location in coordinate of original CT (mha-RAI coords) */
-	ct_x = vf_origin_x + dose_offset_x + dose_x * dif->delta_x;
-	ct_y = vf_origin_y + dose_offset_z - dose_z * dif->delta_z;
-	ct_z = vf_origin_z + dose_offset_y + dose_y * dif->delta_y;
-
-	/* Get deformation vector at that voxel location */
-	pt[0] = ct_x; pt[1] = ct_y; pt[2] = ct_z;
-	bool bvalue = interpolator->IsInsideBuffer (pt);
-	if (!bvalue) {
-	    printf ("Warning: beamlet dose (%g %g %g) outside vector field\n",
-		    ct_x, ct_y, ct_z);
-	    continue;
-	}
-	OutputType oval = interpolator->Evaluate (pt);
-
-	/* Accumulate dose to output grid */
-	dose_wx = dose_x + (oval[0] / dif->delta_x);
-	dose_wy = dose_y + (oval[2] / dif->delta_y);
-	dose_wz = dose_z - (oval[1] / dif->delta_z);
-#if defined (commentout)
-	printf ("%g %g %g -> %g %g %g\n", ct_x, ct_y, ct_z,
-		oval[0], oval[1], oval[2]);
-	printf ("%d %d %d -> %g %g %g\n", dose_x, dose_y, dose_z,
-		dose_wx, dose_wy, dose_wz);
-#endif
-	total += value;
-	update_output_image (oimg, &discarded, dij_matrix, 
-			    dose_wx, dose_wy, dose_wz, value);
-    }
-    printf ("Discarded dose: %8lu/%8lu (%6.4f %%)\n", discarded, total, 
-	    ((double)discarded/total));
+    return itk_warp_image (pb_img, vf, 1, 0.f);
 }
 
 void
 convert_vector_field (
-    DeformationFieldType::Pointer vf, 
-    Ctatts* ctatts, 
-    Dif* dif,
-    const char* dij_in, 
-    const char* dij_out)
+    Xform::Pointer& xf,
+    Dij_header* dijh,
+    Warp_parms *parms)
 {
     int i;
     FILE *fp_in, *fp_out;
     Dij_Matrix dij_matrix;
     Pencil_Beam pb;
 
-    fp_in = fopen (dij_in, "rb");
+    fp_in = fopen (parms->input_fn.c_str(), "rb");
     if (!fp_in) {
-	fprintf (stderr, "Error opening dij file for read: %s\n", dij_in);
+	fprintf (stderr, "Error opening dij file for read: %s\n", 
+            parms->input_fn.c_str());
 	exit (-1);
     }
-    fp_out = fopen (dij_out, "wb");
+    fp_out = fopen (parms->output_dij_fn.c_str(), "wb");
     if (!fp_out) {
-	fprintf (stderr, "Error opening dij file for write: %s\n", dij_out);
+	fprintf (stderr, "Error opening dij file for write: %s\n", 
+            parms->output_dij_fn.c_str());
 	exit (-1);
     }
 
@@ -554,15 +386,33 @@ convert_vector_field (
     printf ("Found %d pencil beams\n", dij_matrix.num_pencil_beams);
     write_dij_header (&dij_matrix, fp_out);
 
-    /* Create a new image to hold the warped output */
-    FloatImageType::Pointer oimg = make_output_image (&dij_matrix);
+    /* Create a new image to hold the input_image (gets re-used 
+       for each pencil beam) */
+    FloatImageType::Pointer pb_img = make_dose_image (
+        dijh, &dij_matrix);
+
+    /* Resample vector field to match dose image */
+    printf ("Resampling vector field (please be patient)\n");
+    Plm_image_header pih (pb_img);
+    Xform::Pointer xf2 = xform_to_itk_vf (xf, &pih);
+    DeformationFieldType::Pointer vf = xf2->get_itk_vf ();
 
     /* For each pencil beam, load, warp, and write warped */
     for (i = 0; i < dij_matrix.num_pencil_beams; i++) {
-	clear_output_image (oimg);
+        printf ("Warping PB %03d\n", i);
 	read_pencil_beam (&pb, fp_in);
-	warp_pencil_beam (vf, oimg, &dij_matrix, ctatts, dif, &pb);
-	write_pencil_beam (&pb, oimg, fp_out);
+        set_pencil_beam_to_image (pb_img, &pb);
+        if (parms->output_dij_dose_volumes) {
+            std::string fn = string_format ("PB_%03d.nrrd", i);
+            itk_image_save (pb_img, fn);
+        }
+        FloatImageType::Pointer warped_pb_img 
+            = warp_pencil_beam (vf, pb_img, &dij_matrix, dijh, &pb);
+        if (parms->output_dij_dose_volumes) {
+            std::string fn = string_format ("PBw_%03d.nrrd", i);
+            itk_image_save (warped_pb_img, fn);
+        }
+	write_pencil_beam (&pb, warped_pb_img, fp_out);
 	free (pb.vox);
     }
 
@@ -574,23 +424,13 @@ convert_vector_field (
 void
 warp_dij_main (Warp_parms* parms)
 {
-    print_and_exit (
-	"Warping of Dij matrices has been disabled due to lack of interest.\n"
-	"Please contact plastimatch developers if you need this.\n");
-
-#if defined (commentout)
-    DeformationFieldType::Pointer vf = DeformationFieldType::New();
-    Ctatts ctatts;
-    Dif dif;
+    lprintf ("Loading xf...\n");
+    Xform::Pointer xf = xform_load (parms->xf_in_fn);
 
-    printf ("Loading vector field...\n");
-    vf = itk_image_load_float_field (parms->vf_in_fn);
+    lprintf ("Loading dif...\n");
+    Dij_header dijh;
+    load_dif (&dijh, parms->dif_in_fn.c_str());
 
-    printf ("Loading ctatts and dif...\n");
-    load_ctatts (&ctatts, (const char*) parms->ctatts_in_fn);
-    load_dif (&dif, (const char*) parms->dif_in_fn);
-
-    convert_vector_field (vf, &ctatts, &dif, parms->input_fn, 
-	(const char*) parms->output_dij_fn);
-#endif
+    convert_vector_field (xf, &dijh, 
+        parms);
 }
diff --git a/src/plastimatch/cli/pcmd_xf_convert.cxx b/src/plastimatch/cli/pcmd_xf_convert.cxx
index 8632886..42d6020 100755
--- a/src/plastimatch/cli/pcmd_xf_convert.cxx
+++ b/src/plastimatch/cli/pcmd_xf_convert.cxx
@@ -25,8 +25,10 @@ public:
 
     std::string fixed_image;
     std::string moving_image;
-    std::string registered_rcs;
-    std::string source_rcs;
+    std::string fixed_rcs;
+    std::string moving_rcs;
+
+    std::string series_description;
 
     /* Geometry options */
     bool m_have_dim;
@@ -140,8 +142,8 @@ do_xf_convert (Xf_convert_parms *parms)
     if (!parms->output_dicom_dir.empty()) {
         /* Load referenced image sets */
 #if PLM_DCM_USE_DCMTK
-        Rt_study_metadata::Pointer rtm_reg;
-        Rt_study_metadata::Pointer rtm_src;
+        Rt_study_metadata::Pointer rtm_fixed;
+        Rt_study_metadata::Pointer rtm_moving;
 
         /* Fixed image */
         if (!parms->fixed_image.empty()) {
@@ -150,30 +152,30 @@ do_xf_convert (Xf_convert_parms *parms)
             rtds->load_image (parms->fixed_image);
             std::string fixed_path = parms->output_dicom_dir + "/fixed";
             rtds->save_dicom (fixed_path);
-            rtm_reg = rtds->get_rt_study_metadata();
+            rtm_fixed = rtds->get_rt_study_metadata();
         }
-        else if (!parms->registered_rcs.empty()) {
-            lprintf ("Loading registered...\n");
-            rtm_reg = Rt_study_metadata::load (parms->registered_rcs);
+        else if (!parms->moving_rcs.empty()) {
+            lprintf ("Loading fixed...\n");
+            rtm_fixed = Rt_study_metadata::load (parms->fixed_rcs);
         }
 
         /* Moving image */
         if (!parms->moving_image.empty()) {
             lprintf ("Loading moving...\n");
             Rt_study::Pointer rtds = Rt_study::New ();
-            rtds->load_image (parms->fixed_image);
+            rtds->load_image (parms->moving_image);
             std::string moving_path = parms->output_dicom_dir + "/moving";
             rtds->save_dicom (moving_path);
-            rtm_src = rtds->get_rt_study_metadata();
+            rtm_moving = rtds->get_rt_study_metadata();
         }
-        else if (!parms->source_rcs.empty()) {
-            lprintf ("Loading source...\n");
-            rtm_src = Rt_study_metadata::load (parms->source_rcs);
+        else if (!parms->fixed_rcs.empty()) {
+            lprintf ("Loading moving...\n");
+            rtm_moving = Rt_study_metadata::load (parms->moving_rcs);
         }
 
         lprintf ("Saving sro\n");
         Dcmtk_sro::save (
-            xf_out, rtm_src, rtm_reg, parms->output_dicom_dir, true);
+            xf_out, rtm_fixed, rtm_moving, parms->output_dicom_dir, true);
         lprintf ("Done saving sro\n");
 #endif
     }
@@ -225,13 +227,16 @@ parse_fn (
         "Fixed image, to be converted to dicom", 1, "");
     parser->add_long_option ("", "moving-image",
         "Moving image, to be converted to dicom", 1, "");
-    parser->add_long_option ("", "registered-rcs", 
-        "Directory containing DICOM image with registered reference "
-        "(i.e. fixed image) coordinate system", 
+    parser->add_long_option ("", "fixed-rcs", 
+        "Directory containing DICOM image with reference "
+        "coordinate system of fixed image", 
+        1, "");
+    parser->add_long_option ("", "moving-rcs", 
+        "Directory containing DICOM image with reference "
+        "coordinate system of moving image", 
         1, "");
-    parser->add_long_option ("", "source-rcs", 
-        "Directory containing DICOM image with source reference "
-        "(i.e. moving image) coordinate system", 
+    parser->add_long_option ("", "series-description",
+        "DICOM Series Description",
         1, "");
 
     /* Parse options */
@@ -259,8 +264,8 @@ parse_fn (
     /* DICOM spatial registration */
     parms->fixed_image = parser->get_string("fixed-image");
     parms->moving_image = parser->get_string("moving-image");
-    parms->registered_rcs = parser->get_string("registered-rcs");
-    parms->source_rcs = parser->get_string("source-rcs");
+    parms->fixed_rcs = parser->get_string("fixed-rcs");
+    parms->moving_rcs = parser->get_string("moving-rcs");
 
     Xform_convert *xfc = &parms->xfc;
 
diff --git a/src/plastimatch/cli/plastimatch_main.cxx b/src/plastimatch/cli/plastimatch_main.cxx
index 39c92da..461f8f5 100644
--- a/src/plastimatch/cli/plastimatch_main.cxx
+++ b/src/plastimatch/cli/plastimatch_main.cxx
@@ -274,7 +274,7 @@ main (int argc, char *argv[])
     try {
         do_command (argc, argv);
     } catch (const Plm_exception& pe) {
-        fprintf (stderr, "%s", pe.what());
+        //fprintf (stderr, "%s", pe.what());
         return 1;
     }
 
diff --git a/src/plastimatch/cmake/PlastimatchConfig.cmake.in b/src/plastimatch/cmake/PlastimatchConfig-Legacy.cmake.in
similarity index 100%
copy from src/plastimatch/cmake/PlastimatchConfig.cmake.in
copy to src/plastimatch/cmake/PlastimatchConfig-Legacy.cmake.in
diff --git a/src/plastimatch/cmake/PlastimatchConfig.cmake.in b/src/plastimatch/cmake/PlastimatchConfig.cmake.in
index 63d838c..aa493bd 100755
--- a/src/plastimatch/cmake/PlastimatchConfig.cmake.in
+++ b/src/plastimatch/cmake/PlastimatchConfig.cmake.in
@@ -1,26 +1,14 @@
 ##-----------------------------------------------------------------------------
 ##  PlastimatchConfig.cmake - configuration file for external projects
 ##-----------------------------------------------------------------------------
-## See:
-##  http://www.vtk.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file
-
-get_filename_component (PLASTIMATCH_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
-if (EXISTS "${PLASTIMATCH_CMAKE_DIR}/CMakeCache.txt")
-   # In build tree
-   include("${PLASTIMATCH_CMAKE_DIR}/PlastimatchBuildTreeSettings.cmake")
-else()
-   # In install tree
-   set (PLASTIMATCH_INCLUDE_DIRS 
-     "${PLASTIMATCH_CMAKE_DIR}/@CONF_REL_INCLUDE_DIR@")
-endif()
  
 # Our library dependencies (contains definitions for IMPORTED targets)
-include("${PLASTIMATCH_CMAKE_DIR}/PlastimatchLibraryDepends.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/PlastimatchLibraryDepends.cmake")
 
 # These are IMPORTED targets created by PlastimatchDepends.cmake
 set (PLASTIMATCH_LIBRARIES @EXPORT_TARGET_LIST@)
+set (Plastimatch_LIBRARIES @EXPORT_TARGET_LIST@)
 
 # You need to set these to link with a plastimatch static library
 set (PLASTIMATCH_LDFLAGS "@PLASTIMATCH_LDFLAGS@")
-
-set (PLASTIMATCH_FOUND ON)
+set (Plastimatch_LDFLAGS "@PLASTIMATCH_LDFLAGS@")
diff --git a/src/plastimatch/cuda/CMakeLists.txt b/src/plastimatch/cuda/CMakeLists.txt
index d0c5e57..2085869 100644
--- a/src/plastimatch/cuda/CMakeLists.txt
+++ b/src/plastimatch/cuda/CMakeLists.txt
@@ -53,6 +53,7 @@ if (CUDA_FOUND)
       "${PLMCUDA_LIBRARY_SRC}" 
       "${PLMCUDA_LIBRARY_DEPENDENCIES}"
       ""
+      "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
       "")
   endif ()
 endif ()
diff --git a/src/plastimatch/dose/CMakeLists.txt b/src/plastimatch/dose/CMakeLists.txt
index 11fbb4e..cc0a3ad 100644
--- a/src/plastimatch/dose/CMakeLists.txt
+++ b/src/plastimatch/dose/CMakeLists.txt
@@ -21,12 +21,10 @@ set (PLMDOSE_LIBRARY_SRC
   rt_depth_dose.cxx
   rt_dose.cxx
   rt_lut.cxx
+  rt_mebs.cxx
   rt_parms.cxx
   rt_plan.cxx  
   rt_sigma.cxx
-  rt_sobp.cxx rt_sobp.h 
-  rt_sobp_p.cxx rt_sobp_p.h
-  rt_sobp_optimize.cxx 
   wed_parms.cxx
   )
 
@@ -52,4 +50,5 @@ plm_add_library (
   "${PLMDOSE_LIBRARY_SRC}" 
   "${PLMDOSE_LIBRARY_DEPENDENCIES}"
   ""
+  "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
   "")
diff --git a/src/plastimatch/dose/bragg_curve.cxx b/src/plastimatch/dose/bragg_curve.cxx
index 3e85605..9a8a75a 100644
--- a/src/plastimatch/dose/bragg_curve.cxx
+++ b/src/plastimatch/dose/bragg_curve.cxx
@@ -30,11 +30,10 @@ bragg_curve (
 {
     doublereal v, x, dv[100], dp[100], pdd;
     doublereal D_v_1, D_v_2;
-    double p = 1.77;
-    double alpha = 0.0022;
+    double p = particle_parameters[0][1];
+    double alpha = particle_parameters[0][0];
     double R_0 = alpha * pow (E_0, p);
     double sigma_mono = 0.012 * pow (R_0, 0.935);
-    //double sigma_E0 = 0.01 * E_0;
     double epsilon = 0.1;
 
     double sigma_squared = sigma_mono * sigma_mono 
@@ -80,12 +79,3 @@ bragg_curve (
 
     return bragg;
 }
-
-double bragg_curve_norm (
-	double E_0,         /* in MeV */
-    double sigma_E0,    /* in MeV */
-    double z            /* in mm */
-)
-{
-		return bragg_curve(E_0, sigma_E0, z)/ get_dose_max(E_0);
-}
diff --git a/src/plastimatch/dose/bragg_curve.h b/src/plastimatch/dose/bragg_curve.h
index 8635c88..81f611c 100644
--- a/src/plastimatch/dose/bragg_curve.h
+++ b/src/plastimatch/dose/bragg_curve.h
@@ -14,11 +14,4 @@ bragg_curve (
     double z            /* in mm */
 );
 
-double
-bragg_curve_norm (
-    double E_0,         /* in MeV */
-    double sigma_E0,    /* in MeV */
-    double z            /* in mm */
-);
-
 #endif
diff --git a/src/plastimatch/dose/dose_volume_functions.cxx b/src/plastimatch/dose/dose_volume_functions.cxx
index 70c97f8..d27b49d 100644
--- a/src/plastimatch/dose/dose_volume_functions.cxx
+++ b/src/plastimatch/dose/dose_volume_functions.cxx
@@ -5,6 +5,7 @@
 #include "dose_volume_functions.h"
 #include "proj_volume.h"
 #include "ray_data.h"
+#include "rt_lut.h"
 
 void dose_volume_create(Volume* dose_volume, float* sigma_max, Rpl_volume* volume, double range)
 {
@@ -45,7 +46,7 @@ void dose_volume_create(Volume* dose_volume, float* sigma_max, Rpl_volume* volum
         if (i != 2)
         {   
             spacing[i] = 1;
-            //spacing[i] = volume->get_aperture()->get_spacing(i); would be better...? pblm of lost lateral scattering for high resolution....
+            //spacing[i] = volume->get_aperture()->get_spacing(i); // MD Fix
             dim[i] = (plm_long) (2*abs(first_pixel[i]/spacing[i])+1);
         }
         else
@@ -54,9 +55,7 @@ void dose_volume_create(Volume* dose_volume, float* sigma_max, Rpl_volume* volum
             dim[i] = (plm_long) ((back_clip_useful - volume->get_front_clipping_plane())/spacing[i] + 1);
         }
     }
-
     npix = dim[0]*dim[1]*dim[2];
-
     dose_volume->create(dim, origin, spacing, dc, PT_FLOAT,1);
 }
 
@@ -68,7 +67,7 @@ calculate_rpl_coordinates_xyz(std::vector<std:: vector<double> >* xyz_coordinate
     double ray_bev[3] = {0.0,0.0,0.0};
     double vec_antibug_prt[3] = {0.0,0.0,0.0};
 
-    int dim[3] = {rpl_volume->get_vol()->dim[0],rpl_volume->get_vol()->dim[1],rpl_volume->get_vol()->dim[2]};
+    const plm_long *dim = rpl_volume->get_vol()->dim;
     int idx2d = 0;   
     int idx3d = 0;
 
@@ -124,8 +123,8 @@ dose_volume_reconstruction (
                 ct_xyz[2] = (double) (dose_vol->origin[2] + ct_ijk[2] * dose_vol->spacing[2]);
                 ct_xyz[3] = (double) 1.0;
                 idx = volume_index (dose_vol->dim, ct_ijk);
-
                 dose = rpl_dose_vol->get_rgdepth(ct_xyz);
+
                 if (dose <= 0) {
                     continue;
                 }
@@ -176,8 +175,6 @@ find_xyz_center_entrance(double* xyz_ray_center, double* ray, float z_axis_offse
     xyz_ray_center[0] = z_axis_offset * ray[0];
     xyz_ray_center[1] = z_axis_offset * ray[1];
     xyz_ray_center[2] = z_axis_offset * ray[2];
-
-    return;
 }
 
 void 
@@ -186,11 +183,10 @@ find_xyz_center(double* xyz_ray_center, double* ray, float z_axis_offset, int k,
     float alpha = 0.0f;
 
     xyz_ray_center[2] = z_axis_offset+(double)k * z_spacing;
-
     alpha = xyz_ray_center[2] /(double) ray[2];
     xyz_ray_center[0] = alpha * ray[0];
     xyz_ray_center[1] = alpha * ray[1];
-    return;
+
 }
 
 void 
@@ -201,30 +197,15 @@ find_xyz_from_ijk(double* xyz, Volume* volume, int* ijk)
     xyz[2] = volume->origin[2] + ijk[2]*volume->spacing[2];
 }
 
-double distance(const std::vector< std::vector<double> >& v, int i1, int i2)
-{
-    return sqrt( (v[i1][0]-v[i2][0])*(v[i1][0]-v[i2][0]) + (v[i1][1]-v[i2][1])*(v[i1][1]-v[i2][1]) + (v[i1][2]-v[i2][2])*(v[i1][2]-v[i2][2]) );
-}
-
 double erf_gauss(double x)
 {
-    /* constant */
-    double a1 =  0.254829592;
-    double a2 = -0.284496736;
-    double a3 =  1.421413741;
-    double a4 = -1.453152027;
-    double a5 =  1.061405429;
-    double p  =  0.3275911;
-
-    /* save the sign of x */
     int sign = 1;
     if (x < 0) {sign = -1;}
     x = fabs(x);
 
     /* erf interpolation */
-    double t = 1.0/(1.0 + p*x);
-    double y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*exp(-x*x);
-
+    double t = 1.0/(1.0 + ERF_P*x);
+    double y = 1.0 - (((((ERF_A5*t + ERF_A4)*t) + ERF_A3)*t + ERF_A2)*t + ERF_A1)*t*exp(-x*x);
     return sign*y;
 }
 
@@ -236,8 +217,8 @@ double double_gaussian_interpolation(double* gaussian_center, double* pixel_cent
     double y2 = y1 + spacing[1];
 
     double z = .25 
-        * (erf_gauss((x2-gaussian_center[0])/(sigma*1.4142135)) - erf_gauss((x1-gaussian_center[0])/(sigma*1.4142135)))
-        * (erf_gauss((y2-gaussian_center[1])/(sigma*1.4142135)) - erf_gauss((y1-gaussian_center[1])/(sigma*1.4142135)));
+        * (erf_gauss((x2-gaussian_center[0])/(sigma*M_SQRT2)) - erf_gauss((x1-gaussian_center[0])/(sigma*M_SQRT2)))
+        * (erf_gauss((y2-gaussian_center[1])/(sigma*M_SQRT2)) - erf_gauss((y1-gaussian_center[1])/(sigma*M_SQRT2)));
     return z;
 }
 
@@ -246,3 +227,69 @@ double get_off_axis(double radius, double dr, double sigma)
     return M_PI / 8.0 * sigma * ( exp(- (radius - dr)*(radius -dr) / (2 * sigma * sigma)) -  exp(- (radius + dr)*(radius + dr) / (2 * sigma * sigma)));
 }
 
+/* MD Fix: don't consider any cosines directions */
+void dose_normalization_to_dose(Volume::Pointer dose_volume, double dose, Rt_beam* beam)
+{
+	int idx = 0;
+	double norm = 0;
+	int ijk_max[3] = {0,0,0};
+	float* img = (float*) dose_volume->img;
+
+	for(int i = 0; i < dose_volume->dim[0]; i++)
+	{
+		for(int j = 0; j < dose_volume->dim[1]; j++)
+		{
+			for(int k = 0; k < dose_volume->dim[2]; k++)
+			{
+				idx = i + (dose_volume->dim[0] * (j + dose_volume->dim[1] * k));
+
+				if (img[idx] > norm)
+				{
+					norm = img[idx];
+					ijk_max[0] = i;
+					ijk_max[1] = j;
+					ijk_max[2] = k;
+				}
+			}
+		}
+	}
+	if (norm > 0)
+	{
+		for (int i = 0; i < dose_volume->dim[0] * dose_volume->dim[1] * dose_volume->dim[2]; i++)
+		{
+			img[i] = img[i] / norm * dose;
+		}
+    int ap_dim[2] = {beam->get_aperture()->get_dim(0),beam->get_aperture()->get_dim(1)};
+    beam->get_mebs()->scale_num_part(dose/norm, ap_dim);
+
+		printf("Raw dose at the maximum (%lg, %lg, %lg) : %lg A.U.\nDose normalized at the maximum to ", dose_volume->origin[0] + ijk_max[0] * dose_volume->spacing[0], dose_volume->origin[1] + ijk_max[1] * dose_volume->spacing[1], dose_volume->origin[2] + ijk_max[2] * dose_volume->spacing[2], norm);
+	}
+	else
+	{
+		printf("Dose is null in the entire volume. Please check your input conditions.\n");
+	}
+}
+
+/* MD Fix: don't consider any cosines directions */
+void dose_normalization_to_dose_and_point(Volume::Pointer dose_volume, double dose, const float* rdp_ijk, const float* rdp, Rt_beam* beam)
+{
+    std::vector<float>& num_part = beam->get_mebs()->get_num_particles();
+    double norm = dose_volume->get_ijk_value(rdp_ijk);
+    float* img = (float*) dose_volume->img;
+
+    if (norm > 0)
+    {
+        for (int i = 0; i < dose_volume->dim[0] * dose_volume->dim[1] * dose_volume->dim[2]; i++)
+        {
+            img[i] = img[i] / norm * dose;
+        }
+        int ap_dim[2] = {beam->get_aperture()->get_dim(0),beam->get_aperture()->get_dim(1)};
+        beam->get_mebs()->scale_num_part(dose/norm, ap_dim);
+        printf("Raw dose at the reference dose point (%lg, %lg, %lg) : %lg A.U.\nDose normalized at the reference dose point to ", rdp[0], rdp[1], rdp[2], norm);
+    }
+    else
+    {
+        printf("***WARNING***\nDose null at the reference dose point.\nDose normalized to the dose maximum in the volume.\n");
+        dose_normalization_to_dose(dose_volume, dose,beam);
+    }
+}
diff --git a/src/plastimatch/dose/dose_volume_functions.h b/src/plastimatch/dose/dose_volume_functions.h
index b626536..d17cad9 100644
--- a/src/plastimatch/dose/dose_volume_functions.h
+++ b/src/plastimatch/dose/dose_volume_functions.h
@@ -5,6 +5,7 @@
 #define _dose_volume_functions_h_
 
 #include "rpl_volume.h"
+#include "rt_beam.h"
 #include "volume.h"
 
 void dose_volume_create(Volume* dose_volume, float* sigma_max, Rpl_volume* volume, double range);
@@ -20,11 +21,12 @@ void find_xyz_center_entrance(double* xyz_ray_center, double* ray, float z_axis_
 void find_xyz_center(double* xyz_ray_center, double* ray, float z_axis_offset, int k, float z_spacing);
 void find_xyz_from_ijk(double* xyz, Volume* volume, int* ijk);
 
-double distance(const std::vector< std::vector <double> >&, int, int); // Is this useful??
-
 double erf_gauss(double x);
 double double_gaussian_interpolation(double* gaussian_center, double* pixel_center, double sigma, double* spacing);
 
 double get_off_axis(double radius, double dr, double sigma);
 
+void dose_normalization_to_dose(Volume::Pointer dose_volume, double dose, Rt_beam* beam);
+void dose_normalization_to_dose_and_point(Volume::Pointer dose_volume, double dose, const float* rdp_ijk, const float* rdp, Rt_beam* beam);
+
 #endif
\ No newline at end of file
diff --git a/src/plastimatch/dose/particle_type.cxx b/src/plastimatch/dose/particle_type.cxx
index 0f21202..b95f5a5 100644
--- a/src/plastimatch/dose/particle_type.cxx
+++ b/src/plastimatch/dose/particle_type.cxx
@@ -31,6 +31,9 @@ particle_type_parse (const std::string& s)
     else if (s == "C") {
         return PARTICLE_TYPE_C;
     }
+		else if (s == "N") {
+        return PARTICLE_TYPE_N;
+    }
     else if (s == "O") {
         return PARTICLE_TYPE_O;
     }
@@ -55,6 +58,8 @@ particle_type_string (Particle_type p)
         return "Boron";
     case PARTICLE_TYPE_C:			// carbon
         return "Carbon";
+	case PARTICLE_TYPE_N:			// nitrogen
+		return "Nitrogen";
     case PARTICLE_TYPE_O:			// oxygen
         return "Oxygen";
     default:
diff --git a/src/plastimatch/dose/particle_type.h b/src/plastimatch/dose/particle_type.h
index d2b683e..4f9d228 100644
--- a/src/plastimatch/dose/particle_type.h
+++ b/src/plastimatch/dose/particle_type.h
@@ -7,7 +7,7 @@
 #include "plmdose_config.h"
 #include <string>
 
-/* Particle type: 0=photon, 1= proton, ions: 2= helium, 3=lithium, 4=beryllium, 5=bore, 6=carbon, 8=oxygen */
+/* Particle type: 0=photon, 1= proton, ions: 2= helium, 3=lithium, 4=beryllium, 5=bore, 6=carbon, 7= nitrogen(not used), 8=oxygen */
 enum Particle_type {
     PARTICLE_TYPE_UNKNOWN=-20,
     PARTICLE_TYPE_X=0, 
@@ -16,7 +16,8 @@ enum Particle_type {
     PARTICLE_TYPE_LI=3, 
     PARTICLE_TYPE_BE=4, 
     PARTICLE_TYPE_B=5, 
-    PARTICLE_TYPE_C=6, 
+    PARTICLE_TYPE_C=6,
+	PARTICLE_TYPE_N=7,		// Not used for particle therapy
     PARTICLE_TYPE_O=8
 };
 
diff --git a/src/plastimatch/dose/rt_beam.cxx b/src/plastimatch/dose/rt_beam.cxx
index fd0bbd8..0841b45 100644
--- a/src/plastimatch/dose/rt_beam.cxx
+++ b/src/plastimatch/dose/rt_beam.cxx
@@ -2,9 +2,11 @@
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
 #include "plmdose_config.h"
+#include "proj_volume.h"
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <string_util.h>
 #include <math.h>
 
 #include "bragg_curve.h"
@@ -19,33 +21,20 @@ public:
 
     double source[3];
     double isocenter[3];
-    int detail;
     char flavor;
     char homo_approx;
-    Particle_type part;
-
-    float photon_energy; // energy for mono-energetic beams
 
     float beamWeight;
 
-    Rt_sobp::Pointer sobp;
-
+    Rt_mebs::Pointer mebs;
     std::string debug_dir;
 
     float smearing;
-
-    float prescription_d_min;
-    float prescription_d_max;
-    float proximal_margin;
-    float distal_margin;
-
-    double step_length;
-    float z_min;
-    float z_max;
-    float z_step;
-
+    char rc_MC_model;
     float source_size;
 
+    float step_length;
+
     Aperture::Pointer aperture;
     Plm_image::Pointer target;
 
@@ -58,14 +47,7 @@ public:
     std::string range_compensator_out;
     std::string sigma_out;
     std::string wed_out;
-
-    /* When a new sobp is created from an existing sobp, 
-       the peaks are copied (not manual).  Modifications of 
-       an implicitly defined sobp (by adding a peak) will 
-       delete any existing peaks. */
-    bool have_copied_peaks;
-	bool have_manual_peaks;
-    bool have_prescription;
+    std::string beam_line_type;
 
 public:
     Rt_beam_private ()
@@ -78,48 +60,28 @@ public:
         this->isocenter[0] = 0.f;
         this->isocenter[1] = 0.f;
         this->isocenter[2] = 0.f;
-        this->detail = 1;
         this->flavor = 'a';
         this->homo_approx = 'n';
-        this->part = PARTICLE_TYPE_P;
-
-        this->photon_energy = 6.f; 
 
         this->beamWeight = 1.f;
-
-        this->sobp = Rt_sobp::New();
-
+        this->mebs = Rt_mebs::New();
         this->debug_dir = "";
-
         this->smearing = 0.f;
-
-        this->prescription_d_min = 0.f;
-        this->prescription_d_max = 0.f;
-        this->proximal_margin = 0.f;
-        this->distal_margin = 0.f;
-
-        this->step_length = 1.0;
-        this->z_min = 0.f;
-        this->z_max = 100.f;
-        this->z_step = 1.f;
-
+        this->rc_MC_model = 'n';
         this->source_size = 0.f;
+        this->step_length = 1.f;
 
         aperture = Aperture::New();
 
         this->aperture_in = "";
         this->range_compensator_in = "";
-
         this->aperture_out = "";
         this->proj_dose_out = "";
         this->proj_img_out = "";
         this->range_compensator_out = "";
         this->sigma_out = "";
         this->wed_out = "";
-
-        this->have_copied_peaks = false;
-		this->have_manual_peaks = false;
-        this->have_prescription = false;
+        this->beam_line_type = "active";
     }
     Rt_beam_private (const Rt_beam_private* rtbp)
     {
@@ -131,49 +93,29 @@ public:
         this->isocenter[0] = rtbp->isocenter[0];
         this->isocenter[1] = rtbp->isocenter[1];
         this->isocenter[2] = rtbp->isocenter[2];
-        this->detail = rtbp->detail;
         this->flavor = rtbp->flavor;
         this->homo_approx = rtbp->homo_approx;
-        this->part = rtbp->part;
-
-        this->photon_energy = rtbp->photon_energy;
 
+        /* Copy the mebs object */
         this->beamWeight = rtbp->beamWeight;
-
-        /* Copy the sobp object */
-        this->sobp = Rt_sobp::New (rtbp->sobp);
-
+        this->mebs = Rt_mebs::New (rtbp->mebs);
         this->debug_dir = rtbp->debug_dir;
-
         this->smearing = rtbp->smearing;
-
-        this->prescription_d_min = rtbp->prescription_d_min;
-        this->prescription_d_max = rtbp->prescription_d_max;
-        this->proximal_margin = rtbp->proximal_margin;
-        this->distal_margin = rtbp->distal_margin;
-
-        this->step_length = rtbp->step_length;
-        this->z_min = rtbp->z_min;
-        this->z_max = rtbp->z_max;
-        this->z_step = rtbp->z_step;
-
         this->source_size = rtbp->source_size;
+        this->step_length = rtbp->step_length;
 
         /* Copy the aperture object */
         aperture = Aperture::New (rtbp->aperture);
 
         this->aperture_in = rtbp->aperture_in;
         this->range_compensator_in = rtbp->range_compensator_in;
-
         this->aperture_out = rtbp->aperture_out;
         this->proj_dose_out = rtbp->proj_dose_out;
         this->proj_img_out = rtbp->proj_img_out;
         this->range_compensator_out = rtbp->range_compensator_out;
         this->sigma_out = rtbp->sigma_out;
         this->wed_out = rtbp->wed_out;
-
-        this->have_copied_peaks = true;
-        this->have_prescription = rtbp->have_prescription;
+        this->beam_line_type = rtbp->beam_line_type;
     }
 };
 
@@ -184,11 +126,6 @@ Rt_beam::Rt_beam ()
 
     /* Creation of the volumes useful for dose calculation */
 
-    if (this->get_flavor() == 'a')
-    {    
-        this->aperture_vol = new Rpl_volume();
-    }
-
     if (this->get_flavor() == 'f')
     {    
         this->rpl_ct_vol_HU = new Rpl_volume();
@@ -199,7 +136,6 @@ Rt_beam::Rt_beam ()
     {    
         this->rpl_ct_vol_HU = new Rpl_volume();
         this->sigma_vol = new Rpl_volume();
-
         this->rpl_vol_lg = new Rpl_volume();
         this->rpl_ct_vol_HU_lg = new Rpl_volume();
         this->sigma_vol_lg = new Rpl_volume();
@@ -210,13 +146,10 @@ Rt_beam::Rt_beam ()
     {    
         this->rpl_ct_vol_HU = new Rpl_volume();
         this->sigma_vol = new Rpl_volume();
-
         this->rpl_vol_lg = new Rpl_volume();
         this->rpl_ct_vol_HU_lg = new Rpl_volume();
         this->sigma_vol_lg = new Rpl_volume();
         this->rpl_dose_vol = new Rpl_volume();
-
-        this->aperture_vol = new Rpl_volume();
     }
 }
 
@@ -234,7 +167,6 @@ Rt_beam::Rt_beam (const Rt_beam* rt_beam)
     this->rpl_ct_vol_HU_lg = 0;
     this->sigma_vol_lg = 0;
     this->rpl_dose_vol = 0;
-    this->aperture_vol = 0;
 }
 
 Rt_beam::~Rt_beam ()
@@ -263,13 +195,13 @@ Rt_beam::load (const char* fn)
 }
 
 const double*
-Rt_beam::get_source_position ()
+Rt_beam::get_source_position () const
 {
     return d_ptr->source;
 }
 
 double
-Rt_beam::get_source_position (int dim)
+Rt_beam::get_source_position (int dim) const
 {
     return d_ptr->source[dim];
 }
@@ -291,13 +223,13 @@ Rt_beam::set_source_position (const double* position)
 }
 
 const double*
-Rt_beam::get_isocenter_position ()
+Rt_beam::get_isocenter_position () const
 {
     return d_ptr->isocenter;
 }
 
 double
-Rt_beam::get_isocenter_position (int dim)
+Rt_beam::get_isocenter_position (int dim) const
 {
     return d_ptr->isocenter[dim];
 }
@@ -318,32 +250,10 @@ Rt_beam::set_isocenter_position (const double* position)
     }
 }
 
-void
-Rt_beam::add_peak (
-    double E0,                      /* initial ion energy (MeV) */
-    double spread,                  /* beam energy sigma (MeV) */
-    double dres,                    /* spatial resolution of bragg curve (mm)*/
-    double dmax,                    /* maximum w.e.d. (mm) */
-    double weight)
-{
-    if (d_ptr->have_copied_peaks == true) {
-        d_ptr->sobp->clear_peaks ();
-    }
-    d_ptr->have_copied_peaks = false;
-
-    d_ptr->sobp->add_peak (E0, spread, dres, dmax, weight);
-}
-
-int
-Rt_beam::get_detail (void) const
-{
-    return d_ptr->detail;
-}
-
-void
-Rt_beam::set_detail (int detail)
+double 
+Rt_beam::get_source_distance () const
 {
-    d_ptr->detail = detail;
+    return vec3_dist (d_ptr->isocenter, d_ptr->source);
 }
 
 char
@@ -367,19 +277,13 @@ Rt_beam::get_homo_approx () const
 void 
 Rt_beam::set_homo_approx (char homo_approx)
 {
-  d_ptr->homo_approx = homo_approx;
+	d_ptr->homo_approx = homo_approx;
 }
 
-double 
-Rt_beam::get_sobp_maximum_depth ()
+Rt_mebs::Pointer
+Rt_beam::get_mebs()
 {
-    return d_ptr->sobp->get_maximum_depth ();
-}
-
-Rt_sobp::Pointer
-Rt_beam::get_sobp()
-{
-	return d_ptr->sobp;
+	return d_ptr->mebs;
 }
 
 float
@@ -394,130 +298,254 @@ Rt_beam::set_beam_weight (float beamWeight)
     d_ptr->beamWeight = beamWeight;
 }
 
-float 
-Rt_beam::get_proximal_margin()
-{
-    return d_ptr->proximal_margin;
-}
-	
-float 
-Rt_beam::get_distal_margin()
+void
+Rt_beam::set_rc_MC_model (char rc_MC_model)
 {
-    return d_ptr->distal_margin;
+    d_ptr->rc_MC_model = rc_MC_model;
 }
 
-float 
-Rt_beam::get_prescription_min()
+char
+Rt_beam::get_rc_MC_model (void) const
 {
-    return d_ptr->prescription_d_min;
+    return d_ptr->rc_MC_model;
 }
 
 void
-Rt_beam::set_prescription_min (float prescription_min)
+Rt_beam::set_source_size(float source_size)
 {
-    d_ptr->prescription_d_min = prescription_min;
-    d_ptr->have_prescription = true;
+    d_ptr->source_size = source_size;
 }
 
-float 
-Rt_beam::get_prescription_max()
+float
+Rt_beam::get_source_size() const
 {
-    return d_ptr->prescription_d_max;
+    return d_ptr->source_size;
 }
 
 void
-Rt_beam::set_prescription_max (float prescription_max)
+Rt_beam::set_debug (const std::string& dir)
 {
-    d_ptr->prescription_d_max = prescription_max;
-    d_ptr->have_prescription = true;
+    d_ptr->debug_dir = dir;
 }
 
 void
-Rt_beam::set_proximal_margin (float proximal_margin)
+Rt_beam::dump (const char* dir)
 {
-    d_ptr->proximal_margin = proximal_margin;
-    d_ptr->sobp->set_prescription_min_max (
-        d_ptr->prescription_d_min - d_ptr->proximal_margin,
-        d_ptr->prescription_d_max + d_ptr->distal_margin);
+    d_ptr->mebs->dump (dir);
 }
 
-void
-Rt_beam::set_distal_margin (float distal_margin)
+void 
+Rt_beam::compute_prerequisites_beam_tools(Plm_image::Pointer& target)
 {
-    d_ptr->distal_margin = distal_margin;
-    d_ptr->sobp->set_prescription_min_max (
-        d_ptr->prescription_d_min - d_ptr->proximal_margin,
-        d_ptr->prescription_d_max + d_ptr->distal_margin);
+    if (d_ptr->mebs->get_have_particle_number_map() == true && d_ptr->beam_line_type == "passive")
+    {
+        printf("***WARNING*** Passively scattered beam line with spot map file detected: %s.\nBeam line set to active scanning.\n", d_ptr->mebs->get_particle_number_in().c_str());
+        printf("Any manual peaks set, depth prescription, target or range compensator will not be considered.\n");
+        this->compute_beam_data_from_spot_map();
+        return;
+    }
+
+    /* The priority gets to spot map > manual peaks > dose prescription > target */
+    if (d_ptr->mebs->get_have_particle_number_map() == true)
+    {
+        printf("Spot map file detected: Any manual peaks set, depth prescription, target or range compensator will not be considered.\n");
+        this->compute_beam_data_from_spot_map();
+        return;
+    }
+    if (d_ptr->mebs->get_have_manual_peaks() == true)
+    {
+        printf("Manual peaks detected [PEAKS]: Any prescription or target depth will not be considered.\n");
+        this->get_mebs()->set_have_manual_peaks(true);
+        this->compute_beam_data_from_manual_peaks(target);
+        return;
+    }
+    if (d_ptr->mebs->get_have_prescription() == true)
+    {
+        this->get_mebs()->set_have_prescription(true);
+        /* Apply margins */
+        this->get_mebs()->set_target_depths(d_ptr->mebs->get_prescription_min(), d_ptr->mebs->get_prescription_max());
+        printf("Prescription depths detected. Any target depth will not be considered.\n");
+        this->compute_beam_data_from_prescription(target);
+        return;
+    }
+    if (target->get_vol())
+    {
+        printf("Target detected.\n");
+        this->get_mebs()->set_have_manual_peaks(false);
+        this->get_mebs()->set_have_prescription(false);
+        this->compute_beam_data_from_target(target);
+        return;
+    }
+	
+    /* If we arrive to this point, it is because no beam was defined
+       Creation of a default beam: 100 MeV */
+    printf("***WARNING*** No spot map, manual peaks, depth prescription or target detected.\n");
+    printf("Beam set to a 100 MeV mono-energetic beam. Proximal and distal margins not considered.\n");
+    this->compute_default_beam();
+    return;
 }
 
-void 
-Rt_beam::set_sobp_prescription_min_max (float d_min, float d_max)
+void
+Rt_beam::compute_beam_data_from_spot_map()
 {
-    d_ptr->prescription_d_min = d_min;
-    d_ptr->prescription_d_max = d_max;
-    d_ptr->sobp->set_prescription_min_max (
-        d_ptr->prescription_d_min - d_ptr->proximal_margin,
-        d_ptr->prescription_d_max + d_ptr->distal_margin);
+    this->get_mebs()->clear_depth_dose();
+    this->get_mebs()->extract_particle_number_map_from_txt(this->get_aperture());
+
+    /* If an aperture is defined in the input file, the aperture is erased. 
+       The range compensator is not considered if the beam line is defined as active scanning */
+    this->update_aperture_and_range_compensator();
 }
 
 void
-Rt_beam::set_source_size(float source_size)
+Rt_beam::compute_beam_data_from_manual_peaks(Plm_image::Pointer& target)
 {
-    d_ptr->source_size = source_size;
+    /* The spot map will be identical for passive or scanning beam lines */
+    int ap_dim[2] = {this->get_aperture()->get_dim()[0], this->get_aperture()->get_dim()[1]};
+    this->get_mebs()->generate_part_num_from_weight(ap_dim);
+    if ((target->get_vol() && (d_ptr->aperture_in =="" || d_ptr->range_compensator_in =="")) && (d_ptr->mebs->get_have_manual_peaks() == true || d_ptr->mebs->get_have_prescription() == true)) // we build the associate range compensator and aperture
+    {
+        if (d_ptr->beam_line_type == "active")
+        {
+            this->rpl_vol->compute_beam_modifiers_active_scanning(target->get_vol(), d_ptr->smearing, d_ptr->mebs->get_proximal_margin(), d_ptr->mebs->get_distal_margin());
+        }
+        else
+        {
+            this->rpl_vol->compute_beam_modifiers_passive_scattering(target->get_vol(), d_ptr->smearing, d_ptr->mebs->get_proximal_margin(), d_ptr->mebs->get_distal_margin());
+        }
+    }
+    /* the aperture and range compensator are erased and the ones defined in the input file are considered */
+    this->update_aperture_and_range_compensator();
 }
 
-float
-Rt_beam::get_source_size()
+void
+Rt_beam::compute_beam_data_from_manual_peaks_passive_slicerRt(Plm_image::Pointer& target)
 {
-    return d_ptr->source_size;
+    /* The spot map will be identical for passive or scanning beam lines */
+    int ap_dim[2] = {this->get_aperture()->get_dim()[0], this->get_aperture()->get_dim()[1]};
+    this->get_mebs()->generate_part_num_from_weight(ap_dim);
+
+    this->rpl_vol->compute_beam_modifiers_passive_scattering_slicerRt(target, d_ptr->smearing, d_ptr->mebs->get_proximal_margin(), d_ptr->mebs->get_distal_margin());
+    
+    /* the aperture and range compensator are erased and the ones defined in the input file are considered */
+    this->update_aperture_and_range_compensator();
 }
 
 void
-Rt_beam::set_debug (const std::string& dir)
+Rt_beam::compute_beam_data_from_manual_peaks()
 {
-    d_ptr->debug_dir = dir;
+    /* The spot map will be identical for passive or scanning beam lines */
+    int ap_dim[2] = {this->get_aperture()->get_dim()[0], this->get_aperture()->get_dim()[1]};
+    this->get_mebs()->generate_part_num_from_weight(ap_dim);
+    /* the aperture and range compensator are erased and the ones defined in the input file are considered */
+    this->update_aperture_and_range_compensator();
 }
 
 void
-Rt_beam::optimize_sobp ()
+Rt_beam::compute_beam_data_from_prescription(Plm_image::Pointer& target)
 {
-    d_ptr->sobp->optimize ();
+    /* The spot map will be identical for passive or scanning beam lines */
+    /* Identic to compute from manual peaks, with a preliminary optimization */
+    d_ptr->mebs->optimize_sobp();
+    this->compute_beam_data_from_manual_peaks(target);
 }
 
-bool
-Rt_beam::generate ()
+void
+Rt_beam::compute_beam_data_from_target(Plm_image::Pointer& target)
 {
-    return d_ptr->sobp->generate ();
+    /* Compute beam aperture, range compensator 
+       + SOBP for passively scattered beam lines */
+	
+    if (this->get_beam_line_type() != "passive")
+    {
+        d_ptr->mebs->compute_particle_number_matrix_from_target_active(this->rpl_vol, this->get_target(), d_ptr->smearing);
+    }
+    else
+    {
+        this->compute_beam_modifiers (d_ptr->target->get_vol(), this->get_mebs()->get_min_wed_map(), this->get_mebs()->get_max_wed_map());
+        this->compute_beam_data_from_prescription(target);
+    }
 }
 
-void
-Rt_beam::dump (const char* dir)
+void 
+Rt_beam::compute_default_beam()
 {
-    d_ptr->sobp->dump (dir);
+	/* Computes a default 100 MeV peak */
+	this->get_mebs()->add_peak(100, 1, 1);
+	this->compute_beam_data_from_manual_peaks();
 }
 
-float
-Rt_beam::lookup_sobp_dose (
-    float depth
-)
+void 
+Rt_beam::compute_beam_modifiers (Volume *seg_vol)
 {
-    return d_ptr->sobp->lookup_energy(depth);
+    if (d_ptr->beam_line_type == "active")
+    {
+        this->rpl_vol->compute_beam_modifiers_active_scanning(seg_vol, d_ptr->smearing, d_ptr->mebs->get_proximal_margin(), d_ptr->mebs->get_distal_margin());
+    }
+    else
+    {
+        this->rpl_vol->compute_beam_modifiers_passive_scattering(seg_vol, d_ptr->smearing, d_ptr->mebs->get_proximal_margin(), d_ptr->mebs->get_distal_margin());
+    }
+
+    d_ptr->mebs->set_prescription_depths(this->rpl_vol->get_min_wed(), this->rpl_vol->get_max_wed());
+    this->rpl_vol->apply_beam_modifiers ();
+    return;
 }
 
-void
-Rt_beam::compute_beam_modifiers ()
+void 
+Rt_beam::compute_beam_modifiers (Volume *seg_vol, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max)
 {
-    /* Compute the aperture and compensator */
-    this->rpl_vol->compute_beam_modifiers (
-        this->get_target()->get_vol(), 0);
-    /* Apply smearing */
-    d_ptr->aperture->apply_smearing (d_ptr->smearing);
+    if (d_ptr->beam_line_type == "active")
+    {
+        this->rpl_vol->compute_beam_modifiers_active_scanning(seg_vol, d_ptr->smearing, d_ptr->mebs->get_proximal_margin(), d_ptr->mebs->get_distal_margin(), map_wed_min, map_wed_max);
+    }
+    else
+    {
+        this->rpl_vol->compute_beam_modifiers_passive_scattering(seg_vol, d_ptr->smearing, d_ptr->mebs->get_proximal_margin(), d_ptr->mebs->get_distal_margin(), map_wed_min, map_wed_max);
+    }
+    d_ptr->mebs->set_prescription_depths(this->rpl_vol->get_min_wed(), this->rpl_vol->get_max_wed());
+    this->rpl_vol->apply_beam_modifiers ();
+    return;
 }
 
 void
-Rt_beam::apply_beam_modifiers ()
+Rt_beam::update_aperture_and_range_compensator()
 {
-    this->rpl_vol->apply_beam_modifiers ();
+    /* The aperture is copied from rpl_vol
+       the range compensator and/or the aperture are erased if defined in the input file */
+    if (d_ptr->aperture_in != "")
+    {
+        Plm_image::Pointer ap_img = Plm_image::New (d_ptr->aperture_in, PLM_IMG_TYPE_ITK_UCHAR);
+        this->get_aperture()->set_aperture_image(d_ptr->aperture_in.c_str());
+        this->get_aperture()->set_aperture_volume(ap_img->get_volume_uchar());
+        if (this->rpl_vol->get_minimum_distance_target() == 0) // means that there is no target defined
+        {
+            printf("Smearing applied to the aperture. The smearing width is defined in the aperture frame.\n");
+            d_ptr->aperture->apply_smearing_to_aperture(d_ptr->smearing, d_ptr->aperture->get_distance());
+        }
+        else
+        {
+            printf("Smearing applied to the aperture. The smearing width is defined at the target minimal distance.\n");
+            d_ptr->aperture->apply_smearing_to_aperture(d_ptr->smearing, this->rpl_vol->get_minimum_distance_target());
+        }
+    }
+    /* Set range compensator */
+    if (d_ptr->range_compensator_in != "" && d_ptr->beam_line_type != "active")
+    {
+        Plm_image::Pointer rgc_img = Plm_image::New (d_ptr->range_compensator_in, PLM_IMG_TYPE_ITK_FLOAT);
+        this->get_aperture()->set_range_compensator_image(d_ptr->range_compensator_in.c_str());
+        this->get_aperture()->set_range_compensator_volume(rgc_img->get_volume_float());
+		
+        if (this->rpl_vol->get_minimum_distance_target() == 0) // means that there is no target defined
+        {
+            printf("Smearing applied to the range compensator. The smearing width is defined in the aperture frame.\n");
+            d_ptr->aperture->apply_smearing_to_range_compensator(d_ptr->smearing, d_ptr->aperture->get_distance());
+        }
+        else
+        {
+            printf("Smearing applied to the range compensator. The smearing width is defined at the target minimal distance.\n");
+            d_ptr->aperture->apply_smearing_to_range_compensator(d_ptr->smearing, this->rpl_vol->get_minimum_distance_target());
+        }
+    }
 }
 
 Plm_image::Pointer&
@@ -598,48 +626,28 @@ Rt_beam::set_aperture_spacing (const float ap_spacing[])
     this->get_aperture()->set_spacing (ap_spacing);
 }
 
-void
-Rt_beam::set_smearing (float smearing)
+void 
+Rt_beam::set_step_length(float step)
 {
-    d_ptr->smearing = smearing;
+	d_ptr->step_length = step;
 }
 
 float 
-Rt_beam::get_smearing()
-{
-    return d_ptr->smearing;
-}
-
-void
-Rt_beam::set_step_length (double step_length)
-{
-    d_ptr->step_length = step_length;
-}
-
-double 
 Rt_beam::get_step_length()
 {
-    return d_ptr->step_length;
+	return d_ptr->step_length;
 }
 
 void
-Rt_beam::set_beam_depth (float z_min, float z_max, float z_step)
-{
-    d_ptr->z_min = z_min;
-    d_ptr->z_max = z_max;
-    d_ptr->z_step = z_step;
-}
-
-void 
-Rt_beam::set_particle_type(Particle_type particle_type)
+Rt_beam::set_smearing (float smearing)
 {
-    d_ptr->part = particle_type;
+    d_ptr->smearing = smearing;
 }
 
-Particle_type 
-Rt_beam::get_particle_type()
+float 
+Rt_beam::get_smearing()
 {
-    return d_ptr->part;
+    return d_ptr->smearing;
 }
 
 void 
@@ -739,71 +747,22 @@ Rt_beam::get_wed_out()
 }
 
 void 
-Rt_beam::set_photon_energy(float energy)
-{
-    d_ptr->photon_energy = energy;
-}
-
-float 
-Rt_beam::get_photon_energy()
-{
-    return d_ptr->photon_energy;
-}
-
-void 
-Rt_beam::set_have_prescription(bool have_prescription)
-{
-    d_ptr->have_prescription = have_prescription;
-}
-
-bool 
-Rt_beam::get_have_prescription()
-{
-    return d_ptr->have_prescription;
-}
-
-void 
-Rt_beam::set_have_copied_peaks(bool have_copied_peaks)
-{
-    d_ptr->have_copied_peaks = have_copied_peaks;
-}
-	
-bool 
-Rt_beam::get_have_copied_peaks()
-{
-    return d_ptr->have_copied_peaks;
-}
-
-void 
-Rt_beam::set_have_manual_peaks(bool have_manual_peaks)
+Rt_beam::set_beam_line_type(std::string str)
 {
-    d_ptr->have_manual_peaks = have_manual_peaks;
+	if (str == "active")
+	{
+		d_ptr->beam_line_type = str;
+	}
+	else
+	{
+		d_ptr->beam_line_type = "passive";
+	}
 }
 
-bool 
-Rt_beam::get_have_manual_peaks()
+std::string
+Rt_beam::get_beam_line_type()
 {
-    return d_ptr->have_manual_peaks;
-}
-
-void 
-Rt_beam::copy_sobp(Rt_sobp::Pointer sobp)
-{
-    d_ptr->sobp->set_dose_lut(sobp->get_d_lut(), sobp->get_e_lut(), sobp->get_num_samples()); /* copy also num_samples */
-    d_ptr->sobp->set_dres(sobp->get_dres());
-    d_ptr->sobp->set_eres(sobp->get_eres());
-    d_ptr->sobp->set_E_min(sobp->get_E_min());
-    d_ptr->sobp->set_E_max(sobp->get_E_max());
-    d_ptr->sobp->set_dmin(sobp->get_dmin());
-    d_ptr->sobp->set_dmax(sobp->get_dmax());
-    d_ptr->sobp->set_dend(sobp->get_dend());
-    d_ptr->sobp->set_particle_type(sobp->get_particle_type());
-    d_ptr->sobp->set_p(sobp->get_p());
-    d_ptr->sobp->set_alpha(sobp->get_alpha());
-    d_ptr->sobp->set_prescription_min(sobp->get_prescription_min());
-    d_ptr->sobp->set_prescription_max(sobp->get_prescription_max());
-    d_ptr->sobp->add_weight(sobp->get_weight()[sobp->get_num_peaks()-1]);
-    d_ptr->sobp->add_depth_dose(sobp->get_depth_dose()[sobp->get_num_peaks()-1]);	
+    return d_ptr->beam_line_type;
 }
 
 bool
@@ -886,9 +845,126 @@ Rt_beam::load_txt (const char* fn)
         this->e_lut[this->num_samples-1] = dose;
         this->dmax = range;         /* Assume entries are sorted */
     }
-
     fclose (fp);
 #endif
     return true;
 }
 
+bool
+Rt_beam::get_intersection_with_aperture(double* idx_ap, int* idx, double* rest, double* ct_xyz)
+{
+	double ray[3] = {0,0,0};
+	double length_on_normal_axis = 0;
+	
+	vec3_copy(ray, ct_xyz);
+	vec3_sub2(ray, d_ptr->source);
+
+	length_on_normal_axis = -vec3_dot(ray, rpl_ct_vol_HU->get_proj_volume()->get_nrm()); // MD Fix: why is the aperture not updated at this point? and why proj vol is?
+	if (length_on_normal_axis < 0)
+	{
+		return false;
+	}
+
+	vec3_scale2(ray, this->get_aperture()->get_distance()/length_on_normal_axis);
+
+	vec3_add2(ray, d_ptr->source);
+	vec3_sub2(ray, rpl_ct_vol_HU->get_proj_volume()->get_ul_room());
+					
+	idx_ap[0] = vec3_dot(ray, rpl_ct_vol_HU->get_proj_volume()->get_incr_c()) / (this->get_aperture()->get_spacing(0) * this->get_aperture()->get_spacing(0));
+	idx_ap[1] = vec3_dot(ray, rpl_ct_vol_HU->get_proj_volume()->get_incr_r()) / (this->get_aperture()->get_spacing(1) * this->get_aperture()->get_spacing(1));
+	idx[0] = (int) floor(idx_ap[0]);
+	idx[1] = (int) floor(idx_ap[1]);
+	rest[0] = idx_ap[0] - (double) idx[0];
+	rest[1] = idx_ap[1] - (double) idx[1];
+	return true;
+}
+
+bool 
+Rt_beam::is_ray_in_the_aperture(int* idx, unsigned char* ap_img)
+{
+	if ((float) ap_img[idx[0] + idx[1] * this->get_aperture()->get_dim(0)] == 0) {return false;}
+	if (idx[0] + 1 < this->get_aperture()->get_dim(0))
+	{
+		if ((float) ap_img[idx[0] + 1 + idx[1] * this->get_aperture()->get_dim(0)] == 0) {return false;}
+	}
+	if (idx[1] + 1 < this->get_aperture()->get_dim(1))
+	{
+		if ((float) ap_img[idx[0] + (idx[1] + 1) * this->get_aperture()->get_dim(0)] == 0) {return false;}
+	}
+	if (idx[0] + 1 < this->get_aperture()->get_dim(0) && idx[1] + 1 < this->get_aperture()->get_dim(1))
+	{
+		if ((float) ap_img[idx[0] + 1 + (idx[1] + 1) * this->get_aperture()->get_dim(0)] == 0) {return false;}
+	}
+	 return true;
+}
+
+float 
+Rt_beam::compute_minimal_target_distance(Volume* target_vol, float background)
+{
+    float* target_img = (float*) target_vol->img;
+
+    float min = FLT_MAX;
+    int idx = 0;
+    const plm_long *dim = target_vol->dim;
+    float target_image_origin[3] = {target_vol->origin[0], target_vol->origin[1], target_vol->origin[2]};
+    float target_image_spacing[3] = {target_vol->spacing[0], target_vol->spacing[1], target_vol->spacing[2]};
+    float source[3] = {(float) this->get_source_position(0), (float) this->get_source_position(1), (float) this->get_source_position(2)};
+
+    float voxel_xyz[3] = {0, 0, 0};
+    float min_tmp;
+
+    for (int k = 0; k < dim[2]; k++) 
+    {
+        for (int j = 0; j < dim[1]; j++) 
+        {
+            for (int i = 0; i < dim[0]; i++) 
+            {
+                idx = i + (dim[0] * (j + dim[1] * k));
+                if (target_img[idx] > background)
+                {
+                    voxel_xyz[0] = target_image_origin[0] + (float) i * target_image_spacing[0];
+                    voxel_xyz[1] = target_image_origin[1] + (float) j * target_image_spacing[1];
+                    voxel_xyz[2] = target_image_origin[2] + (float) k * target_image_spacing[2];
+                    min_tmp = vec3_dist(voxel_xyz, source);
+                    if (min_tmp < min) {min = min_tmp;}
+                }
+            }
+        }
+    }
+    return min;
+}
+
+void Rt_beam::set_energy_resolution (float eres)
+{
+    d_ptr->mebs->set_energy_resolution (eres);
+}
+
+float Rt_beam::get_energy_resolution () const
+{
+    return d_ptr->mebs->get_energy_resolution ();
+}
+
+void Rt_beam::set_proximal_margin (float proximal_margin)
+{
+    d_ptr->mebs->set_proximal_margin (proximal_margin);
+}
+
+float Rt_beam::get_proximal_margin () const
+{
+    return d_ptr->mebs->get_proximal_margin ();
+}
+
+void Rt_beam::set_distal_margin (float distal_margin)
+{
+    d_ptr->mebs->set_distal_margin (distal_margin);
+}
+
+float Rt_beam::get_distal_margin () const
+{
+    return d_ptr->mebs->get_distal_margin ();
+}
+
+void Rt_beam::set_prescription (float prescription_min, float prescription_max)
+{
+    d_ptr->mebs->set_prescription (prescription_min, prescription_max);
+}
diff --git a/src/plastimatch/dose/rt_beam.h b/src/plastimatch/dose/rt_beam.h
index 0428289..d4ec0a5 100644
--- a/src/plastimatch/dose/rt_beam.h
+++ b/src/plastimatch/dose/rt_beam.h
@@ -4,15 +4,19 @@
 #ifndef _rt_beam_h_
 #define _rt_beam_h_
 
+#include "file_util.h"
 #include "plmdose_config.h"
 #include <string>
+#include <vector>
 
 #include "aperture.h"
+#include "particle_type.h"
 #include "rpl_volume.h"
-#include "rt_sobp.h"
+#include "rt_mebs.h"
+#include "smart_pointer.h"
 
 class Rt_beam_private;
-class Rt_sobp;
+class Rt_mebs;
 
 /*! \brief 
  * The Rt_beam class encapsulates a single SOBP Rt beam, including 
@@ -20,51 +24,41 @@ class Rt_sobp;
  */
 class PLMDOSE_API Rt_beam {
 public:
+    SMART_POINTER_SUPPORT (Rt_beam);
+    Rt_beam_private *d_ptr;
+public:
     Rt_beam ();
     Rt_beam (const Rt_beam* rt_beam);
     ~Rt_beam ();
-public:
-    Rt_beam_private *d_ptr;
 
 public:
     /*! \name Inputs */
-    ///@{
     /*! \brief Load PDD from XiO or txt file */
     bool load (const char* fn);
 
     /*! \brief Get the position of the beam source in world coordinates. */
-    const double* get_source_position ();
+    const double* get_source_position () const;
     /*! \brief Get the x, y, or z coordinate of the beam source 
       in world coordinates. */
-    double get_source_position (int dim);
+    double get_source_position (int dim) const;
     /*! \brief Set the position of the beam source in world coordinates. */
     void set_source_position (const float position[3]);
     /*! \brief Set the position of the beam source in world coordinates. */
     void set_source_position (const double position[3]);
 
     /*! \brief Get the position of the beam isocenter in world coordinates. */
-    const double* get_isocenter_position ();
+    const double* get_isocenter_position () const;
     /*! \brief Get the x, y, or z coordinate of the beam source 
       in world coordinates. */
-    double get_isocenter_position (int dim);
+    double get_isocenter_position (int dim) const;
     /*! \brief Set the position of the beam isocenter in world coordinates. */
     void set_isocenter_position (const float position[3]);
     /*! \brief Set the position of the beam isocenter in world coordinates. */
     void set_isocenter_position (const double position[3]);
 
-    /*! \brief Add an SOBP pristine peak to this beam */
-    void add_peak ();              /* an empty peak */
-    void add_peak (
-        double E0,                 /* initial ion energy (MeV) */
-        double spread,             /* beam energy sigma (MeV) */
-        double dres,               /* spatial resolution of bragg curve (mm)*/
-        double dmax,               /* maximum w.e.d. (mm) */
-        double weight);
-
-    /*! \brief Get "detail" parameter of dose calculation algorithm */
-    int get_detail () const;
-    /*! \brief Set "detail" parameter of dose calculation algorithm */
-    void set_detail (int detail);
+    /*! \brief Get the source distance. */
+    double get_source_distance () const;
+    
     /*! \brief Get "flavor" parameter of dose calculation algorithm */
     char get_flavor () const;
     /*! \brief Set "flavor" parameter of dose calculation algorithm */
@@ -75,63 +69,49 @@ public:
     /*! \brief Set "homo_approx" parameter of dose calculation algorithm */
     void set_homo_approx (char homo_approx);
 
-    /*! \brief Get maximum depth (in mm) in SOBP curve */
-    double get_sobp_maximum_depth ();
-
-    /*! \brief Get Sobp */
-    Rt_sobp::Pointer get_sobp();
+    /*! \brief Get mebs */
+    Rt_mebs::Pointer get_mebs();
 
     /*! \brief Get "beam_weight" parameter of dose calculation algorithm */
     float get_beam_weight () const;
     /*! \brief Set "beam_weight" parameter of dose calculation algorithm */
     void set_beam_weight (float beam_weight);
 
-    /*! \Get proximal, distal margins and prescription */
-    float get_proximal_margin();
-    float get_distal_margin();
-    float get_prescription_min();
-    void set_prescription_min (float prescription_min);
-    float get_prescription_max();
-    void set_prescription_max (float prescription_max);
-
-    /*! \brief Set/Get proximal margin; this is subtracted from the 
-      minimum depth */
-    void set_proximal_margin (float proximal_margin);
-    /*! \brief Set distal margin; this is added onto the prescription
-      maximum depth */
-    void set_distal_margin (float distal_margin);
-    /*! \brief Set SOBP range and modulation for prescription 
-      as minimum and maximum depth (in mm) */
-    void set_sobp_prescription_min_max (float d_min, float d_max);
+    /*! \brief Get "rc_MC_model" for the model of the range compensator, y = Monte Carlo, n = Highland */
+    char get_rc_MC_model () const;
+    /*! \brief Set "rc_MC_model" for the model of the range compensator, y = Monte Carlo, n = Highland */
+    void set_rc_MC_model (char rc_MC_model);
 
     /* Set source size in mm */
     void set_source_size(float source_size);
 
     /* Get source size in mm */
-    float get_source_size();
+    float get_source_size() const;
 
     /*! \brief Request debugging information to be written to directory */
     void set_debug (const std::string& dir);
 
-    ///@}
-
-    /*! \name Execution */
-    ///@{
-    void optimize_sobp ();          /* automatically create, weigh peaks */
-    bool generate ();               /* use manually weighted peaks */
-    ///@}
-
     /*! \name Outputs */
-    ///@{
     void dump (const char* dir);     /* Print debugging information */
-    float lookup_sobp_dose (float depth);
-    ///@}
+
+    /* Compute beam modifiers, SOBP etc. according to the teatment strategy */
+    void compute_prerequisites_beam_tools(Plm_image::Pointer& target);
+
+    /* Different strategies preparation */
+    void compute_beam_data_from_spot_map();
+    void compute_beam_data_from_manual_peaks();
+    void compute_beam_data_from_manual_peaks(Plm_image::Pointer& target);
+    void compute_beam_data_from_manual_peaks_passive_slicerRt(Plm_image::Pointer& target);
+    void compute_beam_data_from_prescription(Plm_image::Pointer& target);
+    void compute_beam_data_from_target(Plm_image::Pointer& target);
+    void compute_default_beam();
 
     /* This computes the aperture and range compensator */
-    void compute_beam_modifiers ();
+    void compute_beam_modifiers (Volume *seg_vol);
+    void compute_beam_modifiers (Volume *seg_vol, std::vector<double>& map_wed_min, std::vector<double>& map_wed_max); // returns also the wed max and min maps
 
-    /* This modifies the rpl_volume to account for aperture and range compensator */
-    void apply_beam_modifiers ();
+    /* copy the aperture and range compensator from the rpl_vol if not defined in the input file */
+    void update_aperture_and_range_compensator();
 
     /* Set/ Get target */
     Plm_image::Pointer& get_target ();
@@ -152,20 +132,13 @@ public:
     void set_aperture_resolution (const int[]);
     void set_aperture_spacing (const float[]);
 
+    void set_step_length(float step);
+    float get_step_length();
+
     /* Set smearing */
     void set_smearing (float smearing);
     float get_smearing();
 
-    /* Set/Get step_length */
-    void set_step_length (double ray_step);
-    double get_step_length();
-
-    void set_beam_depth (float z_min, float z_max, float z_step);
-
-    /* set the type of particle (proton, helium ions, carbon ions...)*/
-    void set_particle_type(Particle_type particle_type);
-    Particle_type get_particle_type();
-
     /* Set/Get intput file names */
     void set_aperture_in (const std::string& str);
     std::string get_aperture_in();
@@ -192,20 +165,25 @@ public:
     void set_wed_out(std::string str);
     std::string get_wed_out();
 
-    void set_photon_energy(float energy);
-    float get_photon_energy();
-
-    void set_have_prescription(bool have_prescription);
-    bool get_have_prescription();
-
-    void set_have_copied_peaks(bool have_copied_peaks);
-    bool get_have_copied_peaks();
+    void set_beam_line_type(std::string str);
+    std::string get_beam_line_type();
 
-	void set_have_manual_peaks(bool have_manual_peaks);
-	bool get_have_manual_peaks();
+    bool get_intersection_with_aperture(double* idx_ap, int* idx, double* rest, double* ct_xyz);
+    bool is_ray_in_the_aperture(int* idx, unsigned char* ap_img);
 
-    void copy_sobp(Rt_sobp::Pointer sobp);
+    /* computes the minimal geometric distance of the target for this beam
+       -- used for smearing */
+    float compute_minimal_target_distance(Volume* target_vol, float background);
 
+    /* functions that pass through to mebs object */
+    void set_energy_resolution (float eres);
+    float get_energy_resolution () const;
+    void set_proximal_margin (float proximal_margin);
+    float get_proximal_margin() const;
+    void set_distal_margin (float distal_margin);
+    float get_distal_margin() const;
+    void set_prescription (float prescription_min, float prescription_max);
+    
 public: 
 
     /* Volumes useful for dose calculation */
@@ -213,20 +191,16 @@ public:
     Rpl_volume* rpl_vol; // contains the radiologic path length along a ray
     Rpl_volume* rpl_ct_vol_HU; // contains the HU units along the ray
     Rpl_volume* sigma_vol;  // contains the sigma (lateral spread of the pencil beam - used to calculate the off-axis term) along the ray
-    	
+
     /* larger volumes for Hong and divergent geometry algorithms */
     Rpl_volume* rpl_vol_lg;
     Rpl_volume* rpl_ct_vol_HU_lg;
     Rpl_volume* sigma_vol_lg;
     Rpl_volume* rpl_dose_vol; // contains the dose vol for the divergent geometry algorithm
-
-    /* aperture 3D volume to avoid artefacts*/
-    Rpl_volume* aperture_vol;
-
+    
 private:
     bool load_xio (const char* fn);
     bool load_txt (const char* fn);
-
 };
 
 #endif
diff --git a/src/plastimatch/dose/rt_depth_dose.cxx b/src/plastimatch/dose/rt_depth_dose.cxx
index 0d06da6..8149bb2 100644
--- a/src/plastimatch/dose/rt_depth_dose.cxx
+++ b/src/plastimatch/dose/rt_depth_dose.cxx
@@ -19,14 +19,14 @@ Rt_depth_dose::Rt_depth_dose ()
     this->E0 = 100.0;
     this->spread = 1.0;
     this->dres = .01;
-    this->dmax = 400.0;
-    this->weight = 1.0;
+    this->dend = 400.0;
+	this->index_of_dose_max = 0;
 
     this->num_samples = 40000;
 }
 
 Rt_depth_dose::Rt_depth_dose (
-    double E0, double spread, double dres, double dmax, double weight)
+    double E0, double spread, double dres, double dmax)
 {
     this->d_lut = NULL;
     this->e_lut = NULL;
@@ -35,9 +35,6 @@ Rt_depth_dose::Rt_depth_dose (
     this->E0 = E0;
     this->spread = spread;
     this->dres = dres;
-    this->dmax = dmax;
-    this->weight = weight;
-
     this->generate();
 }
 
@@ -110,7 +107,7 @@ Rt_depth_dose::load_xio (const char* fn)
             ptoken = strtok (NULL, ",\n\0");
         }
     }
-    this->dmax = this->d_lut[j-1];
+    this->dend = this->d_lut[j-1];
 
     /* load in the energies (10 samples per line) */
     for (i=0, j=0; i<(this->num_samples/10)+1; i++) {
@@ -145,12 +142,14 @@ Rt_depth_dose::load_txt (const char* fn)
     FILE* fp = fopen (fn, "r");
 
     while (fgets (linebuf, 128, fp)) {
-        float range, dose, dose_int;
+        float range, dose;
+				float dose_int =0;
 
         if (2 != sscanf (linebuf, "%f %f", &range, &dose)) {
             break;
         }
 
+				dose_int += dose;
         this->num_samples++;
         this->d_lut = (float*) realloc (
                         this->d_lut,
@@ -159,16 +158,15 @@ Rt_depth_dose::load_txt (const char* fn)
         this->e_lut = (float*) realloc (
                         this->e_lut,
                         this->num_samples * sizeof(float));
-		this->f_lut = (float*) realloc (
+				this->f_lut = (float*) realloc (
 						this->f_lut,
 						this->num_samples * sizeof(float));
 
         this->d_lut[this->num_samples-1] = range;
         this->e_lut[this->num_samples-1] = dose;
-		this->f_lut[this->num_samples-1] = dose_int;
-        this->dmax = range;         /* Assume entries are sorted */
+				this->f_lut[this->num_samples-1] = dose_int;
+        this->dend = range;         /* Assume entries are sorted */
     }
-
     fclose (fp);
     return true;
 }
@@ -179,6 +177,18 @@ Rt_depth_dose::generate ()
     int i;
     double d;
 
+	float max_prep = -1;
+	float depth = -1;
+  if (this->E0 > 190) {depth = 240;} // To accelerate the process and avoid the region where the dose decreases in the first mm (mathematic model) when E>190...
+	float bragg = 0;
+	while (bragg > max_prep)
+	{
+		max_prep = bragg;
+		depth++;
+		bragg = bragg_curve(this->E0, this->spread, depth);
+	}
+	this->dend = depth + 20; // 2 cm margins after the Bragg peak
+
 #if SPECFUN_FOUND
     if (!this->E0) {
         printf ("ERROR: Failed to generate beam -- energy not specified.\n");
@@ -188,13 +198,11 @@ Rt_depth_dose::generate ()
         printf ("ERROR: Failed to generate beam -- energy spread not specified.\n");
         return false;
     }
-    if (!this->dmax) {
+    if (!this->dend) {
         printf ("ERROR: Failed to generate beam -- max depth not specified.\n");
         return false;
     }
-
-    this->num_samples = (int) floorf (this->dmax / this->dres);
-
+    this->num_samples = (int) ceilf (this->dend / this->dres)+1;
     this->d_lut = (float*) malloc (this->num_samples*sizeof(float));
     this->e_lut = (float*) malloc (this->num_samples*sizeof(float));
 	this->f_lut = (float*) malloc (this->num_samples*sizeof(float));
@@ -205,9 +213,39 @@ Rt_depth_dose::generate ()
 
     for (d=0, i=0; i<this->num_samples; d+=this->dres, i++) {
         d_lut[i] = d;
-		e_lut[i] = bragg_curve_norm (this->E0, this->spread, d)*this->dres;
-		if (d == 0) {f_lut[i] = e_lut[i];} else {f_lut[i] = f_lut[i-1] + e_lut[i];}
+		e_lut[i] = bragg_curve (this->E0, this->spread, d);
     }
+	float max = 0;
+	if (this->num_samples > 0) 
+	{ 
+		max = e_lut[0];
+		for (int k = 1; k < this->num_samples; k++)
+		{
+			if (e_lut[k] > max)
+			{
+				max = e_lut[k];
+				this->index_of_dose_max = k;
+			}
+		}
+	
+		/* normalization and creation of the accumulated dose curve */
+		if (max > 0)
+		{
+			e_lut[0] /= max;
+			f_lut[0] = e_lut[0] * this->dres;
+			for (int k = 1; k < this->num_samples; k++)
+			{
+				e_lut[k] /= max;
+				f_lut[k] = f_lut[k-1] + e_lut[k]*this->dres;
+			}
+		}
+		else
+		{
+			printf("Error: Depth dose curve must have at least one value > 0.\n");
+			return false;
+		}
+	}
+
     return true;
 #else
     printf ("ERROR: No specfun found.\n");
@@ -223,19 +261,23 @@ Rt_depth_dose::dump (const char* fn) const
     for (int i=0; i<this->num_samples; i++) {
        fprintf (fp, "%3.2f %3.2f\n", this->d_lut[i], this->e_lut[i]);
     }
-
     fclose (fp);
 }
 
+int 
+Rt_depth_dose::get_index_of_dose_max()
+{
+	return index_of_dose_max;
+}
+
 float
 Rt_depth_dose::lookup_energy_integration (float depth, float dz) const
-{	
-    int i;
-	int j;
-    float energy = 0.0f;
+{
+	int i = 0;
+	int j = 0;
 
+    float energy = 0.0f;
 	float dres = this->dres;
-
 	float dmin = depth - dz/2.0;
 	float dmax = depth + dz/2.0;
 
@@ -252,7 +294,7 @@ Rt_depth_dose::lookup_energy_integration (float depth, float dz) const
         }
     }
 
-	for (j = i; j < this->num_samples-1; j++) {
+	for (j = i; j < this->num_samples; j++) {
 		if (this->d_lut[j] > dmax) {
 			j--;
 			break;
@@ -267,7 +309,7 @@ Rt_depth_dose::lookup_energy_integration (float depth, float dz) const
             * ((this->f_lut[j+1] - this->f_lut[j]) 
                 / (this->d_lut[j+1] - this->d_lut[j]));
     } 
-	else //(j == num_samples-1)
+	else
 	{
 		energy = this->f_lut[num_samples-1];
 	}
@@ -279,10 +321,48 @@ Rt_depth_dose::lookup_energy_integration (float depth, float dz) const
             * ((this->f_lut[i+1] - this->f_lut[i]) 
                 / (this->d_lut[i+1] - this->d_lut[i]));
     } 
-	else if (i == num_samples-1) //(i == num_samples-1)
+	else if (i == num_samples-1)
 	{
 		energy -= this->f_lut[num_samples-1];
 	}
-
-	return energy;  
+	return energy;
 }
+
+float
+Rt_depth_dose::lookup_energy (
+    float depth)
+{	
+    int i = 0;
+    float energy = 0.0f;
+
+    /* Sanity check */
+    if (depth < 0 || depth > dend) {
+        return 0.0f;
+    }
+
+    /* Find index into profile arrays */
+    for (i = (int) floor(depth / dres); i < num_samples-1; i++) {
+        if (d_lut[i] > depth) {
+            i--;
+            break;
+        }
+    }
+
+    /* Clip input depth to maximum in lookup table */
+    if (i == num_samples-1) {
+        depth = d_lut[i];
+    }
+	
+    /* Use index to lookup and interpolate energy */
+    if (i >= 0 || i < num_samples-1) {
+        // linear interpolation
+        energy = e_lut[i]
+            + (depth - d_lut[i])
+            * ((e_lut[i+1] - e_lut[i]) 
+                / (d_lut[i+1] - d_lut[i]));
+    } else {
+        // we wen't past the end of the lookup table
+        energy = 0.0f;
+    }
+    return energy;
+}
\ No newline at end of file
diff --git a/src/plastimatch/dose/rt_depth_dose.h b/src/plastimatch/dose/rt_depth_dose.h
index 933c384..78f7c50 100644
--- a/src/plastimatch/dose/rt_depth_dose.h
+++ b/src/plastimatch/dose/rt_depth_dose.h
@@ -1,8 +1,8 @@
 /* -----------------------------------------------------------------------
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
-#ifndef _Rt_depth_dose_h_
-#define _Rt_depth_dose_h_
+#ifndef _rt_depth_dose_h_
+#define _rt_depth_dose_h_
 
 #include "plmdose_config.h"
 
@@ -10,7 +10,7 @@ class PLMDOSE_API Rt_depth_dose {
 public:
     Rt_depth_dose ();
     Rt_depth_dose (double E0, double spread, double dres, 
-        double dmax, double weight);
+        double dend);
     ~Rt_depth_dose ();
 
     bool load (const char* fn);     /* load from file */
@@ -19,7 +19,11 @@ public:
     /* debug: print bragg curve to file */
     void dump (const char* fn) const;
 
+	/* Get dose maximum information */
+	int get_index_of_dose_max();
+
 	float lookup_energy_integration(float depth, float dz) const;
+	float lookup_energy (float depth);
 
 private:
     bool load_xio (const char* fn);
@@ -30,13 +34,13 @@ public:
     float* e_lut;                   /* energy array (MeV) */
 	float* f_lut;					/* integrated energy array (MeV) */
 
-    double E0;                      /* initial ion energy (MeV) */
+    float E0;                      /* initial ion energy (MeV) */
     double spread;                  /* beam energy sigma (MeV) */
     double dres;                    /* spatial resolution of bragg curve (mm)*/
-    double dmax;                    /* maximum w.e.d. (mm) */
-    double weight;
-
+    double dend;                    /* maximum w.e.d. (mm) */
     int num_samples;                /* # of discrete bragg curve samples */
+
+	int index_of_dose_max;
 };
 
 #endif
diff --git a/src/plastimatch/dose/rt_dose.cxx b/src/plastimatch/dose/rt_dose.cxx
index fa4a0ae..2272fd2 100644
--- a/src/plastimatch/dose/rt_dose.cxx
+++ b/src/plastimatch/dose/rt_dose.cxx
@@ -1,19 +1,5 @@
 /* -----------------------------------------------------------------------
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-
-   Algorithm c (modified Hong algorithm)
-   -------------------------------------
-   Stage 1(a): Compute pencil beam in standard grid
-   (z direction is pdd in water)
-   (x-y direction is scatter in water, or just 1-d with distance)
-
-   Stage 1(b): Compute RPL in interpolated coordinate system
-   (z axis is beam axis)
-   (x-y perpendicular to beam, arbitrary v-up vector)
-
-   Stage 2: For each voxel
-   a) Look up primary in RPL grid
-   b) Convolve to find scatter within x-y axis of primary grid (ignoring tilt)
    ----------------------------------------------------------------------- */
 #include "plmdose_config.h"
 #include <stdio.h>
@@ -37,7 +23,7 @@
 #include "rt_lut.h"
 #include "rt_parms.h"
 #include "rt_plan.h"
-#include "rt_sobp.h"
+#include "rt_mebs.h"
 #include "threading.h"
 #include "volume.h"
 
@@ -66,7 +52,6 @@ rotate_about_ray (
     double v[3];
     double w[3];
     double tmp[3] = {0.0, 0.0, 1.0};
-
     double M[12];
 
 #if defined (commentout)
@@ -111,747 +96,41 @@ rotate_about_ray (
     mat43_mult_vec3(xyz_new, M, xyz);
 }
 
-#if defined (DEBUG_VOXEL)
-static void 
-debug_voxel (
-    double r,             /* current radius */
-    double t,             /* current angle */
-    double rgdepth,       /* radiographic depth */
-    double d,             /* dose from scatter_xyz */
-    double* scatter_xyz,  /* space coordinates of scatterer */
-    double* ct_xyz,       /* voxel receiving dose */
-    double w,             /* gaussian weight (x) */
-    double d0,            /* unweighted d */
-    double sigma,         /* Gauss kernel stdev */
-    double dose           /* aggr dose @ ct_xyz */
-)
-{
-    FILE* fp;
-
-    fp = fopen ("dump_voxel.txt", "a");
-
-    fprintf (fp, "r,t: [%3.3f %1.3f] rgdepth: %2.3f d: %4.1f dist: %3.3f w: %1.4f d0: %4.3f sigma: %1.4f dose: %4.4f\n",
-             r, t, rgdepth, d, vec3_dist(scatter_xyz, ct_xyz), w, d0, sigma, dose);
-
-    fclose (fp);
-}
-#endif
-
-/* This computes the Highland scattering radius due to Coulombic interactions.
- * 
- * This is valid only for an "infinitely thick" medium such as the patient.  A
- * different approximation is used to find the radial scattering in thin
- * degraders.
- */
-static double
-highland (
-    double rgdepth,
-    Rt_beam* beam
-)
-{
-#if defined (commentout)
-    float rad_length = 1.0;     /* Radiation length of material (g/cm2) */
-    float density    = 1.0;     /* Density of material (g/cm3)          */
-    float p          = 0.0;     /* Ion momentum (passed in)          */
-    float v          = 0.0;     /* Ion velocity (passed in)          */
-    float sum        = 0.0;
-    float i, tmp;
-
-    for (i=0; i<rgdepth; i+=rgdepth/1000.0f) {
-        tmp = ((rgdepth - i) / (p*v));
-        sum += (tmp * tmp) * (density / rad_length) * (rgdepth/1000.0f);
-
-        /* Update p and v here */
-    }
-
-    sum = sqrtf(sum);
-//    printf ("%f\n", 14.1 * (1 + (1/9) * log10(rgdepth/rad_length)) * sum);
-
-    return 14.1 * (1 + (1/9) * log10(rgdepth/rad_length)) * sum; 
-#endif
-
-#if defined (commentout) /* MOVE TO BEAM or SOBP class */
-
-    /* This is just a normalization I used to use instead
-     * of the Highland approximation */
-    return 3.0 * (rgdepth - beam->sobp->d_lut[0]) 
-        / (beam->dmax - beam->sobp->d_lut[0]);
-#endif
-    return 0;
-}
-
-static double
-highland_maxime_aperture_theta0 (
-    double rgdepth,
-    Rt_beam* beam
-)
-{
-    float energy = 158.6;		/*Beam energy (MeV)*/
-    float mc2 = 939.4; /* proton mass at rest (MeV) */
-    float c = 299792458; /* speed of light (m/s2) */
-    float rad_length = 36.08;     /* Radiation length of material (g/cm2) */
-    float density    = 1.0;     /* Density of material (g/cm3) */
-    float p = 0.0;     /* Proton momentum (passed in)          */
-    float v = 0.0;     /* Proton velocity (passed in)          */
-    float range = 0;			/* Mean range of the proton beam (g/cm2) */
-    float stop = 0;				/* stopping power energy (MeV.cm2/g) */
-	
-    float sum = 0.0;			/* integration expression */
-    float step = 0.1;			/*step of the integration along the pathway (cm)*/
-
-    float function_to_be_integrated = 0.0; /* expression to be integrated on dz, second part of the highland's formula */
-
-    UNUSED_VARIABLE (density);
-    UNUSED_VARIABLE (range);
-
-    range = getrange(energy);
-
-    /* in the Hong algorithm, rgdepth is in cm but given in mm by plastimatch
-       integration of the integrale part of the highland's formula */
-    rgdepth = rgdepth/10;   
-
-    for (float i = 0; i <= rgdepth && energy > 0.5; i+=step)
-    {
-		/* p & v are updated */
-
-        p= sqrt(2*energy*mc2+energy*energy)/c; // in MeV.s.m-1
-        v= c*sqrt(1-pow((mc2/(energy+mc2)),2)); //in m.s-1
-		/*integration*/
-
-        function_to_be_integrated = 1/(pow(p*v,2)*rad_length);
-        sum += function_to_be_integrated*step;
-
-		/* energy is updated after passing through dz */
-        stop = getstop(energy);
-        energy = energy - stop*step;
-    }
-
-    return 14.1 * (1 + (1/9) * log10(rgdepth/rad_length)) * sqrt(sum); 
-}
-
-static double
-highland_maxime_patient_theta_pt (
-    double rgdepth,
-    Rt_beam* beam
-)
-{
-    float energy = 85;		/*Beam energy (MeV)*/
-    float mc2 = 939.4;          /* proton mass at rest (MeV) */
-    float c = 299792458;        /* speed of light (m/s2) */
-    float rad_length = 36.08;   /* Radiation length of material (g/cm2) */
-    float density    = 1.0;     /* Density of material (g/cm3) !!!!!!!!!! to be determined!! */
-    float p = 0.0;              /* Proton momentum (passed in)          */
-    float v = 0.0;              /* Proton velocity (passed in)          */
-    float range = 0;		/* Mean range of the proton beam (g/cm2) */
-    float stop = 0;		/* stopping power energy (MeV.cm2/g) */
-	
-    float sum = 0.0;		/* integration expression */
-
-    float step = 0.1;		/*step of the integration along the pathway (cm)*/
-
-    float function_to_be_integrated = 0.0; /* expression to be integrated on dz, second part of the highland's formula */
-
-    UNUSED_VARIABLE (range);
-
-    rgdepth = rgdepth /10; /* rgdepth is given in mm by plastimatch, but is in cm in the hong algorithm */
-
-    range = getrange(energy);
-
-    /* integration of the integrale part of the highland's formula */
-
-    for (float i = 0; i <= rgdepth && energy > 0.5; i+=step)
-    {
-	/* p & v are updated */
-
-        p= sqrt(2*energy*mc2+energy*energy)/c; // in MeV.s.m-1
-        v= c*sqrt(1-pow((mc2/(energy+mc2)),2)); //in m.s-1
-	/*integration*/
-
-        function_to_be_integrated = (pow(((rgdepth-i)/(p*v)),2)* density / rad_length);
-        sum += function_to_be_integrated*step;
-
-	/* energy is updated after passing through dz */
-        stop = getstop(energy);
-        energy = energy - stop*step;
-    }
-
-    return 14.1 * (1 + (1/9) * log10(rgdepth/rad_length)) * sqrt(sum) * 10; // y0 * 10 (cm->mm)
-}
-
-static double
-gaus_kernel (
-    double* p,
-    double* ct_xyz,
-    double sigma
-)
-{
-    double w1, w2;
-    double denom;
-    double sigma2;
-
-    // kludge to prevent evaluations 
-    // greater than 1
-    if (sigma < 0.4) {
-        sigma = 0.4;
-    }
-
-    sigma2 = sigma * sigma;
-
-    denom = 2.0f * M_PI * sigma2;
-    denom = sqrt (denom);
-    denom = 1.0f / denom;
-
-    w1 = denom * exp ( (-1.0*p[0]*p[0]) / (2.0f*sigma2) );
-    w2 = denom * exp ( (-1.0*p[1]*p[1]) / (2.0f*sigma2) );
-
-    return w1 * w2;
-}
-
-static double
-off_axis_maxime (
-    double r,
-    double sigma_srm /*,
-	double sigma_pt*/
-)
-{
-    double denom;
-    double sigma_tot2;
-
-    sigma_tot2 = /*sigma_source +*/ sigma_srm * sigma_srm /*+ sigma_pt * sigma_pt*/; /* !! source !! and sigma patient*/
-
-    denom = 1/ (2.0f * M_PI * sigma_tot2);
-    return denom * exp (-1.0*r*r / (2.0f*sigma_tot2));  /* Off-axis term */
-}
-
-
-/* This function should probably be marked for deletion once dose_scatter() is
- * working properly.  GCS: This funcion is useful for debugging.  Let's keep
- * it as flavor 'a'.
- */
+/* Ray Tracer */
 double
-dose_direct (
-    double* ct_xyz,             /* voxel to dose */
-	Rt_beam* beam
+energy_direct (
+    float rgdepth,          /* voxel to dose */
+	Rt_beam* beam,
+	int beam_idx
 )
 {
-    /* Find radiological depth at voxel ct_xyz */
-    double rgdepth = beam->rpl_vol->get_rgdepth (ct_xyz);
-	double WER =  compute_PrWER_from_HU(beam->rpl_ct_vol_HU->get_rgdepth(ct_xyz));
-
     /* The voxel was not hit directly by the beam */
     if (rgdepth <= 0.0f) {
         return 0.0f;
     }
 
-#if defined (commentout)
-    printf ("RGD [%g %g %g] = %f, %f\n", 
-        ct_xyz[0], ct_xyz[1], ct_xyz[2], rgdepth,
-        beam->beam->lookup_sobp_dose (rgdepth));
-#endif
-
     /* return the dose at this radiographic depth */
-    return (double) beam->lookup_sobp_dose ((float)rgdepth) * WER;
-}
-
-double
-dose_debug (
-    double* ct_xyz,             /* voxel to dose */
-    Rt_beam* beam
-)
-{
-#if defined (commentout)
-    return rpl_volume_get_rgdepth (beam->rpl_vol, ct_xyz);
-#endif
-
-    /* Find radiological depth at voxel ct_xyz */
-    return beam->rpl_vol->get_rgdepth (ct_xyz);
-}
-
-/* Accounts for small angle scattering due to Columbic interactions */
-double
-dose_scatter (
-    double* ct_xyz,
-    plm_long* ct_ijk,            // DEBUG
-    Rt_beam* beam
-)
-{
-    const Aperture::Pointer& ap = beam->get_aperture();
-    Rpl_volume*   rpl_vol = beam->rpl_vol;
-
-    double rgdepth;
-    double sigma;
-
-    double r, t;
-    double r_step, t_step;
-    double r_max;
-
-    double sp_pos[3] = {0.0, 0.0, 0.0};
-    double scatter_xyz[4] = {0.0, 0.0, 0.0, 1.0};
-    double proj_xy[2] = {0.0, 0.0};
-    double sctoct[3] = {0.0, 0.0, 0.0};
-    double tmp[3] = {0.0, 0.0, 0.0};
-
-    double d = 0.0f;
-    double dose = 0.0f;
-    double w;
-
-    int debug = 0;
-
-    double dmax = beam->get_sobp_maximum_depth ();
-
-#if defined (DEBUG_VOXEL)
-    double d0;
-
-    //int watch_ijk[3] = {0, 255, 67};  // entry
-    //int watch_ijk[3] = {134, 256, 67};  // bragg peak
-    //int watch_ijk[3] = {23, 255, 67};    // "stem"
-
-    int watch_ijk[3] = {20, 19, 19};
-
-    if (ct_ijk[0] == watch_ijk[0] &&
-        ct_ijk[1] == watch_ijk[1] &&
-        ct_ijk[2] == watch_ijk[2]) {
-
-        printf ("Watching voxel [%i %i %i]\n", watch_ijk[0], watch_ijk[1], watch_ijk[2]);
-        debug = 1;
-    }
-#endif 
-
-    /* Get approximation for scatterer search radius
-     * NOTE: This is not used to define the Gaussian
-     */
-#if defined (commentout)
-    rgdepth = rpl_volume_get_rgdepth (rpl_vol, ct_xyz);
-#endif
-    rgdepth = rpl_vol->get_rgdepth (ct_xyz);
-
-    if (debug) {
-//        printf ("rgdepth = %f\n", rgdepth);
-    }
-
-    /* If the voxel was not hit *directly* by the beam, there is still a
-     * chance that it was hit by scatterers generated by a neighbor who *was*
-     * hit directly by the beam.  As a result, we cannot obtain a resonable
-     * estimate, so we assume the largest scattering radius.
-     */
-    if (rgdepth < 0.0) {
-        if (beam->get_detail() == 0) {
-            rgdepth = dmax;
-        }
-        else if (beam->get_detail() == 1) {
-            /* User wants to ignore "scatter only" dose */
-            if (debug) {
-//                printf ("Voxel culled by detail flag\n");
-            }
-            return 0.0f;
-        }
-        else {
-            rgdepth = dmax;
-        }
-    }
-
-    sigma = highland (rgdepth, beam);
-    r_max = 3.0*sigma;
-
-    r_step = 1.00;          // mm
-    t_step = M_PI / 8.0f;   // radians
-
-    if (debug) {
-        printf ("sigma = %f\n", sigma);
-        printf ("r_max = %f\n", r_max);
-        printf ("r_step = %f\n", r_step);
-        printf ("t_step = %f\n", t_step);
-    }
-
-    /* Step radius */
-    for (r = 0; r < r_max; r += r_step) {
-        vec3_copy (sp_pos, ct_xyz);
-        vec3_scale3 (tmp, ap->pdn, r);
-        vec3_add2 (sp_pos, tmp);
-
-        /* Step angle */
-        for (t = 0.0f; t < 2.0*M_PI; t += t_step) {
-
-            rotate_about_ray (
-                scatter_xyz,  // O: new xyz coordinate
-                sp_pos,       // I: init xyz coordinate
-                t,            // I: angle of rotation
-                ct_xyz);      // I: axis of rotation
-
-            /* neighbor (or self) hit by ion beam? */
-#if defined (commentout)
-            rgdepth = rpl_volume_get_rgdepth (rpl_vol, scatter_xyz);
-#endif
-            rgdepth = rpl_vol->get_rgdepth (scatter_xyz);
-
-            if (rgdepth < 0.0f) {
-                if (debug) {
-                    printf ("Voxel culled by rgdepth\n");
-                }
-                continue;
-            } else {
-                d = beam->lookup_sobp_dose (rgdepth);
-#if defined (DEBUG_VOXEL)
-                d0 = d;
-#endif
-            }
-
-            vec3_sub3 (sctoct, scatter_xyz, ct_xyz);
-
-            proj_xy[0] = vec3_dot (sctoct, ap->prt);
-            proj_xy[1] = vec3_dot (sctoct, ap->pdn);
-
-            sigma = highland (rgdepth, beam);
-
-            /* weight by gaussian kernel */
-            w = gaus_kernel (proj_xy, ct_xyz, sigma);
-            d *= w;
-
-            /* Add to total dose for our target voxel */
-            dose += d;
-
-#if defined (DEBUG_VOXEL)
-            if (debug) {
-                debug_voxel (r, t, rgdepth, d, scatter_xyz, ct_xyz,
-                             w, d0, sigma, dose);
-            }
-#endif
-
-            /* Don't spin at the origin! */
-            if (r == 0) {
-                break;
-            }
-
-        }
-    }
-
-    return dose;    
+	return (double) beam->get_mebs()->get_depth_dose()[beam_idx]->lookup_energy(rgdepth);
 }
 
-double
-dose_hong (
-    double* ct_xyz,
-    plm_long* ct_ijk,            // DEBUG
-    Rt_beam* beam
-)
-{
-    const Aperture::Pointer& ap = beam->get_aperture();
-    Rpl_volume* rpl_vol = beam->rpl_vol;
-
-    double rgdepth;
-    double sigma;
-
-    double r, t;
-    double r_step, t_step;
-    double r_max;
-
-    double sp_pos[3] = {0.0, 0.0, 0.0};
-    double scatter_xyz[4] = {0.0, 0.0, 0.0, 1.0};
-    double proj_xy[2] = {0.0, 0.0};
-    double sctoct[3] = {0.0, 0.0, 0.0};
-    double tmp[3] = {0.0, 0.0, 0.0};
-
-    double d = 0.0f;
-    double dose = 0.0f;
-    double w;
-
-    double dmax = beam->get_sobp_maximum_depth ();
-
-    int debug = 0;
-
-#if defined (DEBUG_VOXEL) 
-    double d0;
-
-    int watch_ijk[3] = {20, 19, 19};
-
-    if (ct_ijk[0] == watch_ijk[0] &&
-        ct_ijk[1] == watch_ijk[1] &&
-        ct_ijk[2] == watch_ijk[2]) {
-
-        printf ("Watching voxel [%i %i %i]\n", 
-            watch_ijk[0], watch_ijk[1], watch_ijk[2]);
-        debug = 1;
-    }
-#endif 
-
-    /* Get approximation for scatterer search radius
-     * NOTE: This is not used to define the Gaussian
-     */
-#if defined (commentout)
-    rgdepth = rpl_volume_get_rgdepth (rpl_vol, ct_xyz);
-#endif
-    rgdepth = rpl_vol->get_rgdepth (ct_xyz);
-
-    if (debug) {
-        printf ("rgdepth = %f\n", rgdepth);
-    }
-
-    /* If the voxel was not hit *directly* by the beam, there is still a
-     * chance that it was hit by scatterers generated by a neighbor who
-     * *was* * hit directly by the beam.  As a result, we cannot obtain
-     * a resonable estimate, so we assume the largest scattering radius.
-     */
-    if (rgdepth < 0.0) {
-        if (beam->get_detail() == 0) {
-            rgdepth = dmax;
-        }
-        else if (beam->get_detail() == 1) {
-            /* User wants to ignore "scatter only" dose */
-            if (debug) {
-                printf ("Voxel culled by detail flag\n");
-            }
-            return 0.0f;
-        }
-        else {
-            rgdepth = dmax;
-        }
-    }
-
-    sigma = highland (rgdepth, beam);
-    r_max = 3.0*sigma;
-
-    r_step = 1.00;          // mm
-    t_step = M_PI / 8.0f;   // radians
-
-    if (debug) {
-        printf ("sigma = %f\n", sigma);
-        printf ("r_max = %f\n", r_max);
-        printf ("r_step = %f\n", r_step);
-        printf ("t_step = %f\n", t_step);
-    }
-
-    /* Step radius */
-    for (r = 0; r < r_max; r += r_step) {
-        vec3_copy (sp_pos, ct_xyz);
-        vec3_scale3 (tmp, ap->pdn, r);
-        vec3_add2 (sp_pos, tmp);
-
-        /* Step angle */
-        for (t = 0.0f; t < 2.0*M_PI; t += t_step) {
-
-            rotate_about_ray (
-                scatter_xyz,  // O: new xyz coordinate
-                sp_pos,       // I: init xyz coordinate
-                t,            // I: angle of rotation
-                ct_xyz);      // I: axis of rotation
-
-            /* neighbor (or self) hit by ion beam? */
-#if defined (commentout)
-            rgdepth = rpl_volume_get_rgdepth (rpl_vol, scatter_xyz);
-#endif
-            rgdepth = rpl_vol->get_rgdepth (scatter_xyz);
-
-            if (rgdepth < 0.0f) {
-                if (debug) {
-                    printf ("Voxel culled by rgdepth\n");
-                }
-                continue;
-            } else {
-                d = beam->lookup_sobp_dose (rgdepth);
-#if defined (DEBUG_VOXEL)
-                d0 = d;
-#endif
-            }
-
-            vec3_sub3 (sctoct, scatter_xyz, ct_xyz);
-
-            proj_xy[0] = vec3_dot (sctoct, ap->prt);
-            proj_xy[1] = vec3_dot (sctoct, ap->pdn);
-
-            sigma = highland (rgdepth, beam);
-
-            /* weight by gaussian kernel */
-            w = gaus_kernel (proj_xy, ct_xyz, sigma);
-            d *= w;
-
-            /* Add to total dose for our target voxel */
-            dose += d;
-
-#if defined (DEBUG_VOXEL)
-            if (debug) {
-                debug_voxel (r, t, rgdepth, d, scatter_xyz, ct_xyz,
-                    w, d0, sigma, dose);
-            }
-#endif
-
-            /* Don't spin at the origin! */
-            if (r == 0) {
-                break;
-            }
-
-        }
-    }
-    return dose;    
-
-}
-
-double /* to be implemented */
-dose_hong_maxime (
-    double* ct_xyz,
-    plm_long* ct_ijk,            // DEBUG
-    Rt_beam* beam
-)
-{
-    const Aperture::Pointer& ap = beam->get_aperture();
-    Rpl_volume* rpl_vol = beam->rpl_vol;
-
-    double rgdepth;
-    double sigma;
-
-    double r, t;
-    double r_step, t_step;
-    double r_max;
-
-    double r_number = 4; // the number of segmentations
-    double t_number = 16; 
-
-    double sp_pos[3] = {0.0, 0.0, 0.0};
-    double scatter_xyz[4] = {0.0, 0.0, 0.0, 1.0};
-    double proj_xy[2] = {0.0, 0.0};
-    double sctoct[3] = {0.0, 0.0, 0.0};
-    double tmp[3] = {0.0, 0.0, 0.0};
-
-    double center_ct_xyz[3] = {0.0, 0.0, 0.0};
-
-    double axis[3] = {ct_xyz[0]-beam->get_source_position(0),ct_xyz[1]-beam->get_source_position(1),ct_xyz[2]-beam->get_source_position(2)};
-    
-    double aperture_right[3] = {0.0,1.0,0.0};
-    double aperture_down[3] = {0.0,0.0,-1.0};
-
-    double d = 0.0f;
-    double dose = 0.0f;
-    double w;
-    
-    /* creation of a vector perpendicular to axis to initiate the rotation around */
-    double vector_init[3] = {1.0,0.0,0.0};
-    double vector_opposite[3] = {0.0,0.0,0.0};
-    double vector_norm_axis[3] = {0.0,0.0,0.0};
-
-    UNUSED_VARIABLE (ap);
-    UNUSED_VARIABLE (sp_pos);
-    UNUSED_VARIABLE (proj_xy);
-    UNUSED_VARIABLE (sctoct);
-
-    rotate_about_ray(vector_opposite,vector_init,M_PI,axis);
-    vec3_sub3 (vector_norm_axis,vector_init,vector_opposite);
-    vec3_scale2(vector_norm_axis,1/sqrt(vector_norm_axis[0]*vector_norm_axis[0]+vector_norm_axis[1]*vector_norm_axis[1]+vector_norm_axis[2]*vector_norm_axis[2]));
-    
-     /* Get approximation for scatterer search radius
-     * NOTE: This is not used to define the Gaussian
-     */
-    rgdepth = rpl_vol->get_rgdepth (ct_xyz);
-	
-	if (rgdepth < 0.0f) {
-            dose = 0;
-	    rgdepth = rpl_vol->get_rgdepth(center_ct_xyz);
-            } else {
-                dose = 0;
-            }
-
-    /* If the voxel was not hit *directly* by the beam, there is still a
-     * chance that it was hit by scatterers generated by a neighbor who
-     * *was* * hit directly by the beam.  As a result, we cannot obtain
-     * a resonable estimate, so we assume the largest scattering radius.
-     */
-
-    sigma = highland_maxime_patient_theta_pt (rgdepth, beam); /*should be highland_patient_theta0 - !! multiplied by 10 to see it */
-    r_max = 3.0*sigma;
-
-    r_step = r_max/r_number;
-
-    t_step =2 * M_PI / t_number;   // radians
-
-    /* Step radius */
-    for (int i = 0; i < r_number; i++) {
-        r = r_step*(i+1);
-        vec3_scale3 (tmp, vector_norm_axis, r);
-
-        /* Step angle */
-        for (t = 0.0f; t < 2.0*M_PI; t += t_step) {
-
-            rotate_about_ray (
-                scatter_xyz,  // O: new xyz coordinate
-                tmp,       // I: init xyz coordinate
-                t,            // I: angle of rotation
-	        axis);      // I: axis of rotation
-
-            /* neighbor (or self) hit by proton beam? */
-
-            vec3_add2(scatter_xyz, ct_xyz);
-
-            rgdepth = rpl_vol->get_rgdepth (scatter_xyz);
-
-            if (rgdepth < 0.0f) {
-		d=0;
-            } else {
-                d = beam->lookup_sobp_dose (rgdepth);
-
-                proj_xy[0] = vec3_dot (scatter_xyz, aperture_right);
-                proj_xy[1] = vec3_dot (scatter_xyz, aperture_down);
-
-                sigma = highland_maxime_patient_theta_pt(rgdepth, beam); /* should be the global one: highland_max_patient_theta0 once the density rho problem will be fixed*/
-
-                /* weight by gaussian kernel */
-                w = off_axis_maxime (r, sigma);
-                d *= M_PI*(pow(r,2)-pow(r-r_step, 2))/t_number*w; //integration of the pencil beams of this area
-			
-                /* Add to total dose for our target voxel */
-                dose += d;
-	    }
-
-
-            /* Don't spin at the origin! */
-            if (r == 0) {
-                break;
-            }
-	}
-	r += r_step;
-    }
-    return dose;    
-}
-
-double
-dose_hong_sharp (
-    double* ct_xyz,             /* voxel to dose */
-    Rt_beam* beam
-)
-{
-    double value = beam->rpl_dose_vol->get_rgdepth(ct_xyz);
-    /* return the dose at this radiographic depth */
-    if (value < 0) {return 0;}
-    else {return value;}
-}
-
-void
-compute_dose_ray_desplanques (
+void compute_dose_ray_desplanques (
     Volume* dose_volume, 
     Volume::Pointer ct_vol, 
-    Rpl_volume* rpl_volume, 
-    Rpl_volume* sigma_volume, 
-    Rpl_volume* ct_rpl_volume, 
     Rt_beam* beam, 
     Volume::Pointer final_dose_volume, 
-    const Rt_depth_dose* ppp, 
-    float normalization_dose)
+    int beam_index
+)
 {
-    if (ppp->weight <= 0)
-    {
-        return;
-    }
-
     int ijk_idx[3] = {0,0,0};
     int ijk_travel[3] = {0,0,0};
     double xyz_travel[3] = {0.0,0.0,0.0};
 
     double spacing[3] = { (double) (dose_volume->spacing[0]), (double) (dose_volume->spacing[1]), (double) (dose_volume->spacing[2])};
     int ap_ij[2] = {1,0};
-    int dim[2] = {0,0};
-
+    int dim[2] = {beam->sigma_vol->get_aperture()->get_dim(0),beam->sigma_vol->get_aperture()->get_dim(1)};
     double ray_bev[3] = {0,0,0};
-
     double xyz_ray_center[3] = {0.0, 0.0, 0.0};
     double xyz_ray_pixel_center[3] = {0.0, 0.0, 0.0};
-
     double entrance_bev[3] = {0.0f, 0.0f, 0.0f}; // coordinates of intersection with the volume in the bev frame
     double xyz_room[3] = {0.0f, 0.0f, 0.0f}; 
     double xyz_room_tmp[3] = {0.0f, 0.0f, 0.0f};
@@ -859,11 +138,7 @@ compute_dose_ray_desplanques (
     double entrance_length = 0;
     double distance = 0; // distance from the aperture to the POI
     double tmp[3] = {0.0f, 0.0f, 0.0f};
-
-    double PB_density = 1/(rpl_volume->get_aperture()->get_spacing(0) * rpl_volume->get_aperture()->get_spacing(1));
-
-    double dose_norm = get_dose_norm('f', ppp->E0, PB_density); //the Hong algorithm has no PB density, everything depends on the number of sectors
-
+    double PB_density = 1/(beam->rpl_vol->get_aperture()->get_spacing(0) * beam->rpl_vol->get_aperture()->get_spacing(1));
     double ct_density = 0;
     double WER = 0;
     double STPR = 0;
@@ -872,30 +147,22 @@ compute_dose_ray_desplanques (
     double rg_length = 0;
     double radius = 0;
     float range_comp = 0;
-
     float central_axis_dose = 0;
     float off_axis_factor = 0;
 
     int idx = 0; // index to travel in the dose volume
     int idx_bev = 0; // second index for reconstructing the final image
     int idx_room = 0;
-    bool test = true;
-    bool* in = &test;
-
-    double vec_antibug_prt[3] = {0.0,0.0,0.0};
-
     int i_min = 0;
     int i_max = 0;
     int j_min = 0;
     int j_max = 0;
-
-    dim[0] = sigma_volume->get_aperture()->get_dim(0);
-    dim[1] = sigma_volume->get_aperture()->get_dim(1);
+	bool test = true;
+    bool* in = &test;
 
     float* img = (float*) dose_volume->img;
     float* ct_img = (float*) ct_vol->img;
-    float* rpl_image = (float*) rpl_volume->get_vol()->img;
-
+    float* rpl_image = (float*) beam->rpl_vol->get_vol()->img;
     float* rc_img = 0;
     unsigned char *ap_img = 0;
 
@@ -909,72 +176,77 @@ compute_dose_ray_desplanques (
         ap_img = (unsigned char*) ap_vol->img;
     }
 
+	std::vector<float> num_part = beam->get_mebs()->get_num_particles();
+
     double dist = 0;
     int offset_step = 0;
 
-    vec3_cross (vec_antibug_prt, rpl_volume->get_aperture()->pdn, rpl_volume->get_proj_volume()->get_nrm());
+	double vec_pdn_tmp[3] = {0,0,0};
+	double vec_prt_tmp[3] = {0,0,0};
+	double vec_nrm_tmp[3] = {0,0,0};
+
+	vec3_copy(vec_pdn_tmp, beam->rpl_vol->get_proj_volume()->get_incr_c());
+	vec3_normalize1(vec_pdn_tmp);
+	vec3_copy(vec_prt_tmp, beam->rpl_vol->get_proj_volume()->get_incr_r());
+	vec3_normalize1(vec_prt_tmp);
+	vec3_copy(vec_nrm_tmp, beam->rpl_vol->get_proj_volume()->get_nrm());
+	vec3_normalize1(vec_nrm_tmp);
 
     for (int i = 0; i < dim[0]*dim[1]; i++)
     {
-        if (beam->get_aperture()->have_aperture_image()) {
-            if (ap_img[i] == 0)
-            { 
-                continue;
-            }
+		if (ap_img[i] == 0 || num_part[beam_index * dim[0] * dim[1] + i] == 0) 
+		{
+            continue;
         }
-
-        Ray_data* ray_data = &rpl_volume->get_Ray_data()[i];
+        
+		Ray_data* ray_data = &beam->sigma_vol->get_Ray_data()[i]; //MD Fix: Why ray_daya->ray for rpl_vol is wrong at this point?
 
         ap_ij[1] = i / dim[0];
         ap_ij[0] = i- ap_ij[1]*dim[0];
+        ray_bev[0] = vec3_dot (ray_data->ray, vec_prt_tmp);
+        ray_bev[1] = vec3_dot (ray_data->ray, vec_pdn_tmp);
+        ray_bev[2] = -vec3_dot (ray_data->ray, vec_nrm_tmp); // ray_beam_eye_view is already normalized
 
-        ray_bev[0] = vec3_dot (ray_data->ray, vec_antibug_prt);
-        ray_bev[1] = vec3_dot (ray_data->ray, rpl_volume->get_aperture()->pdn);
-        ray_bev[2] = -vec3_dot (ray_data->ray, rpl_volume->get_proj_volume()->get_nrm()); // ray_beam_eye_view is already normalized
+		/* printf("prt: %lg %lg %lg\n",vec_prt_tmp[0], vec_prt_tmp[1], vec_prt_tmp[2]);
+		printf("pdn: %lg %lg %lg\n",vec_pdn_tmp[0], vec_pdn_tmp[1], vec_pdn_tmp[2]);
+		printf("nrm: %lg %lg %lg\n",vec_nrm_tmp[0], vec_nrm_tmp[1], vec_nrm_tmp[2]);
+		printf("ray: %lg %lg %lg\n",ray_data->ray[0], ray_data->ray[1], ray_data->ray[2]);
+		printf("bev: %lg %lg %lg\n", ray_bev[0], ray_bev[1], ray_bev[2]); */
 
         /* Calculation of the coordinates of the intersection of the ray with the clipping plane */
-		
-        entrance_length = vec3_dist(rpl_volume->get_proj_volume()->get_src(), ray_data->cp);
-        entrance_length += (double) ray_data->step_offset * rpl_volume->get_proj_volume()->get_step_length ();
+        entrance_length = vec3_dist(beam->rpl_vol->get_proj_volume()->get_src(), ray_data->cp);
 
         vec3_copy(entrance_bev, ray_bev);
         vec3_scale2(entrance_bev, entrance_length);
 
-
         if (beam->get_aperture()->have_range_compensator_image())
         {
-            range_comp = rc_img[i] * 1.19 * 0.98; // Lucite material: d * rho * WER
+            range_comp = rc_img[i] * PMMA_DENSITY * PMMA_STPR; // Lucite material: d * rho * WER
         }
         else
         {
             range_comp = 0;
         }
-
         if (ray_bev[2]  > DRR_BOUNDARY_TOLERANCE)
         {
-            for(int k = 0; k < dose_volume->dim[2] ;k++)
+            for(int k = 0; k < (int) dose_volume->dim[2] ;k++)
             {
                 find_xyz_center(xyz_ray_center, ray_bev, dose_volume->origin[2],k, dose_volume->spacing[2]);
                 distance = vec3_dist(xyz_ray_center, entrance_bev);
-
-                ct_density = compute_density_from_HU(ct_rpl_volume->get_rgdepth(ap_ij, distance));
-                STPR = compute_PrSTPR_from_HU(ct_rpl_volume->get_rgdepth(ap_ij, distance));
-				
-                rg_length = range_comp + rpl_volume->get_rgdepth(ap_ij, distance);
-                central_axis_dose = ppp->lookup_energy_integration((float)rg_length, ct_density * dose_volume->spacing[2]) * STPR;
-
-                sigma = sigma_volume->get_rgdepth(ap_ij, distance);
+				ct_density = compute_density_from_HU(beam->rpl_ct_vol_HU->get_rgdepth(ap_ij, distance));
+                STPR = compute_PrSTPR_from_HU(beam->rpl_ct_vol_HU->get_rgdepth(ap_ij, distance));
+                rg_length = range_comp + beam->rpl_vol->get_rgdepth(ap_ij, distance);
+                central_axis_dose = beam->get_mebs()->get_depth_dose()[beam_index]->lookup_energy_integration((float)rg_length, ct_density * dose_volume->spacing[2]) * STPR;
+                sigma = beam->sigma_vol->get_rgdepth(ap_ij, distance);
                 sigma_x3 = (int) ceil(3 * sigma);
-					
+
                 /* We defined the grid to be updated, the pixels that receive dose from the ray */
                 /* We don't check to know if we are still in the matrix because the matrix was build to contain all pixels with a 3 sigma_max margin */
                 find_ijk_pixel(ijk_idx, xyz_ray_center, dose_volume);
-                    
                 i_min = ijk_idx[0] - sigma_x3;
                 i_max = ijk_idx[0] + sigma_x3;
                 j_min = ijk_idx[1] - sigma_x3;
                 j_max = ijk_idx[1] + sigma_x3;
-                    
                 for (int i2 = i_min; i2 <= i_max; i2++)
                 {
                     for (int j2 = j_min; j2 <= j_max; j2++)
@@ -989,32 +261,30 @@ compute_dose_ray_desplanques (
                         ijk_travel[2] = k;
 
                         /* calculation of the corresponding position in the room and its HU number*/
-                        vec3_copy(xyz_room_tmp, vec_antibug_prt);
+                        vec3_copy(xyz_room_tmp, vec_prt_tmp);
                         vec3_scale2(xyz_room_tmp, dose_volume->origin[0] + (float) i2 * dose_volume->spacing[0]);
                         vec3_copy(xyz_room, (xyz_room_tmp));
 
-                        vec3_copy(xyz_room_tmp, rpl_volume->get_aperture()->pdn);
+                        vec3_copy(xyz_room_tmp, vec_pdn_tmp);
                         vec3_scale2(xyz_room_tmp, dose_volume->origin[1] + (float) j2 * dose_volume->spacing[1]);
                         vec3_add2(xyz_room, (xyz_room_tmp));
 
-                        vec3_copy(xyz_room_tmp, rpl_volume->get_proj_volume()->get_nrm());
+                        vec3_copy(xyz_room_tmp,  vec_nrm_tmp);
                         vec3_scale2(xyz_room_tmp, (double) (-dose_volume->origin[2] - (float) k * dose_volume->spacing[2]));
                         vec3_add2(xyz_room, (xyz_room_tmp));
-                        vec3_add2(xyz_room, rpl_volume->get_proj_volume()->get_src());
+                        vec3_add2(xyz_room, beam->rpl_vol->get_proj_volume()->get_src());
 						
                         find_ijk_pixel(ijk_ct, xyz_room, ct_vol);
                         idx_room = ijk_ct[0] + (ct_vol->dim[0] * (ijk_ct[1] + ct_vol->dim[1] * ijk_ct[2]));
                         if (ijk_ct[0] < 0 || ijk_ct[1] < 0 || ijk_ct[2] < 0 || ijk_ct[0] >= ct_vol->dim[0] || ijk_ct[1] >= ct_vol->dim[1] || ijk_ct[2] >= ct_vol->dim[2])
                         {
-                            WER = 0.88; // pixel outside of the CT = air
+                            WER = PROTON_WER_AIR;
                         }
                         else
                         {
                             WER =  compute_PrWER_from_HU(ct_img[idx_room]);
                         }
-
                         find_xyz_from_ijk(xyz_travel,dose_volume,ijk_travel);
-                            
                         radius = vec3_dist(xyz_travel,xyz_ray_center); 
                         if (sigma == 0)
                         {
@@ -1028,123 +298,78 @@ compute_dose_ray_desplanques (
                         {
                             off_axis_factor = double_gaussian_interpolation(xyz_ray_center, xyz_travel,sigma, spacing);
                         }
-                        // SOBP is weighted by the weight of the 
-                        // pristine peak
-                        img[idx] += normalization_dose 
-                            * central_axis_dose 
+                        /* SOBP is weighted by the weight of the pristine peak */
+                        img[idx] += num_part[beam_index * dim[0] * dim[1] + i] * central_axis_dose 
                             * WER // dose = dose_w * WER
-                            * off_axis_factor 
-                            * (float) ppp->weight 
-                            / dose_norm;
+                            * off_axis_factor ;
                     }			
                 }
             }
         }
-        else
-        {
-            //printf("Ray[%d] is not directed forward: z,x,y (%lg, %lg, %lg) \n", i, ray_data->ray[0], ray_data->ray[1], ray_data->ray[2]);
-        }
     }
 
     float* final_dose_img = (float*) final_dose_volume->img;
-
     int ijk[3] = {0,0,0};
     float ijk_bev[3] = {0,0,0};
     int ijk_bev_trunk[3];
     float xyz_bev[3] = {0.0,0.0,0.0};
-
     plm_long mijk_f[3];
     plm_long mijk_r[3];
     plm_long idx_lower_left = 0;
-
     float li_frac1[3];
     float li_frac2[3];
-
-    plm_long ct_dim[3] = {ct_vol->dim[0], ct_vol->dim[1], ct_vol->dim[2]};
+    const plm_long *dim_ct = ct_vol->dim;
     plm_long dose_bev_dim[3] = { dose_volume->dim[0], dose_volume->dim[1], dose_volume->dim[2]};
 
-    for (ijk[0] = 0; ijk[0] < ct_dim[0]; ijk[0]++)
+    for (ijk[0] = 0; ijk[0] < dim_ct[0]; ijk[0]++)
     {
-        for (ijk[1] = 0; ijk[1] < ct_dim[1]; ijk[1]++)
+        for (ijk[1] = 0; ijk[1] < dim_ct[1]; ijk[1]++)
         {
-            for (ijk[2] = 0; ijk[2] < ct_dim[2]; ijk[2]++)
+            for (ijk[2] = 0; ijk[2] < dim_ct[2]; ijk[2]++)
             {
-                idx = ijk[0] + ct_dim[0] *(ijk[1] + ijk[2] * ct_dim[1]);
+                idx = ijk[0] + dim_ct[0] *(ijk[1] + ijk[2] * dim_ct[1]);
                 if ( ct_img[idx] >= -1000) // in air we have no dose, we let the voxel number at 0!
                 {   
                     final_dose_volume->get_xyz_from_ijk(xyz_room, ijk);
 
                     /* xyz contains the coordinates of the pixel in the room coordinates */
                     /* we now calculate the coordinates of this pixel in the dose_volume coordinates */
-                    /* need to be fixed after the extrinsic homogeneous coordinates is fixed */
-
-                    vec3_sub3(tmp, rpl_volume->get_proj_volume()->get_src(), xyz_room);
-
-                    xyz_bev[0] = (float) -vec3_dot(tmp, vec_antibug_prt);
-                    xyz_bev[1] = (float) -vec3_dot(tmp, rpl_volume->get_aperture()->pdn);
-                    xyz_bev[2] = (float) vec3_dot(tmp, rpl_volume->get_proj_volume()->get_nrm());
 
+                    vec3_sub3(tmp,  beam->rpl_vol->get_proj_volume()->get_src(), xyz_room);
+                    xyz_bev[0] = (float) -vec3_dot(tmp, vec_prt_tmp);
+                    xyz_bev[1] = (float) -vec3_dot(tmp,  vec_pdn_tmp);
+                    xyz_bev[2] = (float) vec3_dot(tmp,  vec_nrm_tmp);
                     dose_volume->get_ijk_from_xyz(ijk_bev, xyz_bev, in);
 
                     if (*in == true)
                     {
                         dose_volume->get_ijk_from_xyz(ijk_bev_trunk, xyz_bev, in);
-
                         idx_bev = ijk_bev_trunk[0] + ijk_bev[1]*dose_volume->dim[0] + ijk_bev[2] * dose_volume->dim[0] * dose_volume->dim[1];
                         li_clamp_3d(ijk_bev, mijk_f, mijk_r, li_frac1, li_frac2, dose_volume);
-						
                         idx_lower_left =  mijk_f[0] + dose_bev_dim[0] *(mijk_f[1] + mijk_f[2] * dose_bev_dim[1]);
-                                                
                         final_dose_img[idx] += li_value(li_frac1[0], li_frac2[0], li_frac1[1], li_frac2[1], li_frac1[2], li_frac2[2], idx_lower_left, img, dose_volume);
                     }
                 }
             }   
         }     
     }
-	
-
     return;
 }
 
 void 
 compute_dose_ray_sharp (
     const Volume::Pointer ct_vol, 
-    const Rpl_volume* rpl_volume, 
-    const Rpl_volume* sigma_volume, 
-    Rpl_volume* ct_rpl_volume, 
-    const Rt_beam* beam, 
-    Rpl_volume* rpl_dose_volume, 
-    const Aperture::Pointer ap, 
-    const Rt_depth_dose* ppp, 
-    const int* margins, 
-    float normalization_dose
+    Rt_beam* beam, 
+    Rpl_volume* rpl_dose_volume,  
+    int beam_index,
+    const int* margins
 )
 {
     int ap_ij_lg[2] = {0,0};
     int ap_ij_sm[2] = {0,0};
     int dim_lg[3] = {0,0,0};
     int dim_sm[3] = {0,0,0};
-    int dim_ct[3] = {ct_vol->dim[0], ct_vol->dim[1], ct_vol->dim[2]};
-
-    float ct_density = 0;
-    float WER = 0;
-    float STPR = 0;
-    double sigma = 0;
-    double sigma_x3 = 0;
-    double rg_length = 0;
-
-    double central_ray_xyz[3] = {0.0, 0.0, 0.0};
-    double travel_ray_xyz[3] = {0.0, 0.0, 0.0};
-    double xyz_room[4] = {0.0, 0.0, 0.0, 1.0};
-
-    float central_axis_dose = 0;
-    float off_axis_factor = 0;
-
-    double PB_density = 1 / (rpl_volume->get_aperture()->get_spacing(0) * rpl_volume->get_aperture()->get_spacing(1));
-
-    double dose_norm = get_dose_norm ('g', ppp->E0, PB_density);
-    //the Hong algorithm has no PB density, everything depends on the number of sectors
-
+    const plm_long *dim_ct = ct_vol->dim;
     int idx2d_sm = 0;
     int idx2d_lg = 0;
     int idx3d_sm = 0;
@@ -1152,26 +377,38 @@ compute_dose_ray_sharp (
     int idx3d_travel = 0;
     int idx_ct = 0;
     int ijk_ct[3] = {0,0,0};
-
-    double minimal_lateral = 0;
-    double lateral_step[2] = {0,0};
     int i_min = 0;
     int i_max = 0;
     int j_min = 0;
     int j_max = 0;
 
+    float ct_density = 0;
+    float WER = 0;
+    float DENSITY = 0;
+    float STPR = 0;
+    double sigma = 0;
+    double sigma_x3 = 0;
+    double rg_length = 0;
+    float central_axis_dose = 0;
+    float off_axis_factor = 0;
+    double minimal_lateral = 0;
+    double lateral_step[2] = {0,0};
+    double central_ray_xyz[3] = {0.0, 0.0, 0.0};
+    double travel_ray_xyz[3] = {0.0, 0.0, 0.0};
+    double xyz_room[4] = {0.0, 0.0, 0.0, 1.0};
+    double PB_density = 1 / ( beam->rpl_vol->get_aperture()->get_spacing(0) *  beam->rpl_vol->get_aperture()->get_spacing(1));
+
     dim_lg[0] = rpl_dose_volume->get_vol()->dim[0];
     dim_lg[1] = rpl_dose_volume->get_vol()->dim[1];
     dim_lg[2] = rpl_dose_volume->get_vol()->dim[2];
-
-    dim_sm[0] = rpl_volume->get_vol()->dim[0];
-    dim_sm[1] = rpl_volume->get_vol()->dim[1];
-    dim_sm[2] = rpl_volume->get_vol()->dim[2];
+    dim_sm[0] = beam->rpl_vol->get_vol()->dim[0];
+    dim_sm[1] = beam->rpl_vol->get_vol()->dim[1];
+    dim_sm[2] = beam->rpl_vol->get_vol()->dim[2];
 	
-    float* rpl_img = (float*) rpl_volume->get_vol()->img;
-    float* sigma_img = (float*) sigma_volume->get_vol()->img;
+    float* rpl_img = (float*) beam->rpl_vol->get_vol()->img;
+    float* sigma_img = (float*) beam->sigma_vol->get_vol()->img;
     float* rpl_dose_img = (float*) rpl_dose_volume->get_vol()->img;
-    float* ct_rpl_img = (float*) ct_rpl_volume->get_vol()->img;
+    float* ct_rpl_img = (float*) beam->rpl_ct_vol_HU->get_vol()->img;
     float* ct_img = (float*) ct_vol->img;
     float* rc_img = 0;
     unsigned char *ap_img = 0;
@@ -1207,41 +444,40 @@ compute_dose_ray_sharp (
     std::vector <double> lateral_step_x (dim_lg[2],0);
     std::vector <double> lateral_step_y (dim_lg[2],0);
 
-    minimal_lateral = ap->get_spacing(0);
-    if (minimal_lateral < ap->get_spacing(1))
+    minimal_lateral = beam->get_aperture()->get_spacing(0);
+    if (minimal_lateral < beam->get_aperture()->get_spacing(1))
     {
-        minimal_lateral = ap->get_spacing(1);
+        minimal_lateral = beam->get_aperture()->get_spacing(1);
     }
 
     for (int k = 0; k < dim_sm[2]; k++)
     {
-        lateral_minimal_step[k] = (rpl_volume->get_front_clipping_plane() + rpl_volume->get_aperture()->get_distance() + (double) k) * minimal_lateral / rpl_volume->get_aperture()->get_distance();
-        lateral_step_x[k] = (rpl_volume->get_front_clipping_plane() + rpl_volume->get_aperture()->get_distance() + (double) k) * ap->get_spacing(0) / rpl_volume->get_aperture()->get_distance();
-        lateral_step_y[k] = (rpl_volume->get_front_clipping_plane() + rpl_volume->get_aperture()->get_distance() + (double) k) * ap->get_spacing(1) / rpl_volume->get_aperture()->get_distance();
-        //printf("%d: %lg %lg %lg\n", k, lateral_minimal_step[k], lateral_step_x[k], lateral_step_y[k]);
+        lateral_minimal_step[k] = (beam->rpl_vol->get_front_clipping_plane() + beam->rpl_vol->get_aperture()->get_distance() + (double) k) * minimal_lateral / beam->rpl_vol->get_aperture()->get_distance();
+        lateral_step_x[k] = (beam->rpl_vol->get_front_clipping_plane() + beam->rpl_vol->get_aperture()->get_distance() + (double) k) * beam->get_aperture()->get_spacing(0) / beam->rpl_vol->get_aperture()->get_distance();
+        lateral_step_y[k] = (beam->rpl_vol->get_front_clipping_plane() + beam->rpl_vol->get_aperture()->get_distance() + (double) k) *beam->get_aperture()->get_spacing(1) / beam->rpl_vol->get_aperture()->get_distance();
     }
 
+    std::vector<float> num_part = beam->get_mebs()->get_num_particles();
+
     /* calculation of the dose in the rpl_volume */
     for (ap_ij_lg[0] = margins[0]; ap_ij_lg[0] < rpl_dose_volume->get_vol()->dim[0]-margins[0]; ap_ij_lg[0]++){
         for (ap_ij_lg[1] = margins[1]; ap_ij_lg[1] < rpl_dose_volume->get_vol()->dim[1]-margins[1]; ap_ij_lg[1]++){
 
             ap_ij_sm[0] = ap_ij_lg[0] - margins[0];
             ap_ij_sm[1] = ap_ij_lg[1] - margins[1];
-
             idx2d_lg = ap_ij_lg[1] * dim_lg[0] + ap_ij_lg[0];
             idx2d_sm = ap_ij_sm[1] * dim_sm[0] + ap_ij_sm[0];
 
             if (beam->get_aperture()->have_aperture_image())
             {
-                if((float) ap_img[idx2d_sm] == 0)
+                if((float) ap_img[idx2d_sm] == 0 || num_part[beam_index * beam->get_aperture()->get_dim(0) * beam->get_aperture()->get_dim(1) + idx2d_sm] == 0)
                 {
                     continue;
                 }
             }
-
             if (beam->get_aperture()->have_range_compensator_image())
             {
-                range_comp = rc_img[idx2d_sm] * 1.19 * 0.98; // Lucite Material: d * rho * WER
+                range_comp = rc_img[idx2d_sm] * PMMA_DENSITY * PMMA_STPR; // Lucite Material: d * rho * WER, MD Fix
             }
             else
             {
@@ -1265,7 +501,7 @@ compute_dose_ray_sharp (
                 STPR = compute_PrSTPR_from_HU(ct_rpl_img[idx3d_sm]);
 
                 rg_length = range_comp + rpl_img[idx3d_sm];
-                central_axis_dose = ppp->lookup_energy_integration(rg_length, ct_density * rpl_volume->get_vol()->spacing[2]) * STPR;
+                central_axis_dose = num_part[beam_index * beam->get_aperture()->get_dim(0)* beam->get_aperture()->get_dim(1) + idx2d_sm] * beam->get_mebs()->get_depth_dose()[beam_index]->lookup_energy_integration(rg_length, ct_density * beam->rpl_vol->get_vol()->spacing[2]) * STPR;
 
                 if (central_axis_dose <= 0) // no dose on the axis, no dose scattered
                 {
@@ -1273,7 +509,6 @@ compute_dose_ray_sharp (
                 }
 
                 sigma = (double) sigma_img[idx3d_sm];
-                        
                 sigma_x3 = sigma * 3;
 
                 /* finding the rpl_volume pixels that are contained in the the 3 sigma range */                    
@@ -1288,7 +523,6 @@ compute_dose_ray_sharp (
 
                 for (int i1 = i_min; i1 <= i_max; i1++) {
                     for (int j1 = j_min; j1 <= j_max; j1++) {
-
                         idx3d_travel = k * dim_lg[0]*dim_lg[1] + j1 * dim_lg[0] + i1;
 
                         ray_data_tmp = &rpl_dose_volume->get_Ray_data()[j1 * dim_lg[0] + i1];
@@ -1296,16 +530,12 @@ compute_dose_ray_sharp (
                         travel_ray_xyz[0] = xyz_coor_vol[idx3d_travel][0];
                         travel_ray_xyz[1] = xyz_coor_vol[idx3d_travel][1];
                         travel_ray_xyz[2] = xyz_coor_vol[idx3d_travel][2];
-								
-                        radius = vec3_dist(travel_ray_xyz, central_ray_xyz);                            
+                        radius = vec3_dist(travel_ray_xyz, central_ray_xyz);
+
                         if (sigma == 0)
                         {
                             off_axis_factor = 1;
                         }
-                        /*else if (radius >= sigma_x3)
-                          {
-                          off_axis_factor = 0;
-                          } */
                         else
                         {
                             off_axis_factor = double_gaussian_interpolation(central_ray_xyz, travel_ray_xyz, sigma, lateral_step);
@@ -1317,19 +547,20 @@ compute_dose_ray_sharp (
                         find_ijk_pixel(ijk_ct, xyz_room, ct_vol);
                         if (ijk_ct[0] < 0 || ijk_ct[0] >= dim_ct[0] || ijk_ct[1] < 0 || ijk_ct[1] >= dim_ct[1] || ijk_ct[2] < 0 || ijk_ct[2] >= dim_ct[2] )
                         {
-                            WER = .88;
+                            WER = PROTON_WER_AIR;
+                            DENSITY = AIR_DENSITY;
                         }
                         else
                         {
                             WER = compute_PrWER_from_HU(ct_img[ijk_ct[2] * dim_ct[0]*dim_ct[1] + ijk_ct[1] * dim_ct[0] + ijk_ct[0] ] );
+                            DENSITY = compute_density_from_HU(ct_img[ijk_ct[2] * dim_ct[0]*dim_ct[1] + ijk_ct[1] * dim_ct[0] + ijk_ct[0] ] );
+                        }
+                        if (DENSITY > 0.8)
+                        {
+                            rpl_dose_img[idx3d_travel] += central_axis_dose 
+                                * WER / DENSITY
+                                * off_axis_factor ;
                         }
-
-                        rpl_dose_img[idx3d_travel] += normalization_dose 
-                            * central_axis_dose 
-                            * WER
-                            * off_axis_factor 
-                            * (float) ppp->weight 
-                            / dose_norm; 
                     } //for j1
                 } //for i1
             } // for k
@@ -1337,25 +568,27 @@ compute_dose_ray_sharp (
     } // ap_ij[0]   
 }
 
-void compute_dose_ray_shackleford(Volume::Pointer dose_vol, Rt_plan* plan, const Rt_depth_dose* ppp, std::vector<double>* area, std::vector<double>* xy_grid, int radius_sample, int theta_sample)
+void compute_dose_ray_shackleford (
+    Volume::Pointer dose_vol,
+    Rt_plan* plan,
+    Rt_beam* beam,
+    int beam_index,
+    std::vector<double>* area,
+    std::vector<double>* xy_grid,
+    int radius_sample,
+    int theta_sample)
 {
     int ijk[3] = {0,0,0};
     double xyz[4] = {0,0,0,1};
     double xyz_travel[4] = {0,0,0,1};
     double tmp_xy[4] = {0,0,0,1};
     double tmp_cst = 0;
-
-    double dose_norm = get_dose_norm('h', ppp->E0, 1); //the Hong algorithm has no PB density, everything depends on the number of sectors
-
     int idx = 0;
-	
-    int ct_dim[3] = {dose_vol->dim[0], dose_vol->dim[1], dose_vol->dim[2]};
+    const plm_long *dose_dim = dose_vol->dim;
     double vec_ud[4] = {0,0,0,1};
     double vec_rl[4] = {0,0,0,1};
-
     float* ct_img = (float*) plan->get_patient_volume()->img;
     float* dose_img = (float*) dose_vol->img;
-
     double sigma_travel = 0;
     double sigma_3 = 0;
     double rg_length = 0;
@@ -1367,35 +600,43 @@ void compute_dose_ray_shackleford(Volume::Pointer dose_vol, Rt_plan* plan, const
     double theta = 0;
     double dr = 0;
 
+    double idx_ap[2] = {0,0};
+    int idx_ap_int[2] = {0,0};
+    double rest[2] = {0,0};
+    float particle_number = 0;
+
+    unsigned char *ap_img = 0;	
+    if (beam->get_aperture()->have_aperture_image()) {
+        Volume::Pointer ap_vol = beam->get_aperture()->get_aperture_volume();
+        ap_img = (unsigned char*) ap_vol->img;
+    }
+
     /* Dose D(POI) = Dose(z_POI) but z_POI =  rg_comp + depth in CT, if there is a range compensator */
-    if (plan->beam->rpl_vol->get_aperture()->have_range_compensator_image())
+    if (beam->rpl_vol->get_aperture()->have_range_compensator_image())
     {
-        add_rcomp_length_to_rpl_volume(plan->beam);
+        add_rcomp_length_to_rpl_volume(beam);
     }
-
-    vec3_copy(vec_ud, plan->beam->rpl_vol->get_proj_volume()->get_incr_c());
+    vec3_copy(vec_ud, beam->rpl_vol->get_proj_volume()->get_incr_c());
     vec3_normalize1(vec_ud);
-
-    vec3_copy(vec_rl, plan->beam->rpl_vol->get_proj_volume()->get_incr_r());
+    vec3_copy(vec_rl, beam->rpl_vol->get_proj_volume()->get_incr_r());
     vec3_normalize1(vec_rl);
 
-    for (ijk[0] = 0; ijk[0] < ct_dim[0]; ijk[0]++){
-        for (ijk[1] = 0; ijk[1] < ct_dim[1]; ijk[1]++){
-            for (ijk[2] = 0; ijk[2] < ct_dim[2]; ijk[2]++){
-                idx = ijk[0] + ct_dim[0] * (ijk[1] + ct_dim[1] * ijk[2]);
+    for (ijk[0] = 0; ijk[0] < dose_dim[0]; ijk[0]++){
+        printf("%d ", ijk[0]);
+        for (ijk[1] = 0; ijk[1] < dose_dim[1]; ijk[1]++){
+            for (ijk[2] = 0; ijk[2] < dose_dim[2]; ijk[2]++){
+                idx = ijk[0] + dose_dim[0] * (ijk[1] + dose_dim[1] * ijk[2]);
 
                 /* calculation of the pixel coordinates in the room coordinates */
                 xyz[0] = (double) dose_vol->origin[0] + ijk[0] * dose_vol->spacing[0];
                 xyz[1] = (double) dose_vol->origin[1] + ijk[1] * dose_vol->spacing[1];
                 xyz[2] = (double) dose_vol->origin[2] + ijk[2] * dose_vol->spacing[2]; // xyz[3] always = 1.0
-
-                sigma_3 = 3 * plan->beam->sigma_vol_lg->get_rgdepth(xyz);
+                sigma_3 = 3 * beam->sigma_vol_lg->get_rgdepth(xyz);
 
                 for (int i = 0; i < radius_sample; i++)
                 {
                     for (int j =0; j < theta_sample; j++)
                     {
-
                         vec3_copy(xyz_travel, xyz);
 
                         /* calculation of the center of the sector */
@@ -1409,8 +650,30 @@ void compute_dose_ray_shackleford(Volume::Pointer dose_vol, Rt_plan* plan, const
                         vec3_scale2(tmp_xy, tmp_cst);
                         vec3_add2(xyz_travel,tmp_xy);
 							
-                        rg_length = plan->beam->rpl_vol->get_rgdepth(xyz_travel);
-                        HU = plan->beam->rpl_ct_vol_HU_lg->get_rgdepth(xyz_travel);
+                        rg_length = beam->rpl_vol->get_rgdepth(xyz_travel);
+                        HU = beam->rpl_ct_vol_HU_lg->get_rgdepth(xyz_travel);
+                        if (beam->get_intersection_with_aperture(idx_ap, idx_ap_int, rest, xyz_travel) == false)
+                        {
+                            continue;
+                        }
+
+                        /* Check that the ray cross the aperture */
+                        if (idx_ap[0] < 0 || idx_ap[0] > (double) beam->rpl_ct_vol_HU->get_proj_volume()->get_image_dim(0)-1
+                            || idx_ap[1] < 0 || idx_ap[1] > (double) beam->rpl_ct_vol_HU->get_proj_volume()->get_image_dim(1)-1)
+                        {
+                            continue;
+                        }
+                        /* Check that the ray cross the active part of the aperture */
+                        if (beam->get_aperture()->have_aperture_image() && beam->is_ray_in_the_aperture(idx_ap_int, ap_img) == false)
+                        {
+                            continue;
+                        }
+                        /* Check that the spot map is positive for this ray */
+                        particle_number = beam->get_mebs()->get_particle_number_xyz(idx_ap_int, rest, beam_index, beam->get_aperture()->get_dim());
+                        if (particle_number <= 0)
+                        {
+                            continue;
+                        }
                         ct_density = compute_density_from_HU(HU);
                         STPR = compute_PrSTPR_from_HU(HU);
 							
@@ -1420,27 +683,24 @@ void compute_dose_ray_shackleford(Volume::Pointer dose_vol, Rt_plan* plan, const
                         }
                         else
                         {
+		
                             /* the dose from that sector is summed */
-                            sigma_travel = plan->beam->sigma_vol->get_rgdepth(xyz_travel);
+                            sigma_travel = beam->sigma_vol->get_rgdepth(xyz_travel);
                             radius = vec3_dist(xyz, xyz_travel);
 								
-                            if (sigma_travel < radius / 3 || (plan->beam->get_aperture()->have_aperture_image() == true && plan->beam->aperture_vol->get_rgdepth(xyz_travel) < 0.999)) 
+                            if (sigma_travel < radius / 3)
                             {
                                 continue;
                             }
                             else
                             {
-                                central_sector_dose = ppp->lookup_energy_integration((float) rg_length, ct_density * plan->beam->rpl_vol->get_vol()->spacing[2])* STPR * (1/(sigma_travel*sqrt(2*M_PI)));
+                                central_sector_dose = particle_number * beam->get_mebs()->get_depth_dose()[beam_index]->lookup_energy_integration((float) rg_length, ct_density * beam->rpl_vol->get_vol()->spacing[2])* STPR * (1/(sigma_travel*sqrt(2*M_PI)));
                                 dr = sigma_3 / (2* radius_sample);
-                                // * is normalized to a radius =1, 
-                                // need to be adapted to a 3_sigma 
-                                // radius circle
-                                dose_img[idx] += 
-                                    plan->get_normalization_dose() 
-                                    * central_sector_dose
+                                dose_img[idx] +=  
+                                    central_sector_dose
                                     * compute_PrWER_from_HU(HU)
                                     * get_off_axis(radius, dr, sigma_3/3) 
-                                    * ppp->weight / dose_norm; 
+                                    * beam->get_mebs()->get_weight()[beam_index]; 
                             }
                         }
                     }
@@ -1450,53 +710,11 @@ void compute_dose_ray_shackleford(Volume::Pointer dose_vol, Rt_plan* plan, const
     }
 }
 
-
-double get_dose_norm(char flavor, double energy, double PB_density)
+void add_rcomp_length_to_rpl_volume (Rt_beam* beam)
 {
-    if (flavor == 'a')
-    {
-        return 1; // to be defined
-    }
-    else if (flavor == 'f')
-    {
-        return 1; /* PB_density * (30.5363 + 0.21570 * energy - 0.003356 * energy * energy + 0.00000917 * energy * energy * energy); */
-    }
-    else if (flavor == 'g')
-    {
-        if (energy >= 70)
-        {
-            return 1; /*  60.87 -0.2212*energy + 0.0001536 * energy * energy; */
-        }
-        else
-        {
-            return 1; /* 156.735 -4.4787 * energy + .060607 * energy * energy -0.000275 * energy * energy * energy; */
-        }
-    }
-    else if (flavor == 'h')
-    {
-        return 1;
-        /* 
-           if (energy >= 100)
-           {
-           return 88.84 -0.3574*energy + .00001284 * energy * energy + 0.000001468 * energy * energy * energy;
-           }
-           else
-           {
-           return 303.34 -7.7026 * energy + .09067 * energy * energy - 0.0003862 * energy * energy * energy;
-           } */
-    }
-    else
-    {
-        return 1;
-    }
-}
-
-void add_rcomp_length_to_rpl_volume(Rt_beam* beam)
-{
-    int dim[3] = {beam->rpl_vol->get_vol()->dim[0], beam->rpl_vol->get_vol()->dim[1], beam->rpl_vol->get_vol()->dim[2]};
+    const plm_long *dim = beam->rpl_vol->get_vol()->dim;
     float* rpl_img = (float*) beam->rpl_vol->get_vol()->img;
     float* rc_img = (float*) beam->rpl_vol->get_aperture()->get_range_compensator_volume()->img;
-
     int idx = 0;
 
     for(int i = 0; i < dim[0] * dim[1]; i++)
@@ -1504,7 +722,7 @@ void add_rcomp_length_to_rpl_volume(Rt_beam* beam)
         for (int k = 0; k < dim[2]; k++)
         {
             idx = i + k * dim[0] * dim[1];
-            rpl_img[idx] += rc_img[i] * 1.19*.98; // Lucite material : d * rho * WER
+            rpl_img[idx] += rc_img[i] * PMMA_DENSITY*PMMA_STPR; // Lucite material : d * rho * WER
         }
     }
 }
diff --git a/src/plastimatch/dose/rt_dose.h b/src/plastimatch/dose/rt_dose.h
index 6879457..ceac39a 100644
--- a/src/plastimatch/dose/rt_dose.h
+++ b/src/plastimatch/dose/rt_dose.h
@@ -1,8 +1,8 @@
 /* -----------------------------------------------------------------------
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
-#ifndef _Rt_dose_h_
-#define _Rt_dose_h_
+#ifndef _rt_dose_h_
+#define _rt_dose_h_
 
 #include "aperture.h"
 #include "rt_depth_dose.h"
@@ -10,79 +10,37 @@
 #include "plmdose_config.h"
 #include "plm_image.h"
 
-PLMDOSE_API
-Plm_image::Pointer
-proton_dose_compute (Rt_plan::Pointer& scene);
-
-double
-dose_direct (
-    double* ct_xyz,             /* voxel to dose */
-    Rt_beam* beam
-);
-double
-dose_debug (
-    double* ct_xyz,             /* voxel to dose */
-    Rt_beam* beam
-);
-double
-dose_scatter (
-    double* ct_xyz,
-    plm_long* ct_ijk,            // DEBUG
-    Rt_beam* beam
-);
 double
-dose_hong (
-    double* ct_xyz,
-    plm_long* ct_ijk,            // DEBUG
-    Rt_beam* beam
-);
-
-double
-dose_hong_maxime (
-    double* ct_xyz,
-    plm_long* ct_ijk,            // DEBUG
-    Rt_beam* beam
-);
-
-double
-dose_hong_sharp (
-    double* ct_xyz,             /* voxel to dose */
-    Rt_beam* beam
+energy_direct (
+    float rgdepth,             /* voxel to dose */
+    Rt_beam* beam,
+	int beam_idx
 );
 
 void compute_dose_ray_desplanques (
     Volume* dose_volume, 
     Volume::Pointer ct_vol, 
-    Rpl_volume* rpl_vol, 
-    Rpl_volume* sigma_vol, 
-    Rpl_volume* ct_vol_density, 
     Rt_beam* beam, 
     Volume::Pointer final_dose_volume, 
-    const Rt_depth_dose* ppp, 
-    float normalization_dose
+    int beam_index
 );
 void compute_dose_ray_sharp (
-    const Volume::Pointer ct_vol, 
-    const Rpl_volume* rpl_vol, 
-    const Rpl_volume* sigma_vol, 
-    Rpl_volume* ct_vol_density, 
-    const Rt_beam* beam, Rpl_volume* rpl_dose_volume, 
-    const Aperture::Pointer ap, 
-    const Rt_depth_dose* ppp, 
-    const int* margins, 
-    float normalization_dose
+    const Volume::Pointer ct_vol,  
+    Rt_beam* beam,
+    Rpl_volume* rpl_dose_volume,
+	int beam_index,
+    const int* margins
 );
 void compute_dose_ray_shackleford (
     Volume::Pointer dose_volume, 
     Rt_plan* plan, 
-    const Rt_depth_dose* ppp, 
+    Rt_beam *beam,
+    int beam_index, 
     std::vector<double>* area, 
     std::vector<double>* xy_grid, 
     int radius_sample, 
     int theta_sample
 );
-
-double get_dose_norm(char flavor, double energy, double PB_density);
 void add_rcomp_length_to_rpl_volume(Rt_beam* beam);
 
 #endif
diff --git a/src/plastimatch/dose/rt_lut.cxx b/src/plastimatch/dose/rt_lut.cxx
index a149030..c9676a7 100644
--- a/src/plastimatch/dose/rt_lut.cxx
+++ b/src/plastimatch/dose/rt_lut.cxx
@@ -4,10 +4,11 @@
 #include "plmdose_config.h"
 
 #include "rt_lut.h"
+#include "rpl_volume.h" // for PMMA constants
 
-double getrange(double energy)
+double get_proton_range(double energy)
 {
-    int i_lo = 0, i_hi = 110;
+    int i_lo = 0, i_hi = PROTON_TABLE_SIZE;
     double energy_lo = lookup_proton_range_water[i_lo][0];
     double range_lo = lookup_proton_range_water[i_lo][1];
     double energy_hi = lookup_proton_range_water[i_hi][0];
@@ -39,9 +40,9 @@ double getrange(double energy)
         (energy-energy_lo) * (range_hi-range_lo) / (energy_hi-energy_lo);
 }
 
-double getstop (double energy)
+double get_proton_stop (double energy)
 {
-    int i_lo = 0, i_hi = 110;
+    int i_lo = 0, i_hi = PROTON_TABLE_SIZE;
     double energy_lo = lookup_proton_stop_water[i_lo][0];
     double stop_lo = lookup_proton_stop_water[i_lo][1];
     double energy_hi = lookup_proton_stop_water[i_hi][0];
@@ -73,35 +74,70 @@ double getstop (double energy)
         (energy-energy_lo) * (stop_hi-stop_lo) / (energy_hi-energy_lo);
 }
 
-double get_dose_max(double E0)
+double get_theta0_Highland(double range)
 {
-    if (E0 < 0 || E0 > 255)
-    {
-        return 1;
-    }
+	/* lucite sigma0 (in rads) computing- From the figure A2 of the Hong's paper (be careful, in this paper the fit shows sigma0^2)*/
+	if (range > 150)
+	{
+		return 0.05464 + 5.8348E-6 * range -5.21006E-9 * range * range;
+	}
+	else 
+	{
+		return 0.05394 + 1.80222E-5 * range -5.5430E-8 * range * range;
+	}
+}
+
+double get_theta0_MC(float energy)
+{
+	return 4.742E-6 * energy * energy -1.918E-3 * energy + 1.158;
+}
 
-    int E0_floor = floor(E0);
-    double rest = E0 - (double) E0_floor;
-		
-    return lookup_proton_dose_max_bragg[E0_floor][0] + rest * (lookup_proton_dose_max_bragg[E0_floor+1][0]-lookup_proton_dose_max_bragg[E0_floor][0]);
+double get_theta_rel_Highland(double rc_over_range)
+{
+	return rc_over_range * ( 1.6047 -2.7295 * rc_over_range + 2.1578 * rc_over_range * rc_over_range);
 }
 
-int get_depth_max(double E0)
+double get_theta_rel_MC(double rc_over_range)
 {
-    int E0_floor = floorf(E0); 
-    if (E0 < 0)
+	return 3.833E-2 * pow(rc_over_range, 0.657) + 2.118E-2 * pow(rc_over_range, 6.528);
+}
+
+double get_scat_or_Highland(double rc_over_range)
+{
+	/* calculation of rc_eff - see Hong's paper graph A3 - linear interpolation of the curve */
+	if (rc_over_range >= 0 && rc_over_range < 0.5)
     {
-        return 0;
+        return 1 - (0.49 + 0.060 / 0.5 * rc_over_range);
+    }
+    else if (rc_over_range >= 0.5 && rc_over_range <0.8)
+    {
+        return 1 - (0.55 + 0.085 / 0.3 * (rc_over_range-0.5));
+    }
+    else if (rc_over_range >= 0.8 && rc_over_range <0.9)
+    {
+        return 1 - (0.635 + 0.055 / 0.1 * (rc_over_range-0.8));
     }
-    else if (E0 >255)
+    else if (rc_over_range >= 0.9 && rc_over_range <0.95)
     {
-        return 40000;
+        return 1 - (0.690 + (rc_over_range-0.9));
+    }
+    else if (rc_over_range >= 0.95 && rc_over_range <= 1)
+    {
+        return 1 - (0.740 + 0.26/0.05 * (rc_over_range-0.95));
+    }
+    else if (rc_over_range > 1)
+    {
+        return 0;
     }
     else
     {
-        return lookup_proton_dose_max_bragg[E0_floor][1];
+        return 0;
     }
+}
 
+double get_scat_or_MC(double rc_over_range)
+{
+	return 0.023 * rc_over_range + 0.332;
 }
 
 double compute_X0_from_HU(double CT_HU)
@@ -360,574 +396,12 @@ const double lookup_proton_stop_water[][2] =
 500.00,	2.743,
 };
 
-extern const double lookup_proton_dose_max_bragg[][2] = { // [0]: dose max, [1]: depth max in 0.01mm
-230.1791,	0, // first line E = 0, extrapolation to 0 MeV
-230.1791,	0,
-220.9350,	4,
-219.9710,	13,
-223.9043,	23,
-226.3141,	35,
-227.6480,	50,
-227.6221,	66,
-226.1386,	85,
-223.2419,	105,
-219.0823,	127,
-213.9347,	151,
-208.0689,	176,
-201.7980,	203,
-194.8930,	232,
-188.8440,	262,
-182.3840,	294,
-176.4200,	327,
-170.5880,	362,
-164.7550,	399,
-159.5390,	436,
-154.7960,	476,
-150.1000,	517,
-145.6560,	559,
-141.5270,	603,
-137.5560,	648,
-133.8990,	695,
-130.3960,	743,
-127.0300,	793,
-123.9160,	843,
-120.9480,	896,
-118.1550,	949,
-115.4790,	1004,
-112.9110,	1060,
-110.4990,	1118,
-108.1800,	1177,
-105.9680,	1237,
-103.8380,	1299,
-101.8120,	1361,
-99.8712,	1426,
-98.0264,	1491,
-96.2303,	1558,
-94.5115,	1626,
-92.8698,	1695,
-91.2850,	1765,
-89.7541,	1837,
-88.2773,	1910,
-86.8559,	1984,
-85.4793,	2059,
-84.1527,	2136,
-82.8659,	2214,
-81.6235,	2293,
-80.4220,	2373,
-79.2532,	2454,
-78.1254,	2537,
-77.0279,	2621,
-75.9635,	2706,
-74.9307,	2792,
-73.9252,	2879,
-72.9495,	2968,
-71.9976,	3057,
-71.0753,	3148,
-70.1759,	3240,
-69.2986,	3333,
-68.4462,	3428,
-67.6145,	3523,
-66.8025,	3620,
-66.0098,	3717,
-65.2381,	3816,
-64.4838,	3916,
-63.7465,	4017,
-63.0258,	4120,
-62.3234,	4223,
-61.6351,	4327,
-60.9625,	4433,
-60.3032,	4539,
-59.6599,	4647,
-59.0294,	4756,
-58.4119,	4866,
-57.8070,	4977,
-57.2142,	5089,
-56.6329,	5202,
-56.0637,	5317,
-55.5057,	5432,
-54.9573,	5549,
-54.4209,	5666,
-53.8933,	5785,
-53.3761,	5904,
-52.8684,	6025,
-52.3695,	6147,
-51.8795,	6270,
-51.3981,	6394,
-50.9252,	6519,
-50.4605,	6645,
-50.0039,	6772,
-49.5550,	6900,
-49.1135,	7029,
-48.6792,	7159,
-48.2518,	7290,
-47.8313,	7423,
-47.4180,	7556,
-47.0109,	7690,
-46.6099,	7826,
-46.2156,	7962,
-45.8270,	8099,
-45.4445,	8238,
-45.0676,	8377,
-44.6963,	8518,
-44.3305,	8659,
-43.9701,	8802,
-43.6148,	8945,
-43.2646,	9090,
-42.9194,	9235,
-42.5791,	9382,
-42.2433,	9529,
-41.9125,	9678,
-41.5858,	9827,
-41.2639,	9978,
-40.9460,	10130,
-40.6326,	10282,
-40.3231,	10436,
-40.0179,	10590,
-39.7165,	10746,
-39.4191,	10902,
-39.1252,	11060,
-38.8354,	11218,
-38.5489,	11377,
-38.2663,	11538,
-37.9870,	11699,
-37.7110,	11862,
-37.4386,	12025,
-37.1694,	12189,
-36.9033,	12354,
-36.6406,	12521,
-36.3809,	12688,
-36.1242,	12856,
-35.8705,	13025,
-35.6197,	13195,
-35.3717,	13366,
-35.1267,	13538,
-34.8844,	13711,
-34.6448,	13885,
-34.4079,	14060,
-34.1736,	14236,
-33.9419,	14413,
-33.7126,	14591,
-33.4860,	14769,
-33.2617,	14949,
-33.0398,	15129,
-32.8204,	15311,
-32.6032,	15493,
-32.3883,	15677,
-32.1756,	15861,
-31.9652,	16046,
-31.7568,	16233,
-31.5507,	16420,
-31.3467,	16608,
-31.1447,	16797,
-30.9448,	16987,
-30.7468,	17178,
-30.5509,	17369,
-30.3568,	17562,
-30.1647,	17756,
-29.9745,	17950,
-29.7861,	18146,
-29.5995,	18342,
-29.4147,	18539,
-29.2317,	18738,
-29.0505,	18937,
-28.8709,	19137,
-28.6931,	19338,
-28.5169,	19540,
-28.3423,	19743,
-28.1694,	19946,
-27.9981,	20151,
-27.8283,	20357,
-27.6601,	20563,
-27.4934,	20770,
-27.3282,	20979,
-27.1645,	21188,
-27.0023,	21398,
-26.8416,	21609,
-26.6822,	21821,
-26.5243,	22034,
-26.3677,	22247,
-26.2126,	22462,
-26.0587,	22677,
-25.9062,	22894,
-25.7551,	23111,
-25.6052,	23329,
-25.4566,	23548,
-25.3093,	23768,
-25.1633,	23989,
-25.0184,	24210,
-24.8748,	24433,
-24.7324,	24657,
-24.5912,	24881,
-24.4511,	25106,
-24.3122,	25332,
-24.1745,	25559,
-24.0379,	25787,
-23.9024,	26016,
-23.7680,	26246,
-23.6347,	26476,
-23.5025,	26707,
-23.3714,	26940,
-23.2413,	27173,
-23.1122,	27407,
-22.9842,	27642,
-22.8572,	27878,
-22.7312,	28114,
-22.6062,	28352,
-22.4821,	28590,
-22.3591,	28829,
-22.2370,	29070,
-22.1158,	29311,
-21.9956,	29552,
-21.8763,	29795,
-21.7579,	30039,
-21.6405,	30283,
-21.5239,	30528,
-21.4082,	30775,
-21.2934,	31022,
-21.1795,	31270,
-21.0664,	31518,
-20.9542,	31768,
-20.8428,	32018,
-20.7323,	32270,
-20.6226,	32522,
-20.5137,	32775,
-20.4056,	33029,
-20.2983,	33283,
-20.1917,	33539,
-20.0860,	33795,
-19.9810,	34053,
-19.8769,	34311,
-19.7734,	34570,
-19.6707,	34830,
-19.5688,	35090,
-19.4676,	35352,
-19.3671,	35614,
-19.2673,	35877,
-19.1683,	36141,
-19.0699,	36406,
-18.9723,	36672,
-18.8753,	36939,
-18.7790,	37206,
-18.6834,	37474,
-18.5885,	37743,
-18.4943,	38013,
-18.4007,	38284,
-18.3077,	38556,
-18.2154,	38828,
-18.1238,	39101,
-18.0328,	39375,
-17.9424,	39650,
-};
-
-const double lookup_off_axis[][2]={ // x[0] = r/sigma, x[1] = exp(-x�/(2 * sigma�))
-0.00, 1.0000,
-0.01, 1.0000,
-0.02, 0.9998,
-0.03, 0.9996,
-0.04, 0.9992,
-0.05, 0.9988,
-0.06, 0.9982,
-0.07, 0.9976,
-0.08, 0.9968,
-0.09, 0.9960,
-0.10, 0.9950,
-0.11, 0.9940,
-0.12, 0.9928,
-0.13, 0.9916,
-0.14, 0.9902,
-0.15, 0.9888,
-0.16, 0.9873,
-0.17, 0.9857,
-0.18, 0.9839,
-0.19, 0.9821,
-0.20, 0.9802,
-0.21, 0.9782,
-0.22, 0.9761,
-0.23, 0.9739,
-0.24, 0.9716,
-0.25, 0.9692,
-0.26, 0.9668,
-0.27, 0.9642,
-0.28, 0.9616,
-0.29, 0.9588,
-0.30, 0.9560,
-0.31, 0.9531,
-0.32, 0.9501,
-0.33, 0.9470,
-0.34, 0.9438,
-0.35, 0.9406,
-0.36, 0.9373,
-0.37, 0.9338,
-0.38, 0.9303,
-0.39, 0.9268,
-0.40, 0.9231,
-0.41, 0.9194,
-0.42, 0.9156,
-0.43, 0.9117,
-0.44, 0.9077,
-0.45, 0.9037,
-0.46, 0.8996,
-0.47, 0.8954,
-0.48, 0.8912,
-0.49, 0.8869,
-0.50, 0.8825,
-0.51, 0.8781,
-0.52, 0.8735,
-0.53, 0.8690,
-0.54, 0.8643,
-0.55, 0.8596,
-0.56, 0.8549,
-0.57, 0.8501,
-0.58, 0.8452,
-0.59, 0.8403,
-0.60, 0.8353,
-0.61, 0.8302,
-0.62, 0.8251,
-0.63, 0.8200,
-0.64, 0.8148,
-0.65, 0.8096,
-0.66, 0.8043,
-0.67, 0.7990,
-0.68, 0.7936,
-0.69, 0.7882,
-0.70, 0.7827,
-0.71, 0.7772,
-0.72, 0.7717,
-0.73, 0.7661,
-0.74, 0.7605,
-0.75, 0.7548,
-0.76, 0.7492,
-0.77, 0.7435,
-0.78, 0.7377,
-0.79, 0.7319,
-0.80, 0.7261,
-0.81, 0.7203,
-0.82, 0.7145,
-0.83, 0.7086,
-0.84, 0.7027,
-0.85, 0.6968,
-0.86, 0.6909,
-0.87, 0.6849,
-0.88, 0.6790,
-0.89, 0.6730,
-0.90, 0.6670,
-0.91, 0.6610,
-0.92, 0.6549,
-0.93, 0.6489,
-0.94, 0.6429,
-0.95, 0.6368,
-0.96, 0.6308,
-0.97, 0.6247,
-0.98, 0.6187,
-0.99, 0.6126,
-1.00, 0.6065,
-1.01, 0.6005,
-1.02, 0.5944,
-1.03, 0.5883,
-1.04, 0.5823,
-1.05, 0.5762,
-1.06, 0.5702,
-1.07, 0.5641,
-1.08, 0.5581,
-1.09, 0.5521,
-1.10, 0.5461,
-1.11, 0.5401,
-1.12, 0.5341,
-1.13, 0.5281,
-1.14, 0.5222,
-1.15, 0.5162,
-1.16, 0.5103,
-1.17, 0.5044,
-1.18, 0.4985,
-1.19, 0.4926,
-1.20, 0.4868,
-1.21, 0.4809,
-1.22, 0.4751,
-1.23, 0.4693,
-1.24, 0.4636,
-1.25, 0.4578,
-1.26, 0.4521,
-1.27, 0.4464,
-1.28, 0.4408,
-1.29, 0.4352,
-1.30, 0.4296,
-1.31, 0.4240,
-1.32, 0.4184,
-1.33, 0.4129,
-1.34, 0.4075,
-1.35, 0.4020,
-1.36, 0.3966,
-1.37, 0.3912,
-1.38, 0.3859,
-1.39, 0.3806,
-1.40, 0.3753,
-1.41, 0.3701,
-1.42, 0.3649,
-1.43, 0.3597,
-1.44, 0.3546,
-1.45, 0.3495,
-1.46, 0.3445,
-1.47, 0.3394,
-1.48, 0.3345,
-1.49, 0.3295,
-1.50, 0.3247,
-1.51, 0.3198,
-1.52, 0.3150,
-1.53, 0.3102,
-1.54, 0.3055,
-1.55, 0.3008,
-1.56, 0.2962,
-1.57, 0.2916,
-1.58, 0.2870,
-1.59, 0.2825,
-1.60, 0.2780,
-1.61, 0.2736,
-1.62, 0.2692,
-1.63, 0.2649,
-1.64, 0.2606,
-1.65, 0.2563,
-1.66, 0.2521,
-1.67, 0.2480,
-1.68, 0.2439,
-1.69, 0.2398,
-1.70, 0.2357,
-1.71, 0.2318,
-1.72, 0.2278,
-1.73, 0.2239,
-1.74, 0.2201,
-1.75, 0.2163,
-1.76, 0.2125,
-1.77, 0.2088,
-1.78, 0.2051,
-1.79, 0.2015,
-1.80, 0.1979,
-1.81, 0.1944,
-1.82, 0.1909,
-1.83, 0.1874,
-1.84, 0.1840,
-1.85, 0.1806,
-1.86, 0.1773,
-1.87, 0.1740,
-1.88, 0.1708,
-1.89, 0.1676,
-1.90, 0.1645,
-1.91, 0.1614,
-1.92, 0.1583,
-1.93, 0.1553,
-1.94, 0.1523,
-1.95, 0.1494,
-1.96, 0.1465,
-1.97, 0.1436,
-1.98, 0.1408,
-1.99, 0.1381,
-2.00, 0.1353,
-2.01, 0.1326,
-2.02, 0.1300,
-2.03, 0.1274,
-2.04, 0.1248,
-2.05, 0.1223,
-2.06, 0.1198,
-2.07, 0.1174,
-2.08, 0.1150,
-2.09, 0.1126,
-2.10, 0.1103,
-2.11, 0.1080,
-2.12, 0.1057,
-2.13, 0.1035,
-2.14, 0.1013,
-2.15, 0.0991,
-2.16, 0.0970,
-2.17, 0.0949,
-2.18, 0.0929,
-2.19, 0.0909,
-2.20, 0.0889,
-2.21, 0.0870,
-2.22, 0.0851,
-2.23, 0.0832,
-2.24, 0.0814,
-2.25, 0.0796,
-2.26, 0.0778,
-2.27, 0.0760,
-2.28, 0.0743,
-2.29, 0.0727,
-2.30, 0.0710,
-2.31, 0.0694,
-2.32, 0.0678,
-2.33, 0.0662,
-2.34, 0.0647,
-2.35, 0.0632,
-2.36, 0.0617,
-2.37, 0.0603,
-2.38, 0.0589,
-2.39, 0.0575,
-2.40, 0.0561,
-2.41, 0.0548,
-2.42, 0.0535,
-2.43, 0.0522,
-2.44, 0.0510,
-2.45, 0.0497,
-2.46, 0.0485,
-2.47, 0.0473,
-2.48, 0.0462,
-2.49, 0.0450,
-2.50, 0.0439,
-2.51, 0.0428,
-2.52, 0.0418,
-2.53, 0.0407,
-2.54, 0.0397,
-2.55, 0.0387,
-2.56, 0.0377,
-2.57, 0.0368,
-2.58, 0.0359,
-2.59, 0.0349,
-2.60, 0.0340,
-2.61, 0.0332,
-2.62, 0.0323,
-2.63, 0.0315,
-2.64, 0.0307,
-2.65, 0.0299,
-2.66, 0.0291,
-2.67, 0.0283,
-2.68, 0.0276,
-2.69, 0.0268,
-2.70, 0.0261,
-2.71, 0.0254,
-2.72, 0.0247,
-2.73, 0.0241,
-2.74, 0.0234,
-2.75, 0.0228,
-2.76, 0.0222,
-2.77, 0.0216,
-2.78, 0.0210,
-2.79, 0.0204,
-2.80, 0.0198,
-2.81, 0.0193,
-2.82, 0.0188,
-2.83, 0.0182,
-2.84, 0.0177,
-2.85, 0.0172,
-2.86, 0.0167,
-2.87, 0.0163,
-2.88, 0.0158,
-2.89, 0.0154,
-2.90, 0.0149,
-2.91, 0.0145,
-2.92, 0.0141,
-2.93, 0.0137,
-2.94, 0.0133,
-2.95, 0.0129,
-2.96, 0.0125,
-2.97, 0.0121,
-2.98, 0.0118,
-2.99, 0.0114,
-3.00, 0.0111,
-};
-
-/* matrix that contains the alpha and p parameters Range = f(E, alpha, p)
+/* matrix that contains the alpha and p parameters from equation: Range = f(E, alpha, p)
     First line is proton, second line is He... */
 
 extern const double particle_parameters[][2] = {
-    0.0022, 1.77,   //P
+    0.00217, 1.7709,//P
+	/* To be updated to the right values for ions */
     0.0022, 1.77,   //HE
     0.0022, 1.77,   //LI
     0.0022, 1.77,   //BE
@@ -936,207 +410,3 @@ extern const double particle_parameters[][2] = {
     0.00,   0.00,   //N not used for ion therapy - set to 0
     0.0022, 1.77    //O
 };
-
-extern const int max_depth_proton[] = {
-	0,
-	0,
-	0,
-	0,
-	0,
-	0,
-	0,
-	0,
-	0,
-	1,
-	1,
-	1,
-	1,
-	2,
-	2,
-	2,
-	3,
-	3,
-	3,
-	4,
-	4,
-	4,
-	5,
-	5,
-	6,
-	6,
-	7,
-	7,
-	8,
-	8,
-	9,
-	9,
-	10,
-	10,
-	11,
-	12,
-	12,
-	13,
-	14,
-	14,
-	15,
-	15,
-	16,
-	17,
-	17,
-	18,
-	19,
-	20,
-	20,
-	21,
-	22,
-	23,
-	24,
-	24,
-	25,
-	26,
-	27,
-	28,
-	29,
-	30,
-	30,
-	31,
-	32,
-	33,
-	34,
-	35,
-	36,
-	37,
-	38,
-	39,
-	40,
-	41,
-	42,
-	43,
-	44,
-	45,
-	46,
-	47,
-	49,
-	50,
-	51,
-	52,
-	53,
-	54,
-	55,	
-	57,
-	58,	
-	59,
-	60,
-	61,
-	63,
-	64,
-	65,
-	66,
-	68,
-	69,
-	70,
-	72,
-	73,
-	74,
-	76,
-	77,
-	78,
-	80,
-	81,
-	82,
-	84,
-	85,	
-	87,
-	88,
-	89,
-	91,
-	92,
-	94,
-	95,
-	97,
-	98,
-	100,
-	101,
-	103,
-	104,
-	106,
-	107,
-	109,
-	111,
-	112,
-	114,
-	115,
-	117,
-	119,
-	120,
-	122,
-	124,
-	125,
-	127,
-	129,
-	130,
-	132,
-	134,
-	135,
-	137,
-	139,
-	141,
-	142,
-	144,
-	146,
-	148,	
-	149,
-	151,
-	153,
-	155,
-	157,
-	159,
-	160,
-	162,
-	164,
-	166,
-	168,
-	170,
-	172,
-	174,
-	176,
-	178,
-	179,
-	181,
-	183,
-	185,
-	187,
-	189,
-	191,
-	193,
-	195,
-	197,
-	199,
-	201,
-	204,
-	206,
-	208,
-	210,
-	212,
-	214,
-	216,
-	218,
-	220,
-	222,
-	225,
-	227,
-	229,
-	231,
-	233,
-	235,
-	238,
-	240,
-	242,
-	244,
-	247,
-	249,
-	251,
-	253,
-	256,
-	258,
-};
diff --git a/src/plastimatch/dose/rt_lut.h b/src/plastimatch/dose/rt_lut.h
index 94f0436..aea6970 100644
--- a/src/plastimatch/dose/rt_lut.h
+++ b/src/plastimatch/dose/rt_lut.h
@@ -6,25 +6,40 @@
 
 #include <math.h>
 
-double getrange(double energy);
-double getstop(double energy);
-double get_dose_max(double E0);
-int get_depth_max(double E0);
+double get_proton_range(double energy);
+double get_proton_stop(double energy);
+
+/* Range compensator functions, Highland-based definition or Monte Carlo based definition */
+double get_theta0_Highland(double range);
+double get_theta0_MC(float energy);
+double get_theta_rel_Highland(double rc_over_range);
+double get_theta_rel_MC(double rc_over_range);
+double get_scat_or_Highland(double rc_over_range);
+double get_scat_or_MC(double rc_over_range);
 
 double compute_X0_from_HU(double CT_HU); // Radiation length
 
 extern const double lookup_proton_range_water[][2];
 extern const double lookup_proton_stop_water[][2];
 
-extern const double lookup_proton_dose_max_bragg[][2];
-
-
-extern const double lookup_off_axis[][2];
-
-/* declaration of a matrix that contains the depth of the max for each energy */
-extern const int max_depth_proton[];
-
 /* declaration of a matrix that contains the alpha and p parameters of the particles (Range = f(E, alpha, p) */
 extern const double particle_parameters[][2];
 
+/* Definition of some constants for the Plastimatch dose module */
+const int PROTON_TABLE_SIZE = 110;		// Look up table size -1 
+const double PROTON_E_MAX = 255;		// Maximum proton energy dealt by Plastimatch is 255 MeV.
+const double LIGHT_SPEED = 299792458;	// m.s-1
+const double PROTON_REST_MASS = 939.4;	// MeV
+const double PROTON_WER_AIR = 0.88;
+const double AIR_DENSITY = 0.001205;    // g.cm-3
+
+
+/* Error Function parameters */
+const double ERF_A1 =  0.254829592;
+const double ERF_A2 = -0.284496736;
+const double ERF_A3 =  1.421413741;
+const double ERF_A4 = -1.453152027;
+const double ERF_A5 =  1.061405429;
+const double ERF_P  =  0.3275911;
+
 #endif
diff --git a/src/plastimatch/dose/rt_mebs.cxx b/src/plastimatch/dose/rt_mebs.cxx
new file mode 100644
index 0000000..13a5600
--- /dev/null
+++ b/src/plastimatch/dose/rt_mebs.cxx
@@ -0,0 +1,1693 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plmdose_config.h"
+#include <fstream>
+#include <string>
+#include <utility>
+#include <vector>
+#include <stdio.h>
+#include <math.h>
+
+#include "bragg_curve.h"
+#include "file_util.h"
+#include "path_util.h"
+#include "print_and_exit.h"
+#include "rt_depth_dose.h"
+#include "rt_mebs.h"
+#include "string_util.h"
+
+class Rt_mebs_private {
+public:
+    std::vector<Rt_depth_dose*> depth_dose;
+
+    float* d_lut;               /* depth array (mm) */
+    float* e_lut;               /* energy array (MeV) */
+    float* f_lut;				/* integrated energy array (MeV) */
+    int num_samples;	        /* number of depths */
+
+    float beam_min_energy;
+    float beam_max_energy;
+    float energy_res;
+    int energy_number;
+
+    float target_min_depth;			/* lower depth  of target */
+    float target_max_depth;			/* higher depth of target */
+    float depth_res;				/* depth resolution */
+    float depth_end;				/* end of the depth array */
+
+    float prescription_depth_min;
+    float prescription_depth_max;
+    float proximal_margin;
+    float distal_margin;
+
+    double spread; /* spread for optimized SOBP, may be changed to a vector for each energy */
+
+    /* p  & alpha are parameters that bind depth and energy 
+       according to ICRU */
+    Particle_type particle_type;			
+    double alpha;
+    double p;
+
+    float photon_energy; // energy for photon mono-energetic beams
+
+    /* When a new sobp is created from an existing sobp, 
+       the peaks are copied (not manual).  Modifications of 
+       an implicitly defined sobp (by adding a peak) will 
+       delete any existing peaks. */
+    bool have_copied_peaks;
+    bool have_manual_peaks;
+    bool have_prescription;
+    bool have_particle_number_map;
+
+    /* passive scattering: weights of the mono-energetic beams */
+    std::vector<float> depth_dose_weight;
+    std::vector<float> energies;
+
+    /* min and max wed for each ray */
+    std::vector<double> min_wed_map;
+    std::vector<double> max_wed_map;
+
+    /* particle number map for both beam line systems */
+    std::vector<float> num_particles;
+
+    /* particle number file paths */
+    std::string particle_number_in;
+    std::string particle_number_out;
+
+public:
+    Rt_mebs_private ()
+    {
+        this->d_lut = new float[0];
+        this->e_lut = new float[0];
+        this->f_lut = new float[0];
+        this->num_samples = 0;
+
+        this->beam_min_energy = 0.f;
+        this->beam_max_energy = 0.f;
+        this->energy_res = 1.f;
+        this->energy_number = 1;
+
+        this->target_min_depth = 0.f;			/* lower depth  of target */
+        this->target_max_depth = 0.f;			/* higher depth of target */
+        this->depth_res = 0.01f;				/* depth resolution */
+        this->depth_end = 20.f;
+
+        this->prescription_depth_min = 0.f;
+        this->prescription_depth_max = 0.f;
+        this->proximal_margin = 0.f;
+        this->distal_margin = 0.f;
+
+        this->spread = 1.0;
+
+        this->particle_type = PARTICLE_TYPE_P;
+        this->alpha = particle_parameters[0][0];
+        this->p = particle_parameters[0][1];
+
+        this->photon_energy = 6.f;
+
+        this->have_copied_peaks = false;
+        this->have_manual_peaks = false;
+        this->have_prescription = false;
+        this->have_particle_number_map = false;
+
+        this->particle_number_in ="";
+        this->particle_number_out ="";
+    }
+    Rt_mebs_private (Particle_type part)
+    {
+        this->d_lut = new float[0];
+        this->e_lut = new float[0];
+        this->f_lut = new float[0];
+        this->num_samples = 0;
+
+        this->beam_min_energy = 0.f;
+        this->beam_max_energy = 0.f;
+        this->energy_res = 1.f;
+        this->energy_number = 1;
+
+        this->target_min_depth = 0.f;			/* lower depth  of target */
+        this->target_max_depth = 0.f;			/* higher depth of target */
+        this->depth_res = 0.01f;				/* depth resolution */
+        this->depth_end = 20.f;
+
+        this->prescription_depth_min = 0.f;
+        this->prescription_depth_max = 0.f;
+        this->proximal_margin = 0.f;
+        this->distal_margin =0.f;
+
+        this->spread = 1.0;
+
+        this->set_particle_type(part);
+
+        this->photon_energy = 6.f;
+
+        this->have_copied_peaks = false;
+        this->have_manual_peaks = false;
+        this->have_prescription = false;
+        this->have_particle_number_map = false;
+
+        this->particle_number_in ="";
+        this->particle_number_out ="";
+    }
+    Rt_mebs_private (const Rt_mebs_private* rsp)
+    {
+        this->d_lut = new float[0];
+        this->e_lut = new float[0];
+        this->f_lut = new float[0];
+        this->num_samples = rsp->num_samples;
+
+        this->beam_min_energy = rsp->beam_min_energy;
+        this->beam_max_energy = rsp->beam_max_energy;
+        this->energy_res = rsp->energy_res;
+        this->energy_number = rsp->energy_number;
+
+        this->target_min_depth = rsp->target_min_depth;	/* lower depth  of target */
+        this->target_max_depth = rsp->target_max_depth;	/* higher depth of target */
+        this->depth_res = rsp->depth_res;				/* depth resolution */
+        this->depth_end = rsp->depth_end;
+
+        this->prescription_depth_min = rsp->prescription_depth_min;
+        this->prescription_depth_max = rsp->prescription_depth_max;
+        this->proximal_margin = rsp->proximal_margin;
+        this->distal_margin = rsp->distal_margin;
+
+        this->spread =rsp->spread;
+
+        this->particle_type = rsp->particle_type;
+        this->alpha = rsp->alpha;
+        this->p = rsp->p;
+
+        this->photon_energy = rsp->photon_energy;
+
+        this->have_copied_peaks = true;
+        this->have_manual_peaks = rsp->have_manual_peaks;
+        this->have_prescription = rsp->have_prescription;
+        this->have_particle_number_map = rsp->have_particle_number_map;
+
+        this->particle_number_in = rsp->particle_number_in;
+        this->particle_number_out = rsp->particle_number_out;
+
+        /* copy the associated depth dose and update the dept dose curve */
+        for (int i = 0; i < rsp->depth_dose.size(); i++)
+        {
+            this->depth_dose.push_back(rsp->depth_dose[i]);
+        }
+        for (int i = 0; i < rsp->depth_dose_weight.size(); i++)
+        {
+            this->depth_dose_weight.push_back(rsp->depth_dose_weight[i]);
+        }
+        for (int i = 0; i < rsp->energies.size(); i++)
+        {
+            this->energies.push_back(rsp->energies[i]);
+        }
+        for (int i = 0; i < rsp->num_particles.size(); i++)
+        {
+            this->num_particles.push_back(rsp->num_particles[i]);
+        }
+
+        /* update the global depth_dose curve */
+        this->d_lut = (float*) malloc (this->num_samples*sizeof(float));
+        this->e_lut = (float*) malloc (this->num_samples*sizeof(float));
+        this->f_lut = (float*) malloc (this->num_samples*sizeof(float));
+    
+        memset (this->d_lut, 0, this->num_samples*sizeof(float));
+        memset (this->e_lut, 0, this->num_samples*sizeof(float));
+        memset (this->f_lut, 0, this->num_samples*sizeof(float));
+        for (int i = 0; i < rsp->num_samples; i++)
+        {
+            this->d_lut[i] = rsp->d_lut[i];
+            this->e_lut[i] = rsp->e_lut[i];
+            this->f_lut[i] = rsp->f_lut[i];
+        }
+    }
+    ~Rt_mebs_private ()
+    {
+        if (d_lut) delete[] d_lut;
+        if (e_lut) delete[] e_lut;
+        if (f_lut) delete[] f_lut;
+        clear_depth_dose ();
+    }
+public:
+    void set_particle_type (Particle_type part)
+    {
+        this->particle_type = part;
+        switch (particle_type) {
+        case PARTICLE_TYPE_P:
+            alpha = particle_parameters[0][0];
+            p = particle_parameters[0][1];
+            break;
+        case PARTICLE_TYPE_HE:
+            alpha = particle_parameters[1][0];
+            p = particle_parameters[1][1];
+            lprintf ("data for helium particle are not available - based on proton beam data");
+            break;
+        case PARTICLE_TYPE_LI:
+            alpha = particle_parameters[2][0];
+            p = particle_parameters[2][1];
+            lprintf ("data for lithium particle type are not available - based on proton beam data");
+            break;
+        case PARTICLE_TYPE_BE:
+            alpha = particle_parameters[3][0];
+            p = particle_parameters[3][1];
+            lprintf ("data for berilium particle type are not available - based on proton beam data");
+            break;
+        case PARTICLE_TYPE_B:
+            alpha = particle_parameters[4][0];
+            p = particle_parameters[4][1];
+            lprintf ("data for bore particle type are not available - based on proton beam data");
+            break;
+        case PARTICLE_TYPE_C:
+            alpha = particle_parameters[5][0];
+            p = particle_parameters[5][1];
+            lprintf ("data for carbon particle type are not available - based on proton beam data");
+            break;
+        case PARTICLE_TYPE_O:
+            alpha = particle_parameters[7][0];
+            p = particle_parameters[7][1];
+            lprintf ("data for oxygen particle type are not available - based on proton beam data");
+            break;
+        default:
+            alpha = particle_parameters[0][0];
+            p = particle_parameters[0][1];
+            particle_type = PARTICLE_TYPE_P;
+            lprintf ("particle not found - proton beam chosen");
+            break;
+        }
+    }
+    void clear_depth_dose ()
+    {
+        if (depth_dose.size() > 0)
+        {
+            printf("Mono energetic beamlet set is erased.\n");
+        }
+        int stop = depth_dose.size();
+        /* std::vector<Rt_depth_dose*>::iterator it;
+           for (it = depth_dose.begin(); it != depth_dose.end(); ++it) {
+           delete *it;
+           } */
+        depth_dose.clear();
+        /* stop = depth_dose.size();
+           for (int i = 0; i < stop; i++)
+           {   
+           depth_dose.pop_back();
+           } */
+        stop = depth_dose_weight.size();
+        for (int i = 0; i < stop; i++)
+        {
+            depth_dose_weight.pop_back();
+        }
+        stop = energies.size();
+        for (int i = 0; i < stop; i++)
+        {
+            energies.pop_back();
+        }
+        stop = num_particles.size();
+        for (int i = 0; i < stop; i++)
+        {
+            num_particles.pop_back();
+        }
+    }
+	
+};
+
+Rt_mebs::Rt_mebs ()
+{
+    d_ptr = new Rt_mebs_private;
+}
+
+Rt_mebs::Rt_mebs (Particle_type part)
+{
+    d_ptr = new Rt_mebs_private(part);
+}
+
+Rt_mebs::Rt_mebs (const Rt_mebs::Pointer& rt_mebs)
+{
+    d_ptr = new Rt_mebs_private (rt_mebs->d_ptr);
+}
+
+Rt_mebs::~Rt_mebs ()
+{
+    delete d_ptr;
+}
+
+void
+Rt_mebs::clear_depth_dose ()
+{
+    d_ptr->clear_depth_dose ();
+}
+
+void
+Rt_mebs::add_depth_dose (Rt_depth_dose* depth_dose)
+{
+    if (d_ptr->have_copied_peaks == true) {
+        d_ptr->clear_depth_dose ();
+        d_ptr->have_copied_peaks = false;
+    }
+    if (depth_dose->dres != d_ptr->depth_res) {
+        printf("*** ERROR: the depth dose added must have the same resolution than the depth_dose set.\n");
+        printf("depth dose set - resolution: dres = %lf.\n", d_ptr->depth_res);
+        printf("depth dose to be added - resolution: dres = %lf.\n", depth_dose->dres);
+        return;
+    }
+    d_ptr->depth_dose.push_back (depth_dose);
+    d_ptr->energy_number = d_ptr->depth_dose.size();
+    d_ptr->depth_dose_weight.push_back(1);
+    d_ptr->energies.push_back(depth_dose->E0);
+
+    /* update the mebs depth dose length if this one is longer */
+    if (depth_dose->num_samples > d_ptr->num_samples)
+    {
+        d_ptr->num_samples = depth_dose->num_samples;
+    }
+}
+
+void
+Rt_mebs::add_peak (double E0, double spread, double weight)
+{
+    if (d_ptr->have_copied_peaks == true) {
+        d_ptr->clear_depth_dose ();
+        d_ptr->have_copied_peaks = false;
+    }
+
+    switch(d_ptr->particle_type)
+    {
+    case PARTICLE_TYPE_P:			// proton
+    {
+        Rt_depth_dose *depth_dose = new Rt_depth_dose (
+            E0, spread, d_ptr->depth_res, d_ptr->depth_end);
+        if (depth_dose->dend > d_ptr->depth_end)
+        {
+            d_ptr->depth_end = depth_dose->dend;
+        }
+        printf ("Adding peak to sobp (%f, %f, %f) [%f, %f]\n", 
+            (float) E0, (float) spread, (float) weight,
+            d_ptr->depth_res, d_ptr->depth_end);
+        d_ptr->depth_dose.push_back (depth_dose);
+        d_ptr->energy_number = d_ptr->depth_dose.size();
+        d_ptr->depth_dose_weight.push_back((float) weight);
+        d_ptr->energies.push_back(E0);
+
+        /* update the mebs depth dose length if this one is longer */
+        if (depth_dose->num_samples > d_ptr->num_samples)
+        {
+            d_ptr->num_samples = depth_dose->num_samples;
+        }
+    }
+    case PARTICLE_TYPE_HE:			// helium
+    {
+        //to be implemented
+    }
+    break;
+    case PARTICLE_TYPE_LI:			// lithium
+    {
+        //to be implemented
+    }
+    break;
+    case PARTICLE_TYPE_BE:			// berilium
+    {
+        //to be implemented
+    }
+    break;
+    case PARTICLE_TYPE_B:			// bore
+    {
+        //to be implemented
+    }
+    break;
+    case PARTICLE_TYPE_C:			// carbon
+    {
+        //to be implemented
+    }
+    break;
+    case PARTICLE_TYPE_N:			// nitrogen
+    {
+        //to be implemented
+    }
+    break;
+    case PARTICLE_TYPE_O:			// oxygen
+    {
+        //to be implemented
+    }
+    break;
+    default:
+    {
+        //to be implemented
+    }
+    }
+}
+
+float
+Rt_mebs::lookup_energy (
+    float depth)
+{	
+    int i = 0;
+    float energy = 0.0f;
+
+    /* Sanity check */
+    if (depth < 0 || depth > d_ptr->depth_end) {
+        return 0.0f;
+    }
+
+    /* Find index into profile arrays */
+    for (i = (int) floor(depth / d_ptr->depth_res); i < d_ptr->num_samples-1; i++) {
+        if (d_ptr->d_lut[i] > depth) {
+            i--;
+            break;
+        }
+    }
+
+    /* Clip input depth to maximum in lookup table */
+    if (i == d_ptr->num_samples-1) {
+        depth = d_ptr->d_lut[i];
+    }
+	
+    /* Use index to lookup and interpolate energy */
+    if (i >= 0 || i < d_ptr->num_samples-1) {
+        // linear interpolation
+        energy = d_ptr->e_lut[i]
+            + (depth - d_ptr->d_lut[i])
+            * ((d_ptr->e_lut[i+1] - d_ptr->e_lut[i]) 
+                / (d_ptr->d_lut[i+1] - d_ptr->d_lut[i]));
+    } else {
+        // we wen't past the end of the lookup table
+        energy = 0.0f;
+    }
+    return energy;
+}
+
+bool
+Rt_mebs::generate ()
+{
+    printf("depth_dose number %d\n", (int)d_ptr->depth_dose.size());
+
+    /* Construct the data structure first time through */
+    if (d_ptr->d_lut) delete[] d_ptr->d_lut;    
+    if (d_ptr->e_lut) delete[] d_ptr->e_lut;
+    if (d_ptr->f_lut) delete[] d_ptr->f_lut;
+    d_ptr->e_lut = new float [d_ptr->num_samples];
+    d_ptr->f_lut = new float [d_ptr->num_samples];
+    d_ptr->d_lut = new float [d_ptr->num_samples];
+
+    for (int i = 0; i < d_ptr->num_samples; i++) {
+        d_ptr->d_lut[i] = (float) i * d_ptr->depth_res;
+        d_ptr->e_lut[i] = 0;
+        d_ptr->f_lut[i] = 0;
+    }
+
+    for (int it = 0; it < d_ptr->depth_dose.size(); it++)
+    {
+        const Rt_depth_dose *ppp = d_ptr->depth_dose[it];
+        /* Check that this peak has the same resolution */
+        if (ppp->dres != d_ptr->depth_res) {
+            print_and_exit ("Error, mismatch in resolution.\n MEBS: %lg, depth dose # %d: %lg.\n", d_ptr->depth_res, it, ppp->dres);
+        }
+        if (ppp->num_samples > d_ptr->num_samples) {
+            print_and_exit ("Error, num_samples MEBS > num_sample depth dose.\n MEBS: %d, depth dose # %d: %d.\n", d_ptr->num_samples, it, ppp->num_samples);
+        }
+
+        /* Add weighted pristine peak to mebs */
+        for (int i = 0; i < ppp->num_samples; i++) {
+            d_ptr->e_lut[i] += d_ptr->depth_dose_weight[it] * ppp->e_lut[i];
+        }
+
+        /* Go on to next pristine peak */
+        it++;
+    }
+	
+    /* build the integrated dose */
+    if (d_ptr->f_lut[0] && d_ptr->f_lut[0]) {d_ptr->f_lut[0] = d_ptr->e_lut[0];}
+    for (int i = 1; i < d_ptr->num_samples; i++) {
+        d_ptr->f_lut[i] = d_ptr->f_lut[i-1] + d_ptr->e_lut[i];
+    }
+    return true;
+}
+
+void
+Rt_mebs::dump (const char* dir)
+{
+    std::string dirname = dir;
+
+    /* Dump SOBP */
+    std::string sobp_fn = string_format ("%s/bragg_curve.txt", dir);
+    FILE* fp = fopen (sobp_fn.c_str(), "w");
+    for (int i=0; i < d_ptr->num_samples; i++) {
+        fprintf (fp, "%3.2f %3.2f\n", d_ptr->d_lut[i], d_ptr->e_lut[i]);
+    }
+    fclose (fp);
+
+    /* Dump pristine peaks */
+    std::vector<Rt_depth_dose*>::const_iterator it 
+        = d_ptr->depth_dose.begin();
+    while (it != d_ptr->depth_dose.end ()) {
+        std::string fn = string_format ("%s/pristine_%4.2f.txt", dir, 
+            (float) (*it)->E0);
+        (*it)->dump (fn.c_str());
+        it++;
+    }
+}
+
+// return on the command line the parameters of the sobp to be build
+void 
+Rt_mebs::printparameters()
+{
+    printf ("\nParticle type : %s, alpha: %lg, p: %lg\n", particle_type_string (d_ptr->particle_type), d_ptr->alpha, d_ptr->p);
+    printf("Number of depth_dose : %d\n",d_ptr->energy_number = d_ptr->depth_dose.size());
+    printf("Energy set (in MeV):\n");
+    for (int i = 0; i < d_ptr->energies.size(); i++)
+    {
+        printf("%lg ", d_ptr->energies[i]);
+    }
+    printf("\nweights set:\n");
+    for (int i = 0; i < d_ptr->depth_dose_weight.size(); i++)
+    {
+        printf("%lg ", d_ptr->depth_dose_weight[i]);
+    }
+    printf("\nEnegy resolution: %g MeV \n",d_ptr->energy_res);
+    printf("E_min : %g MeV; E_max : %g MeV\n",d_ptr->beam_min_energy, d_ptr->beam_max_energy);
+    printf("num_samples: %d\n", d_ptr->num_samples);
+    printf("depth_min_target : %3.2f mm\n",d_ptr->target_min_depth);
+    printf("depth_max_target : %3.2f mm\n",d_ptr->target_max_depth);
+    printf("depth_resolution : %3.2f mm \n",d_ptr->depth_res);
+    printf("depth_end : %3.2f mm\n",d_ptr->depth_end);
+    printf("prescription depths: proximal: %lg mm, distal: %lg mm\n",d_ptr->prescription_depth_min, d_ptr->prescription_depth_max);
+    printf("margins: proximal: %lg mm, distal: %lg mm\n", d_ptr->proximal_margin, d_ptr->distal_margin);
+}
+
+/* reset the mebs depth dose curve */
+void
+Rt_mebs::reset_mebs_depth_dose_curve()
+{
+    if (d_ptr->d_lut) delete[] d_ptr->d_lut;
+    d_ptr->d_lut = new float[d_ptr->num_samples];
+    if (d_ptr->e_lut) delete[] d_ptr->e_lut;
+    d_ptr->e_lut = new float[d_ptr->num_samples];
+    if (d_ptr->f_lut) delete[] d_ptr->f_lut;
+    d_ptr->f_lut = new float[d_ptr->num_samples];
+
+    for (int i = 0; i < d_ptr->num_samples; i++)
+    {
+        d_ptr->d_lut[i] = (float) i * d_ptr->depth_res;
+        d_ptr->e_lut[i] = 0;
+        d_ptr->f_lut[i] = 0;
+    }
+}
+
+
+/* set the mebs parameters by introducing the min and max energies */
+void 
+Rt_mebs::set_energies(float new_E_min, float new_E_max) 
+{
+    if (new_E_max <= 0 || new_E_min <= 0)
+    {
+        printf("The energies min and max of the Sobp must be positive!\n");
+        printf("Emin = %g, Emax = %g \n", new_E_min, new_E_max);
+        return;
+    }
+	
+    if (new_E_max <= new_E_min)
+    {
+        printf("SOBP: The Energy_max must be superior to the Energy_min.Energies unchanged.\n");
+        printf("Emin = %g, Emax = %g \n", new_E_min, new_E_max);
+        return;
+    }
+
+    d_ptr->beam_min_energy = new_E_min;
+    d_ptr->beam_max_energy = new_E_max;
+    this->update_prescription_depths_from_energies();
+}
+
+void 
+Rt_mebs::set_energies(float new_E_min, float new_E_max, float new_step) 
+{
+   d_ptr->energy_res = new_step;
+   this->set_energies(new_E_min, new_E_max);
+}
+
+/* set the mebs parameters by introducing the min and max energies */
+void 
+Rt_mebs::set_target_depths(float new_depth_min, float new_depth_max) 
+{
+    if (new_depth_max <= 0 || new_depth_min <= 0)
+    {
+        printf("***ERROR*** The depth min and max of the target must be positive!\n");
+        printf("depths min = %g, max = %g \n", new_depth_min, new_depth_max);
+        return;
+    }
+	
+    if (new_depth_max <= new_depth_min)
+    {
+        printf("***ERROR*** The Energy_max must be superior to the Energy_min.Energies unchanged.\n");
+        printf("Emin = %g, Emax = %g \n", new_depth_min, new_depth_max);
+        return;
+    }
+
+    if (new_depth_min - d_ptr->proximal_margin < 0)
+    {
+        printf("***ERROR*** The proximal margins are too big: depth - margins < 0.\n");
+        printf("target_depth: %lg mm, proximal margin: %lg mm.\n", new_depth_min, d_ptr->proximal_margin);
+        return;
+    }
+
+    d_ptr->target_min_depth = new_depth_min;
+    d_ptr->target_max_depth = new_depth_max;
+    d_ptr->prescription_depth_min = new_depth_min - d_ptr->proximal_margin;
+    d_ptr->prescription_depth_max = new_depth_max + d_ptr->distal_margin;
+    d_ptr->depth_end = d_ptr->prescription_depth_max + 20;
+    this->update_energies_from_prescription();
+}
+
+void 
+Rt_mebs::set_target_depths(float new_z_min, float new_z_max, float new_step)
+{
+	d_ptr->depth_res = new_step;
+	this->set_target_depths(new_z_min, new_z_max);
+}
+
+void 
+Rt_mebs::set_prescription_depths(float new_prescription_min, float new_prescription_max)
+{
+    if (new_prescription_min <= d_ptr->proximal_margin || new_prescription_max <= 0)
+    {
+        printf("***ERROR*** The prescription min - proximal margins and prescription max must be positive!\n");
+        printf("prescription min = %g, max = %g \n", new_prescription_min, new_prescription_max);
+        printf("proximal margin = %g mm.\n", d_ptr->proximal_margin);
+        return;
+    }
+    if (new_prescription_max <= new_prescription_min)
+    {
+        printf("***ERROR*** The prescription max must be superior to the prescription min.\n");
+        printf("prescription min = %g,  prescription max = %g \n", new_prescription_min, new_prescription_max);
+        return;
+    }
+    if (new_prescription_min + d_ptr->proximal_margin > new_prescription_max - d_ptr->distal_margin)
+    {
+        printf("***WARNING*** prox_margin + distal margin > prescription_max - prescription min.\n");
+        printf("proximal margin: %lg mm, distal margin: %lg mm.\n", d_ptr->proximal_margin, d_ptr->distal_margin);
+        printf("prescription min: %lg mm, prescription max: %lg mm.\n", new_prescription_min, new_prescription_max);
+        return;
+    }
+    d_ptr->prescription_depth_min = new_prescription_min;
+    d_ptr->prescription_depth_max = new_prescription_max;
+    d_ptr->depth_end = d_ptr->prescription_depth_max + 20;
+    this->update_energies_from_prescription();
+}
+
+void 
+Rt_mebs::set_margins(float proximal_margin, float distal_margin)
+{
+    if (proximal_margin < 0 || distal_margin < 0)
+    {
+        printf("***ERROR*** The margins must be positive or null!\n");
+        printf("prescription min = %g, max = %g \n", proximal_margin, distal_margin);
+        return;
+    }
+    d_ptr->proximal_margin = proximal_margin;
+    d_ptr->distal_margin = distal_margin;
+    // the prescription depths are updated later
+}
+
+/* update the mebs depths parameters from energy definition */
+void 
+Rt_mebs::update_prescription_depths_from_energies() 
+{
+    d_ptr->prescription_depth_min = ((10*d_ptr->alpha)*pow((double)d_ptr->beam_min_energy, d_ptr->p));
+    d_ptr->prescription_depth_max = ((10*d_ptr->alpha)*pow((double)d_ptr->beam_max_energy, d_ptr->p));
+
+    d_ptr->target_min_depth = d_ptr->prescription_depth_min + d_ptr->proximal_margin;
+    d_ptr->target_max_depth = d_ptr->prescription_depth_max - d_ptr->distal_margin;
+    if (d_ptr->target_min_depth > d_ptr->target_max_depth)
+    {
+        printf("***WARNING*** target volume impossible. The difference between the E_min and E_max is smaller than the sum of the margins.\n");
+    }
+    d_ptr->depth_end = d_ptr->prescription_depth_max + 20;
+    d_ptr->num_samples = (int)ceil((d_ptr->depth_end/d_ptr->depth_res))+1;
+    d_ptr->energy_number = (int) ceil((d_ptr->beam_max_energy - d_ptr->beam_min_energy) / d_ptr->energy_res) + 1;
+    this->reset_mebs_depth_dose_curve();
+}
+
+/* update the mebs energy parameters from prescription definition */
+void 
+Rt_mebs::update_energies_from_prescription()
+{
+    int energy_min_index = (int) floor(pow((d_ptr->prescription_depth_min/(10*d_ptr->alpha)),(1/d_ptr->p)) / d_ptr->energy_res);
+    int energy_max_index = (int) ceil(pow((d_ptr->prescription_depth_max/(10*d_ptr->alpha)),(1/d_ptr->p)) / d_ptr->energy_res);
+
+    d_ptr->beam_min_energy = (float) energy_min_index * d_ptr->energy_res;
+    d_ptr->beam_max_energy = (float) energy_max_index * d_ptr->energy_res;
+
+    /* check that the E_max is sufficiently high for covering the distal part of the prescription */
+    d_ptr->beam_max_energy += this->check_and_correct_max_energy(d_ptr->beam_max_energy, d_ptr->prescription_depth_max);
+    energy_max_index = (int) (d_ptr->beam_max_energy / d_ptr->energy_res);
+    /* check that the E_min is sufficiently low for covering the distal part of the prescription */
+    d_ptr->beam_min_energy += this->check_and_correct_min_energy(d_ptr->beam_min_energy, d_ptr->prescription_depth_min);
+    energy_min_index = (int) (d_ptr->beam_min_energy / d_ptr->energy_res);
+
+    d_ptr->depth_end = d_ptr->prescription_depth_max + 20;
+    d_ptr->num_samples = (int)ceil((d_ptr->depth_end/d_ptr->depth_res))+1;
+    d_ptr->energy_number = (int) ceil((d_ptr->beam_max_energy - d_ptr->beam_min_energy) / d_ptr->energy_res) + 1;
+    this->reset_mebs_depth_dose_curve();
+}
+
+float* 
+Rt_mebs::get_d_lut()
+{
+    return d_ptr->d_lut;
+}
+
+float* 
+Rt_mebs::get_e_lut()
+{
+    return d_ptr->e_lut;
+}
+
+float* 
+Rt_mebs::get_f_lut()
+{
+    return d_ptr->f_lut;
+}
+
+void 
+Rt_mebs::set_particle_type(Particle_type particle_type)
+{
+    d_ptr->set_particle_type (particle_type);
+    if (d_ptr->prescription_depth_min !=0) {
+        this->update_energies_from_prescription();
+    }
+}
+
+Particle_type
+Rt_mebs::get_particle_type()
+{
+    return d_ptr->particle_type;
+}
+
+void 
+Rt_mebs::set_alpha(double alpha)
+{
+    d_ptr->alpha = alpha;
+}
+
+double 
+Rt_mebs::get_alpha()
+{
+    return d_ptr->alpha;
+}
+
+void 
+Rt_mebs::set_p(double p)
+{
+    d_ptr->p = p;
+}
+
+double 
+Rt_mebs::get_p()
+{
+    return d_ptr->p;
+}
+
+int
+Rt_mebs::get_energy_number()
+{
+    return d_ptr->energy_number;
+}
+
+std::vector<float> 
+Rt_mebs::get_energy()
+{
+    return d_ptr->energies;
+}
+
+std::vector<float> 
+Rt_mebs::get_weight()
+{
+    return d_ptr->depth_dose_weight;
+}
+
+void 
+Rt_mebs::set_energy_resolution(float eres)
+{
+    if (eres > 0)
+    {
+        d_ptr->energy_res = eres;
+        d_ptr->energy_number = (int) ceil((d_ptr->beam_max_energy - d_ptr->beam_min_energy) / d_ptr->energy_res) + 1;
+    }
+    else 
+    {
+        printf("***WARNING*** Energy resolution must be positive. Energy resolution unchanged");
+    }
+}
+
+float 
+Rt_mebs::get_energy_resolution()
+{
+    return d_ptr->energy_res;
+}
+
+void 
+Rt_mebs::set_energy_min(float E_min)
+{
+    if (E_min > 0)
+    {
+        this->set_energies(E_min, d_ptr->beam_max_energy);
+    }
+    else 
+    {
+        printf("***WARNING*** Energy min must be positive. Energy min unchanged");
+    }
+}
+
+float 
+Rt_mebs::get_energy_min()
+{
+    return d_ptr->beam_min_energy;
+}
+
+void 
+Rt_mebs::set_energy_max(float E_max)
+{
+    if (E_max > 0)
+    {
+        this->set_energies(d_ptr->beam_min_energy, E_max);
+    }
+    else
+    {
+        printf("***WARNING*** Energy max must be positive. Energy max unchanged");
+    }
+}
+
+float 
+Rt_mebs::get_energy_max()
+{
+    return d_ptr->beam_max_energy;
+}
+
+int 
+Rt_mebs::get_num_samples()
+{
+    return d_ptr->num_samples;
+}
+
+void 
+Rt_mebs::set_target_min_depth(float dmin)
+{
+    if (dmin > 0)
+    {
+        this->set_target_depths(dmin, d_ptr->target_max_depth);
+    }
+    else 
+    {
+        printf("***WARNING*** Depth min must be positive. Depth min unchanged");
+    }
+}
+
+float 
+Rt_mebs::get_target_min_depth()
+{
+    return d_ptr->target_min_depth;
+}
+
+void 
+Rt_mebs::set_target_max_depth(float dmax)
+{
+    if (dmax > 0)
+    {
+        this->set_target_depths(d_ptr->target_min_depth, dmax);
+    }
+    else 
+    {
+        printf("***WARNING*** Depth max must be positive. Depth max unchanged");
+    }
+}
+
+float 
+Rt_mebs::get_target_max_depth()
+{
+    return d_ptr->target_max_depth;
+}
+
+void 
+Rt_mebs::set_depth_resolution(float dres)
+{
+    if (dres > 0)
+    {
+        d_ptr->depth_res = dres;
+        d_ptr->num_samples = (int)ceil((d_ptr->depth_end/d_ptr->depth_res))+1;
+        this->reset_mebs_depth_dose_curve();
+    }
+    else 
+    {
+        printf("***WARNING*** Depth resolution must be positive. Depth resolution unchanged");
+    }
+}
+
+float
+Rt_mebs::get_depth_resolution()
+{
+    return d_ptr->depth_res;
+}
+
+void 
+Rt_mebs::set_depth_end(float dend)
+{
+    if (dend > 0)
+    {
+        d_ptr->depth_end = dend;
+        d_ptr->num_samples = (int)ceil((d_ptr->depth_end/d_ptr->depth_res))+1;
+        this->reset_mebs_depth_dose_curve();
+    }
+    else 
+    {
+        printf("***WARNING*** Depth end must be positive. Depth end unchanged");
+    }
+}
+
+float
+Rt_mebs::get_depth_end()
+{
+    return d_ptr->depth_end;
+}
+
+float 
+Rt_mebs::get_prescription_min()
+{
+    return d_ptr->prescription_depth_min;
+}
+
+float
+Rt_mebs::get_prescription_max()
+{
+    return d_ptr->prescription_depth_max;
+}
+
+void
+Rt_mebs::set_distal_margin (float distal_margin)
+{
+    this->set_margins(d_ptr->proximal_margin, distal_margin);
+}
+
+float 
+Rt_mebs::get_distal_margin()
+{
+    return d_ptr->distal_margin;
+}
+
+void
+Rt_mebs::set_proximal_margin (float proximal_margin)
+{
+    this->set_margins(proximal_margin, d_ptr->distal_margin);
+}
+
+float 
+Rt_mebs::get_proximal_margin()
+{
+    return d_ptr->proximal_margin;
+}
+
+void 
+Rt_mebs::set_spread (double spread)
+{
+    d_ptr->spread = spread;
+}
+
+double
+Rt_mebs::get_spread()
+{
+    return d_ptr->spread;
+}
+
+void 
+Rt_mebs::set_photon_energy(float energy)
+{
+    d_ptr->photon_energy = energy;
+}
+
+float 
+Rt_mebs::get_photon_energy()
+{
+    return d_ptr->photon_energy;
+}
+
+std::vector<Rt_depth_dose*> 
+Rt_mebs::get_depth_dose()
+{
+    return d_ptr->depth_dose;
+}
+
+std::vector<float>& 
+Rt_mebs::get_num_particles()
+{
+    return d_ptr->num_particles;
+}
+
+void
+Rt_mebs::set_prescription(float prescription_min, float prescription_max)
+{
+    d_ptr->have_prescription = true;
+    this->set_prescription_depths(prescription_min, prescription_max);
+}
+
+void 
+Rt_mebs::set_have_prescription(bool have_prescription)
+{
+    d_ptr->have_prescription = have_prescription;
+}
+
+bool 
+Rt_mebs::get_have_prescription()
+{
+    return d_ptr->have_prescription;
+}
+
+void 
+Rt_mebs::set_have_copied_peaks(bool have_copied_peaks)
+{
+    d_ptr->have_copied_peaks = have_copied_peaks;
+}
+	
+bool 
+Rt_mebs::get_have_copied_peaks()
+{
+    return d_ptr->have_copied_peaks;
+}
+
+void 
+Rt_mebs::set_have_manual_peaks(bool have_manual_peaks)
+{
+    d_ptr->have_manual_peaks = have_manual_peaks;
+}
+
+bool 
+Rt_mebs::get_have_manual_peaks()
+{
+    return d_ptr->have_manual_peaks;
+}
+
+void 
+Rt_mebs::set_have_particle_number_map(bool have_particle_number_map)
+{
+    d_ptr->have_particle_number_map = have_particle_number_map;
+}
+
+bool 
+Rt_mebs::get_have_particle_number_map()
+{
+    return d_ptr->have_particle_number_map;
+}
+
+std::vector<double>&
+Rt_mebs::get_min_wed_map()
+{
+    return d_ptr->min_wed_map;
+}
+
+std::vector<double>&
+Rt_mebs::get_max_wed_map()
+{
+    return d_ptr->max_wed_map;
+}
+
+void 
+Rt_mebs::set_particle_number_in (const std::string& str)
+{
+    d_ptr->particle_number_in = str;
+}
+
+std::string 
+Rt_mebs::get_particle_number_in ()
+{
+    return d_ptr->particle_number_in;
+}
+
+void 
+Rt_mebs::set_particle_number_out (const std::string& str)
+{
+    d_ptr->particle_number_out = str;
+}
+
+std::string 
+Rt_mebs::get_particle_number_out ()
+{
+    return d_ptr->particle_number_out;
+}
+
+void 
+Rt_mebs::add_depth_dose_weight(float weight)
+{
+    d_ptr->depth_dose_weight.push_back(weight);
+}
+
+/* This function check (and correct if necessary) that E_max is the closest energy (+/- energy_resolution) 
+	to reach the distal prescription */
+/* This function is designed to return a float value that represents the increase/decrease of energy to correct it
+	this is used by two parts of the program on different members (explaining this particular structure) */
+float
+Rt_mebs::check_and_correct_max_energy(float E, float depth)
+{
+    float E_init = E;
+    /* Check that the depth dose is increasing (dose_max not passed) */
+    float dose = bragg_curve(E, d_ptr->spread, depth);
+    float dose_plus = bragg_curve(E, d_ptr->spread, depth + d_ptr->depth_res);
+    while ( dose_plus < dose )
+    {
+        E += d_ptr->energy_res;
+        dose = bragg_curve(E, d_ptr->spread, depth);
+        dose_plus = bragg_curve(E, d_ptr->spread, depth+d_ptr->depth_res);
+    }
+
+    /* Check that this energy is really the smallest one that reach the distal prescription */
+    /* This case happen if E is already superior at a first estimation, estimated by alpha and p */
+    if (E < d_ptr->energy_res)
+    {
+        return E - E_init;
+    }
+    E -= d_ptr->energy_res;
+    dose = bragg_curve(E, d_ptr->spread, depth);
+    dose_plus = bragg_curve(E, d_ptr->spread, depth + d_ptr->depth_res);
+    while ( dose_plus > dose)
+    {
+        E -= d_ptr->energy_res;
+        dose = bragg_curve(E, d_ptr->spread, depth);
+        dose_plus = bragg_curve(E, d_ptr->spread, depth + d_ptr->depth_res);
+    }
+    E += d_ptr->energy_res;
+    return E - E_init;
+}
+
+/* This function check (and correct if necessary) that E_min is the closest energy (+/- energy_resolution) 
+	to reach the proximal prescription */
+/* This function is designed to return a float value that represents the increase/decrease of energy to correct it
+	this is used by two parts of the program on different members (explaining this particular structure) */
+float
+Rt_mebs::check_and_correct_min_energy(float E, float depth)
+{
+    float E_init = E;
+    /* Check that the depth dose is descreasing (dose_min passed) */
+    float dose = bragg_curve(E, d_ptr->spread, depth);
+    float dose_minus = bragg_curve(E, d_ptr->spread, depth - d_ptr->depth_res);
+    while ( dose_minus < dose )
+    {
+        if (E < d_ptr->energy_res)
+        {
+            return E_init - E;
+        }
+        E -= d_ptr->energy_res;
+        dose = bragg_curve(E, d_ptr->spread, depth);
+        dose_minus = bragg_curve(E, d_ptr->spread, depth - d_ptr->depth_res);
+    }
+
+    /* Check that this energy is really the smallest one that stops before the proximal prescription */
+    /* This case happens if E is already superior at a first estimation, estimated by alpha and p */
+    E += d_ptr->energy_res;
+    dose = bragg_curve(E, d_ptr->spread, depth);
+    dose_minus = bragg_curve(E, d_ptr->spread, depth - d_ptr->depth_res);
+    while ( dose_minus > dose)
+    {
+        E += d_ptr->energy_res;
+        dose = bragg_curve(E, d_ptr->spread, depth);
+        dose_minus = bragg_curve(E, d_ptr->spread, depth - d_ptr->depth_res);
+    }
+    E -= d_ptr->energy_res;
+    return E - E_init;
+}
+
+void
+Rt_mebs::optimize_sobp ()
+{	
+    this->update_energies_from_prescription();
+    std::vector<float> weight_tmp;
+    std::vector<float> energy_tmp;
+    this->optimizer (&weight_tmp, &energy_tmp);
+
+    for (int i = 0; i < energy_tmp.size(); i++)
+    {
+        add_peak(energy_tmp[i], d_ptr->spread, weight_tmp[i]);
+    }
+}
+
+void 
+Rt_mebs::optimizer (std::vector<float>* weight_tmp, std::vector<float>* energy_tmp)
+{
+    printf("prescription min/max: %lg mm, %lg mm.\n", d_ptr->prescription_depth_min, d_ptr->prescription_depth_max);
+    std::vector<Rt_depth_dose*> depth_dose_tmp;
+    this->initialize_energy_weight_and_depth_dose_vectors(weight_tmp, energy_tmp, &depth_dose_tmp);
+
+    this->get_optimized_peaks(d_ptr->prescription_depth_min, d_ptr->prescription_depth_max, weight_tmp, &depth_dose_tmp);
+}
+
+void
+Rt_mebs::initialize_energy_weight_and_depth_dose_vectors(std::vector<float>* weight_tmp, std::vector<float>* energy_tmp, std::vector<Rt_depth_dose*>* depth_dose_tmp)
+{
+    /* initialize the energies in the table */
+    printf("\n %d Mono-energetic BP used:\n", d_ptr->energy_number);
+    for (int i = 0; i < d_ptr->energy_number; i++)
+    {
+        energy_tmp->push_back(d_ptr->beam_max_energy - (float) i * d_ptr->energy_res);
+        weight_tmp->push_back(0);
+        printf("%lg ", (*energy_tmp)[i]);
+        if ((*energy_tmp)[i] < 0)
+        {
+            d_ptr->energy_number--;
+            (*energy_tmp).pop_back();
+            weight_tmp->pop_back();
+            printf("sobp: peak with energy < 0, Energy resolution error. Last peak deleted.\n");
+        }
+    }
+    printf("\n");
+
+    /* initialize the depth dose curve associated to the energy vector */
+    /* Creates depth dose set */
+    for (int i = 0; i < d_ptr->energy_number; i++)
+    {
+        Rt_depth_dose* depth_dose = new::Rt_depth_dose((*energy_tmp)[i],d_ptr->spread, d_ptr->depth_res,d_ptr->depth_end);
+        depth_dose_tmp->push_back(depth_dose);
+        if (d_ptr->num_samples < depth_dose->num_samples)
+        {
+            d_ptr->num_samples = depth_dose->num_samples;
+        }
+    }
+}
+
+void 
+Rt_mebs::generate_part_num_from_weight(int* ap_dim)
+{
+    //int idx = 0;
+    for (int i = 0; i < d_ptr->energy_number; i++)
+    {
+        for (int j = 0; j < ap_dim[0] * ap_dim[1]; j++)
+        {
+            d_ptr->num_particles.push_back(d_ptr->depth_dose_weight[i]);
+
+        }
+    }
+}
+
+void 
+Rt_mebs::scale_num_part(double A, int* ap_dim)
+{
+    for (int i = 0; i < (int) d_ptr->energy_number * ap_dim[0] * ap_dim[1]; i++)
+    {
+        d_ptr->num_particles[i] = d_ptr->num_particles[i] * A;
+    }
+}
+
+double 
+Rt_mebs::get_particle_number_xyz(int* idx, double* rest, int idx_beam, const int* ap_dim)
+{
+    /* The boundaries possible errors like idx = dim are already excluded by the test on the aperture
+       Practically, idx = dim -1 is not possible */
+    double A = 0;
+    double B = 0;
+    int spot = 0;
+    spot = ap_dim[0] * ap_dim[1] * idx_beam + ap_dim[0] * idx[1] + idx[0];
+    A = d_ptr->num_particles[spot] + rest[0] * ( d_ptr->num_particles[spot+1] -  d_ptr->num_particles[spot]);
+    spot = ap_dim[0] * ap_dim[1] * idx_beam + ap_dim[0] * (idx[1] +1 ) + idx[0];
+    B =  d_ptr->num_particles[spot] + rest[0] * ( d_ptr->num_particles[spot+1] -  d_ptr->num_particles[spot]);
+    return A + rest[1] * (B-A);
+}
+
+void 
+Rt_mebs::extract_particle_number_map_from_txt(Aperture::Pointer& ap)
+{
+    /* Confirm file can be read */
+    if (!file_exists (d_ptr->particle_number_in)) {
+        printf ("Error reading config file: %s\n", d_ptr->particle_number_in.c_str());
+        printf("Particle number map set to 0 for each dose spot \n");
+        return;
+    }
+
+    /* Read file into string */
+    std::ifstream t (d_ptr->particle_number_in.c_str());
+    std::stringstream buffer;
+    std::string strEnergy;
+    std::stringstream ssenergy;
+
+    buffer << t.rdbuf();
+
+    std::string buf;
+
+    std::stringstream ss (buffer.str());
+
+    std::string val;
+    std::string char_part_numb;
+    char sep[] = " ";
+    char* token;
+
+    int spot_number = 0;
+    int line_number = 0;
+    int energy_number = 0;
+    int idx = 0;
+    double part_number = 0;
+    double energy;
+
+    while (getline (ss, buf)) 
+    {
+        buf = string_trim (buf);
+
+        if (buf == "") continue;
+        if (buf[0] == '#') continue;
+
+        /* Check the dim for the last spot map */
+        if (buf.find ("[Energy]") != std::string::npos && energy_number != 0 && line_number != ap->get_dim(1))
+        {
+            printf("***WARNING*** the number of spot line doesn't correspond to the aperture size\n");
+            printf("spot line number expected: %d, spot line detected: %d.\n", ap->get_dim(1), line_number);
+        }
+
+        if (buf.find ("[Energy]") != std::string::npos)
+        {
+            val ="";
+            val = buf.c_str();
+            val = strtok(&val[0], "[Energy]");
+            energy = strtod(val.c_str(),0);
+            spot_number = 0;
+            line_number = 0;
+
+            if (energy > 0)
+            {
+                printf("Particle number map found for energy %lg.\n", energy);
+                this->add_peak(energy, d_ptr->spread, 1);
+                energy_number++;
+                for (int i = 0; i < ap->get_dim(0) * ap->get_dim(1); i++)
+                {
+                    d_ptr->num_particles.push_back(0);
+                }
+            }
+            continue;
+        }
+        if (energy_number == 0)
+        {
+            continue;
+        }
+
+        /* If we arrive here, it means that we read a spot map */
+        val ="";
+        val = buf.c_str();
+        val = string_trim (val);
+					
+        token = strtok(&val[0], sep);
+        spot_number = 0;
+
+        while (token != NULL)
+        {
+            part_number = strtod(token,0);
+            if (spot_number < ap->get_dim(0))
+            {
+                idx = (energy_number-1) * ap->get_dim(0) * ap->get_dim(1) + line_number * ap->get_dim(0) + spot_number;
+                d_ptr->num_particles[idx] = part_number;
+                spot_number++;
+            }
+            token = strtok(NULL, sep);
+        }
+        if (spot_number != ap->get_dim(0))
+        {
+            printf("***WARNING*** the number of spots doesn't correspond to the aperture size\n");
+            printf("line %d: spot number expected: %d, spot number detected: %d.\n", line_number, ap->get_dim(0), spot_number);
+        }
+        line_number++;
+    }
+
+    /* Check the dim for the last spot map */
+    if (energy_number != 0 && line_number != ap->get_dim(1))
+    {
+        printf("***WARNING*** the number of spot line doesn't correspond to the aperture size\n");
+        printf("spot line number expected: %d, spot line detected: %d.\n", ap->get_dim(1), line_number);
+    }
+}
+
+void
+Rt_mebs::compute_particle_number_matrix_from_target_active (Rpl_volume* rpl_vol, Plm_image::Pointer& target, float smearing)
+{
+    int dim[2] = {rpl_vol->get_aperture()->get_dim()[0], rpl_vol->get_aperture()->get_dim()[1]};
+
+    /* vector containing the min and the max of depth of the target */
+    std::vector <double> dmin;
+    std::vector <double> dmax;
+    float min = 0;
+    float max = 0;
+    rpl_vol->compute_beam_modifiers_active_scanning(target->get_vol(), smearing, d_ptr->proximal_margin, d_ptr->distal_margin, dmin, dmax);
+
+    /* Sanity check */
+    if (dmin.size() != rpl_vol->get_aperture()->get_dim(0) * rpl_vol->get_aperture()->get_dim(1) 
+        || dmax.size() != rpl_vol->get_aperture()->get_dim(0) * rpl_vol->get_aperture()->get_dim(1))
+    {
+        printf("ERROR: the aperture size doesn't correspond to the min and max depth maps of the target.\n");
+        printf("Aperture size: %d, min depth map size: %d, max depth map size: %d.\n", rpl_vol->get_aperture()->get_dim(0) * rpl_vol->get_aperture()->get_dim(1), dmin.size(), dmax.size());
+    }
+
+    for (int i = 0; i < dmax.size(); i++)
+    {
+        if (dmax[i] > max)
+        {
+            max = dmax[i];
+        }
+    }
+    min = max;
+    for (int i = 0; i < dmin.size(); i++)
+    {
+        if (dmin[i] < min && dmin[i] != 0)
+        {
+            min = dmin[i];
+        }
+    }
+    this->set_prescription_depths(min, max);
+    printf("Min and max depths in the PTV (target + margins): %lg mm and %lg mm.\n", d_ptr->prescription_depth_min, d_ptr->prescription_depth_max);
+    printf("Min and max energies for treating the PTV: %lg MeV and %lg MeV.\n", d_ptr->beam_min_energy, d_ptr->beam_max_energy);
+
+    std::vector<float> energy_tmp;
+    std::vector<float> weight_tmp;
+    std::vector<Rt_depth_dose*> depth_dose_tmp;
+    this->initialize_energy_weight_and_depth_dose_vectors(&weight_tmp, &energy_tmp, &depth_dose_tmp);
+
+    /* initialization of the dose matrix slice for monoenergetic slice */
+    for (int i = 0; i < dim[0] *  dim[1] * d_ptr->energy_number;i++)
+    {
+        d_ptr->num_particles.push_back(0);
+    }
+
+    printf("Optimization of the particle number map for any mono-energetic slice in progress...\n");
+    /* Let's optimize the SOBP for each beamlet */
+    for (int i = 0; i < dmin.size(); i++)
+    {
+        this->get_optimized_peaks(dmin[i], dmax[i], &weight_tmp, &depth_dose_tmp);
+        for (int j = 0; j < d_ptr->energy_number; j++)
+        {
+            d_ptr->num_particles[i + j *  dim[0] *  dim[1] ] = weight_tmp[j];
+            /* Reset weight_tmp for next turn */
+            weight_tmp[j] = 0;
+        }
+    }
+    for (int i = 0; i < energy_tmp.size(); i++)
+    {
+        add_peak(energy_tmp[i], d_ptr->spread, 1);
+    }
+}
+
+void
+Rt_mebs::compute_particle_number_matrix_from_target_active_slicerRt (Rpl_volume* rpl_vol, Plm_image::Pointer& target, float smearing)
+{
+    int dim[2] = {rpl_vol->get_aperture()->get_dim()[0], rpl_vol->get_aperture()->get_dim()[1]};
+
+    /* vector containing the min and the max of depth of the target */
+    std::vector <double> dmin;
+    std::vector <double> dmax;
+    float min = 0;
+    float max = 0;
+    rpl_vol->compute_beam_modifiers_core_slicerRt(target, true, smearing, d_ptr->proximal_margin, d_ptr->distal_margin, dmin, dmax);
+
+    /* Sanity check */
+    if (dmin.size() != rpl_vol->get_aperture()->get_dim(0) * rpl_vol->get_aperture()->get_dim(1) 
+        || dmax.size() != rpl_vol->get_aperture()->get_dim(0) * rpl_vol->get_aperture()->get_dim(1))
+    {
+        printf("ERROR: the aperture size doesn't correspond to the min and max depth maps of the target.\n");
+        printf("Aperture size: %d, min depth map size: %d, max depth map size: %d.\n", rpl_vol->get_aperture()->get_dim(0) * rpl_vol->get_aperture()->get_dim(1), dmin.size(), dmax.size());
+    }
+
+    for (int i = 0; i < dmax.size(); i++)
+    {
+        if (dmax[i] > max)
+        {
+            max = dmax[i];
+        }
+    }
+    min = max;
+    for (int i = 0; i < dmin.size(); i++)
+    {
+        if (dmin[i] < min && dmin[i] != 0)
+        {
+            min = dmin[i];
+        }
+    }
+    this->set_prescription_depths(min, max);
+    printf("Min and max depths in the PTV (target + margins): %lg mm and %lg mm.\n", d_ptr->prescription_depth_min, d_ptr->prescription_depth_max);
+    printf("Min and max energies for treating the PTV: %lg MeV and %lg MeV.\n", d_ptr->beam_min_energy, d_ptr->beam_max_energy);
+
+    std::vector<float> energy_tmp;
+    std::vector<float> weight_tmp;
+    std::vector<Rt_depth_dose*> depth_dose_tmp;
+    this->initialize_energy_weight_and_depth_dose_vectors(&weight_tmp, &energy_tmp, &depth_dose_tmp);
+
+    /* initialization of the dose matrix slice for monoenergetic slice */
+    for (int i = 0; i < dim[0] *  dim[1] * d_ptr->energy_number;i++)
+    {
+        d_ptr->num_particles.push_back(0);
+    }
+
+    printf("Optimization of the particle number map for any mono-energetic slice in progress...\n");
+    /* Let's optimize the SOBP for each beamlet */
+    for (int i = 0; i < dmin.size(); i++)
+    {
+        this->get_optimized_peaks(dmin[i], dmax[i], &weight_tmp, &depth_dose_tmp);
+        for (int j = 0; j < d_ptr->energy_number; j++)
+        {
+            d_ptr->num_particles[i + j *  dim[0] *  dim[1] ] = weight_tmp[j];
+            /* Reset weight_tmp for next turn */
+            weight_tmp[j] = 0;
+        }
+    }
+    for (int i = 0; i < energy_tmp.size(); i++)
+    {
+        add_peak(energy_tmp[i], d_ptr->spread, 1);
+    }
+}
+
+/* This function returns optimized weighted peaks for passive systems (SOBP weights)
+	and active systems (beamlet particle numbers for each energy) */
+void 
+Rt_mebs::get_optimized_peaks(float dmin, float dmax, std::vector<float>* weight_tmp, std::vector<Rt_depth_dose*>* depth_dose_tmp)
+{
+    if (dmin == 0 || dmax == 0)
+    {
+        return;
+    }
+    int energy_min_index = (int) floor(pow((dmin/(10*d_ptr->alpha)),(1/d_ptr->p)) / d_ptr->energy_res);
+    int energy_max_index = (int) ceil(pow((dmax/(10*d_ptr->alpha)),(1/d_ptr->p)) / d_ptr->energy_res);
+
+    float E_min_sobp = (float) energy_min_index * d_ptr->energy_res;
+    float E_max_sobp = (float) energy_max_index * d_ptr->energy_res;
+
+    /* This is useful only for active scanning */
+    /* check that the E_max is sufficiently high for covering the distal part of the prescription */
+    E_max_sobp += this->check_and_correct_max_energy(E_max_sobp, dmax);
+
+    /* check that the E_min is sufficiently low for covering the distal part of the prescription */
+    E_min_sobp += this->check_and_correct_min_energy(E_min_sobp, dmin);
+
+    int i0 = (int) ((d_ptr->beam_max_energy - E_max_sobp) / d_ptr->energy_res);
+    int imax = (int) ((d_ptr->beam_max_energy - E_min_sobp) / d_ptr->energy_res);
+
+    std::vector<float> d_lut_tmp (d_ptr->num_samples, 0);
+    std::vector<float> e_lut_tmp (d_ptr->num_samples, 0);
+
+    for (int i = 0; i < d_ptr->num_samples; i++)
+    {
+        d_lut_tmp[i] = (float) i * d_ptr->depth_res;
+    }
+
+    int idx_max = 0;
+
+    for (int i = i0; i <= imax; i++)
+    {
+        idx_max = (*depth_dose_tmp)[i]->index_of_dose_max;
+
+        if (idx_max > d_ptr->num_samples)
+        {
+            printf("***WARNING*** depth_dose %d, max_index > samples.\n", i);
+            continue; // the weight remains at 0
+        }
+        if ( (*depth_dose_tmp)[i]->e_lut[idx_max] <= 0)
+        {
+            printf("***WARNING*** depth dose #%d is null.\n", i);
+            continue; // the weight remains at 0
+        }
+        (*weight_tmp)[i] = (1-e_lut_tmp[idx_max])/ (*depth_dose_tmp)[i]->e_lut[idx_max];
+
+        if ((*weight_tmp)[i] < 0)
+        {
+            (*weight_tmp)[i] = 0;
+        }
+
+        for (int j = 0; j < (*depth_dose_tmp)[i]->num_samples; j++)
+        {
+            e_lut_tmp[j] += (*weight_tmp)[i] *  (*depth_dose_tmp)[i]->e_lut[j];
+        }
+    }
+    
+    for (int k = 0; k < 40; k++)
+    {
+        for (int i = i0; i <= imax; i++)
+        {
+            if (e_lut_tmp[ (*depth_dose_tmp)[i]->index_of_dose_max] != 0)
+            {
+                (*weight_tmp)[i] /= e_lut_tmp[ (*depth_dose_tmp)[i]->index_of_dose_max];
+            }
+        }
+        for (int j = 0; j < d_ptr->num_samples; j++)
+        {
+            e_lut_tmp[j] = 0;
+        }
+		
+        for (int i = i0; i <= imax; i++)
+        {
+            for (int j = 0; j <  (*depth_dose_tmp)[i]->num_samples; j++)
+            {
+                e_lut_tmp[j] += (*weight_tmp)[i] *  (*depth_dose_tmp)[i]->e_lut[j];
+            }
+        }
+    }
+
+    double mean_sobp = 0;
+    double mean_count = 0;
+    for (int i = 0; i < d_ptr->num_samples; i++)
+    {
+        if (d_lut_tmp[i] >= dmin && d_lut_tmp[i] <= dmax)
+        {
+            mean_sobp += e_lut_tmp[i];
+            mean_count++;
+        }
+    }
+
+    if (mean_count == 0 || mean_sobp == 0)
+    {
+        printf("***WARNING*** The dose is null in the target interval\n");
+        return;
+    }
+
+    for(int i = i0; i <= imax; i++)
+    {
+        (*weight_tmp)[i] /= (float) (mean_sobp / mean_count);
+    }
+}
+
+void 
+Rt_mebs::export_spot_map_as_txt(Aperture::Pointer ap)
+{
+    make_parent_directories (d_ptr->particle_number_out.c_str());
+	
+    printf("Trying to write spot maps in %s\n", d_ptr->particle_number_out.c_str());
+
+    std::ofstream fichier(d_ptr->particle_number_out.c_str());
+
+    if ( !fichier ){
+        std::cerr << "Erreur de creation du fichier spot_map" << std::endl;
+        return;
+    }
+
+    int idx = 0;
+    for (int e = 0; e < d_ptr->energy_number; e++)
+    {
+        fichier << "[ENERGY] ";
+        fichier << d_ptr->energies[e] << std::endl;
+        for (int i = 0; i < ap->get_dim(0); i++)
+        {
+            for (int j = 0; j < ap->get_dim(1); j++)
+            {
+                idx = (e * ap->get_dim(0) + i) * ap->get_dim(1) +j;
+                fichier << d_ptr->num_particles[idx] << " ";
+            }
+            fichier << std::endl;
+        }
+        fichier << std::endl;
+    }
+    fichier.close();
+}
+
diff --git a/src/plastimatch/dose/rt_mebs.h b/src/plastimatch/dose/rt_mebs.h
new file mode 100644
index 0000000..ce3c183
--- /dev/null
+++ b/src/plastimatch/dose/rt_mebs.h
@@ -0,0 +1,152 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+/* -----------------------------------------------------------------------
+   rt_mebs (for mono-energetic_beam_set) is a class that creates beams of different energies,
+   including SOBP (Spread Out Bragg Peak) or any multi-energy beam configuration. 
+   ----------------------------------------------------------------------- */
+#ifndef _rt_mebs_h_
+#define _rt_mebs_h_
+
+#include "plmdose_config.h"
+#include <stdio.h>
+#include <vector>
+#include <aperture.h>
+#include "logfile.h"
+#include "particle_type.h"
+#include "plmdose_config.h"
+#include "plm_config.h"
+#include "rpl_volume.h"
+#include "rt_lut.h"
+#include "smart_pointer.h"
+
+class Rt_depth_dose;
+class Rt_mebs_private;
+
+class PLMDOSE_API Rt_mebs {
+public:
+    SMART_POINTER_SUPPORT (Rt_mebs);
+    Rt_mebs_private *d_ptr;
+public:
+    Rt_mebs ();
+    Rt_mebs (Particle_type part);
+    Rt_mebs (const Rt_mebs::Pointer& rt_mebs);
+    ~Rt_mebs ();
+
+    /* Remove all depth dose */
+    void clear_depth_dose ();
+
+    /* Add a pristine peak to a mebs */
+    void add_depth_dose (Rt_depth_dose* depth_dose);
+    void add_peak (double E0, double spread, double weight);
+
+    /* Return simple depth dose result at depth */
+    float lookup_energy (float depth);
+
+    /* use manually weighted peaks */
+    bool generate ();
+
+    /* Save the depth dose to a file */
+    void dump (const char* dir);
+
+    /* Print the parameters of the mebs */
+    void printparameters();
+
+    /* reset the mebs depth dose curve */
+    void reset_mebs_depth_dose_curve();
+
+    /* set the prescription parameters: target and prescription depths, energy */
+    void set_energies(float new_E_min, float new_E_max);
+    void set_energies(float new_E_min, float new_E_max, float new_step);
+    void set_target_depths(float new_depth_min, float new_depth_max);
+    void set_target_depths(float new_depth_min, float new_depth_max, float new_step);
+    void set_prescription_depths(float new_prescription_min, float new_prescription_max);
+    void set_margins(float proximal_margin, float distal_margin);
+    void update_prescription_depths_from_energies();
+    void update_energies_from_prescription();
+	
+    /* Set/Get private members */
+    float* get_d_lut();
+    float* get_e_lut();
+    float* get_f_lut();
+    void set_particle_type(Particle_type particle_type);
+    Particle_type get_particle_type();
+    void set_alpha(double alpha);
+    double get_alpha();
+    void set_p(double p);
+    double get_p();
+    int get_energy_number(); /* set energy_number is not implemented as it must not be changed externally*/
+    std::vector<float> get_energy();
+    std::vector<float> get_weight();
+    void set_energy_resolution(float eres);
+    float get_energy_resolution();
+    void set_energy_min(float E_min);
+    float get_energy_min();
+    void set_energy_max(float E_max);
+    float get_energy_max();
+    int get_num_samples();
+    void set_target_min_depth(float dmin);
+    float get_target_min_depth();
+    void set_target_max_depth(float dmax);
+    float get_target_max_depth();
+    void set_depth_resolution(float dres);
+    float get_depth_resolution();
+    void set_depth_end(float dend);
+    float get_depth_end();
+    float get_prescription_min();
+    float get_prescription_max();
+    void set_proximal_margin (float proximal_margin);
+    float get_proximal_margin();
+    void set_distal_margin (float distal_margin);
+    float get_distal_margin();
+    void set_spread (double spread);
+    double get_spread();
+    void set_photon_energy(float energy);
+    float get_photon_energy();
+    std::vector<Rt_depth_dose*> get_depth_dose();
+    std::vector<float>& get_num_particles();
+    void set_prescription(float prescription_min, float prescription_max);
+    void set_have_prescription(bool have_prescription);
+    bool get_have_prescription();
+    void set_have_copied_peaks(bool have_copied_peaks);
+    bool get_have_copied_peaks();
+    void set_have_manual_peaks(bool have_manual_peaks);
+    bool get_have_manual_peaks();
+    void set_have_particle_number_map(bool have_particle_number_map);
+    bool get_have_particle_number_map();
+    std::vector<double>& get_min_wed_map();
+    std::vector<double>& get_max_wed_map();
+
+    void set_particle_number_in (const std::string& str);
+    std::string get_particle_number_in ();
+
+    void set_particle_number_out (const std::string& str);
+    std::string get_particle_number_out ();
+
+    void add_depth_dose_weight(float weight);
+
+    float check_and_correct_max_energy(float E, float depth);
+    float check_and_correct_min_energy(float E, float depth);
+
+    /* Optimize, then generate mebs depth curve from prescription 
+       range and modulation */
+    void optimize_sobp ();
+    /* Weight optimizer */
+    void optimizer (std::vector<float>* weight_tmp, std::vector<float>* energy_tmp);
+
+    void get_optimized_peaks(float dmin, float dmax, std::vector<float>* weight_tmp, std::vector<Rt_depth_dose*>* depth_dose);
+    void initialize_energy_weight_and_depth_dose_vectors(std::vector<float>* weight_tmp, std::vector<float>* energy_tmp, std::vector<Rt_depth_dose*>* depth_dose_tmp);
+
+    void scale_num_part(double A, int* ap_dim);
+    double get_particle_number_xyz(int* idx, double* rest, int idx_beam, const int* ap_dim);
+
+    /* This computes the E_min and E_max map from a target for all pencil beam*/
+    void generate_part_num_from_weight(int* ap_dim);
+    void extract_particle_number_map_from_txt(Aperture::Pointer& ap);
+    void compute_particle_number_matrix_from_target_active(Rpl_volume* rpl_vol, Plm_image::Pointer& target, float smearing);
+    void compute_particle_number_matrix_from_target_active_slicerRt (Rpl_volume* rpl_vol, Plm_image::Pointer& target, float smearing);
+
+    void export_spot_map_as_txt(Aperture::Pointer ap);
+};
+
+#endif
diff --git a/src/plastimatch/dose/rt_parms.cxx b/src/plastimatch/dose/rt_parms.cxx
index 431784d..79850b1 100644
--- a/src/plastimatch/dose/rt_parms.cxx
+++ b/src/plastimatch/dose/rt_parms.cxx
@@ -19,7 +19,7 @@
 #include "rt_depth_dose.h"
 #include "rt_parms.h"
 #include "rt_plan.h"
-#include "rt_sobp.h"
+#include "rt_mebs.h"
 #include "string_util.h"
 
 class Rt_parms_private {
@@ -36,7 +36,7 @@ public:
     /* Other parameters not directly defined by config the config file but necessary for the beam creation */
     Rt_plan* rt_plan;
     int beam_number; /* contains the number of the beam in the vector<Rt_beam*> beam_storage */
-    Rt_sobp::Pointer sobp;
+    Rt_mebs::Pointer mebs;
     bool have_prescription;
     bool ap_have_origin;
     bool have_manual_peaks;
@@ -56,7 +56,7 @@ public:
            file but necessary for the beam creation */
         this->rt_plan = 0;
         this->beam_number = -1;
-        this->sobp = Rt_sobp::New();
+        this->mebs = Rt_mebs::New();
         this->have_prescription = false;
         this->ap_have_origin = false;
         this->have_manual_peaks = false;
@@ -160,80 +160,10 @@ Rt_parms::append_peak ()
     if (!rt_beam) {
         return;
     }
-	rt_beam->set_have_manual_peaks(true);
-    rt_beam->add_peak (
-        d_ptr->E0, d_ptr->spread, d_ptr->depth_res, 
-        d_ptr->max_depth, d_ptr->weight);
+    rt_beam->get_mebs()->set_have_manual_peaks(true);
+    rt_beam->get_mebs()->add_peak (d_ptr->E0, d_ptr->spread, d_ptr->weight);
 }
 
-#if defined (commentout)
-void 
-save_beam_parameters(int i, int section)
-{
-    /* SETTINGS */
-    if (section == 2)
-    {
-        d_ptr->rt_plan->beam_storage[i]->set_aperture_out(d_ptr->output_aperture_fn);			
-        d_ptr->rt_plan->beam_storage[i]->set_proj_dose_out(d_ptr->output_proj_dose_fn);
-        d_ptr->rt_plan->beam_storage[i]->set_proj_img_out(d_ptr->output_proj_img_fn);
-        d_ptr->rt_plan->beam_storage[i]->set_range_compensator_out(d_ptr->output_range_compensator_fn);
-        d_ptr->rt_plan->beam_storage[i]->set_sigma_out(d_ptr->output_sigma_fn.c_str());
-        d_ptr->rt_plan->beam_storage[i]->set_wed_out(d_ptr->output_wed_fn.c_str());
-        d_ptr->rt_plan->beam_storage[i]->set_particle_type(d_ptr->part);
-        d_ptr->rt_plan->beam_storage[i]->set_detail(d_ptr->detail);
-        d_ptr->rt_plan->beam_storage[i]->set_beamWeight(d_ptr->beam_weight);
-        d_ptr->rt_plan->beam_storage[i]->set_source_position(d_ptr->src);
-        d_ptr->rt_plan->beam_storage[i]->set_isocenter_position(d_ptr->isocenter);
-        d_ptr->rt_plan->beam_storage[i]->set_sobp_prescription_min_max(d_ptr->prescription_min, d_ptr->prescription_max);
-        d_ptr->rt_plan->beam_storage[i]->get_aperture()->set_distance(d_ptr->ap_offset);
-        if (d_ptr->ap_have_origin) 
-        {
-            d_ptr->rt_plan->beam_storage[i]->get_aperture()->set_origin(d_ptr->ap_origin);
-        }
-        d_ptr->rt_plan->beam_storage[i]->get_aperture()->set_dim(d_ptr->ires);
-        d_ptr->rt_plan->beam_storage[i]->get_aperture()->set_spacing(d_ptr->ap_spacing);
-        d_ptr->rt_plan->beam_storage[i]->set_source_size(d_ptr->source_size);
-
-#if defined (commentout)
-        /* GCS TODO: This logic belongs elsewhere */
-        if (d_ptr->target_fn == "") 
-        {
-            if (d_ptr->ap_filename != "") 
-            {
-                d_ptr->rt_plan->beam_storage[i]->set_aperture_in(d_ptr->ap_filename.c_str());
-            }
-            if (d_ptr->rc_filename != "") 
-            {
-                d_ptr->rt_plan->beam_storage[i]->set_range_compensator_in (d_ptr->rc_filename.c_str());
-            }
-        }
-#endif
-        d_ptr->rt_plan->beam_storage[i]->set_smearing(d_ptr->smearing);
-        d_ptr->rt_plan->beam_storage[i]->set_proximal_margin(d_ptr->proximal_margin);
-        d_ptr->rt_plan->beam_storage[i]->set_distal_margin(d_ptr->distal_margin);
-        d_ptr->rt_plan->beam_storage[i]->set_have_prescription(d_ptr->have_prescription);
-        d_ptr->rt_plan->beam_storage[i]->set_photon_energy(d_ptr->photon_energy);
-    }
-
-    /* PEAKS */
-    else if (section == 3)
-    {
-        if (d_ptr->bragg_curve =="")
-        {
-            d_ptr->rt_plan->beam_storage[i]->set_have_manual_peaks(
-                d_ptr->have_manual_peaks);
-            d_ptr->rt_plan->beam_storage[i]->add_peak(
-                d_ptr->E0, d_ptr->spread, d_ptr->depth_res, 
-                d_ptr->max_depth, d_ptr->weight);
-        }
-        else
-        {
-            printf("ERROR: bragg curve already defined by bragg_curve file - impossible to optimize a SOBP from peaks");
-        }
-    }
-}
-#endif
-
 Plm_return_code
 Rt_parms::set_key_value (
     const std::string& section,
@@ -289,7 +219,28 @@ Rt_parms::set_key_value (
             if (sscanf (val.c_str(), "%f", &norm_dose) != 1) {
                 goto error_exit;
             }
+			if (norm_dose <= 0) {
+				goto error_exit;
+			}
             d_ptr->rt_plan->set_normalization_dose (norm_dose);
+			d_ptr->rt_plan->set_have_dose_norm(true);
+        }
+		else if (key == "ref_dose_point") {
+            float rdp[3];
+            int rc = sscanf (val.c_str(), "%f %f %f", 
+                &rdp[0], &rdp[1], &rdp[2]);
+            if (rc != 3) {
+                goto error_exit;
+            }
+            d_ptr->rt_plan->set_ref_dose_point (rdp);
+			d_ptr->rt_plan->set_have_ref_dose_point(true);
+        }
+		else if (key == "non_normalized_dose") {
+            if (val.length() >= 1) {
+                d_ptr->rt_plan->set_non_norm_dose (val[0]);
+            } else {
+                goto error_exit;
+            } 
         }
         else {
             goto error_exit;
@@ -308,6 +259,9 @@ Rt_parms::set_key_value (
                 goto error_exit;
             } 
         }
+		else if (key == "beam_line") {
+            rt_beam->set_beam_line_type (val);
+        }
         else if (key == "homo_approx") {
             if (val.length() >= 1) {
                 rt_beam->set_homo_approx (val[0]);
@@ -322,8 +276,6 @@ Rt_parms::set_key_value (
             }
             rt_beam->set_step_length (step_length);
         }
-
-
         else if (key == "aperture_out") {
             rt_beam->set_aperture_out (val);
         }
@@ -336,6 +288,9 @@ Rt_parms::set_key_value (
         else if (key == "rc_out") {
             rt_beam->set_range_compensator_out (val);
         }
+		else if (key == "particle_number_out") {
+            rt_beam->get_mebs()->set_particle_number_out (val);
+        }
         else if (key == "sigma_out") {
             rt_beam->set_sigma_out (val);
         }
@@ -347,18 +302,7 @@ Rt_parms::set_key_value (
             if (part == PARTICLE_TYPE_UNKNOWN) {
                 goto error_exit;
             }
-            rt_beam->set_particle_type (part);
-        }
-        else if (key == "detail") {
-            if (val == "low") {
-                rt_beam->set_detail (1);
-            }
-            else if (val == "high") {
-                rt_beam->set_detail (0);
-            }
-            else {
-                goto error_exit;
-            }
+            rt_beam->get_mebs()->set_particle_type (part);
         }
         else if (key == "beam_weight") {
             float beam_weight;
@@ -371,11 +315,13 @@ Rt_parms::set_key_value (
             if (sscanf (val.c_str(), "%lf", &(d_ptr->max_depth)) != 1) {
                 goto error_exit;
             }
+			rt_beam->get_mebs()->set_depth_end(d_ptr->max_depth);
         }
         else if (key == "depth_dose_z_res") {
             if (sscanf (val.c_str(), "%lf", &(d_ptr->depth_res)) != 1) {
                 goto error_exit;
             }
+			rt_beam->get_mebs()->set_depth_resolution(d_ptr->max_depth);
         }
         else if (key == "source") {
             float src[3];
@@ -395,21 +341,14 @@ Rt_parms::set_key_value (
             }
             rt_beam->set_isocenter_position (isocenter);
         }
-        else if (key == "prescription_min") {
+        else if (key == "prescription_min_max") {
             float prescription_min;
-            int rc = sscanf (val.c_str(), "%f", &prescription_min);
-            if (rc != 1) {
-                goto error_exit;
-            }
-            rt_beam->set_prescription_min (prescription_min);
-        }
-        else if (key == "prescription_max") {
-            float prescription_max;
-            int rc = sscanf (val.c_str(), "%f", &prescription_max);
-            if (rc != 1) {
+			float prescription_max;
+            int rc = sscanf (val.c_str(), "%f %f", &prescription_min, &prescription_max);
+            if (rc != 2) {
                 goto error_exit;
             }
-            rt_beam->set_prescription_max (prescription_max);
+            rt_beam->get_mebs()->set_prescription (prescription_min, prescription_max);
         }
         else if (key == "aperture_up") {
             float vup[3];
@@ -453,6 +392,13 @@ Rt_parms::set_key_value (
             }
             rt_beam->set_aperture_spacing (ap_spacing);
         }
+		else if (key == "range_comp_mc_model") {
+            if (val.length() >= 1) {
+                rt_beam->set_rc_MC_model (val[0]);
+            } else {
+                goto error_exit;
+            } 
+        }
         else if (key == "source_size") {
             float source_size;
             if (sscanf (val.c_str(), "%f", &source_size) != 1) {
@@ -466,6 +412,10 @@ Rt_parms::set_key_value (
         else if (key == "range_compensator_file_in") {
             rt_beam->set_range_compensator_in (val);
         }
+		else if (key == "particle_number_in") {
+            rt_beam->get_mebs()->set_particle_number_in (val);
+			rt_beam->get_mebs()->set_have_particle_number_map(true);
+        }
         else if (key == "aperture_smearing") {
             float smearing;
             if (sscanf (val.c_str(), "%f", &smearing) != 1) {
@@ -478,21 +428,28 @@ Rt_parms::set_key_value (
             if (sscanf (val.c_str(), "%f", &proximal_margin) != 1) {
                 goto error_exit;
             }
-            rt_beam->set_proximal_margin (proximal_margin);
+            rt_beam->get_mebs()->set_proximal_margin (proximal_margin);
         }
         else if (key == "distal_margin") {
             float distal_margin;
             if (sscanf (val.c_str(), "%f", &distal_margin) != 1) {
                 goto error_exit;
             }
-            rt_beam->set_distal_margin (distal_margin);
+            rt_beam->get_mebs()->set_distal_margin (distal_margin);
+        }
+		else if (key == "energy_resolution") {
+            float eres;
+            if (sscanf (val.c_str(), "%f", &eres) != 1) {
+                goto error_exit;
+            }
+            rt_beam->get_mebs()->set_energy_resolution(eres);
         }
         else if (key == "energy_x") {
             float photon_energy;
             if (sscanf (val.c_str(), "%f", &photon_energy) != 1) {
                 goto error_exit;
             }
-            rt_beam->set_photon_energy (photon_energy);
+            rt_beam->get_mebs()->set_photon_energy (photon_energy);
         }
         else {
             goto error_exit;
@@ -530,60 +487,12 @@ Rt_parms::set_key_value (
     print_and_exit ("Unknown section value: %s\n", section.c_str());
     return PLM_ERROR;
 
-error_exit:
+	error_exit:
     print_and_exit ("Unknown (key,val) combination: (%s,%s)\n", 
         key.c_str(), val.c_str());
     return PLM_ERROR;
 }
 
-#if defined (commentout_TODO)
-void
-Rt_parms::handle_end_of_section (int section)
-{
-    switch (section) {
-    case 0:
-        /* Reading before [PLAN] */
-        break;
-    case 1:
-        /* [PLAN] */
-        d_ptr->rt_plan->set_debug(d_ptr->debug);
-        /* The other parameters are set at the beginning of the dose calculation */
-        break;
-
-    case 2:
-        /* [BEAM] */
-        Rt_beam* new_beam;
-        new_beam = new Rt_beam;
-
-        d_ptr->beam_number++; /* initialized to -1, at first run on the beam, beam_number = 0 */
-        d_ptr->rt_plan->beam_storage.push_back(new_beam);
-
-        d_ptr->sobp = Rt_sobp::New();
-        /* We save the beam data, only the changes will be updated in the other sections */
-        this->save_beam_parameters(d_ptr->beam_number, 2);
-
-        d_ptr->have_manual_peaks = false;
-        d_ptr->ap_have_origin = false;
-        d_ptr->have_prescription = false;
-
-        d_ptr->output_aperture_fn= "";
-        d_ptr->output_proj_dose_fn = "";
-        d_ptr->output_proj_img_fn = "";
-        d_ptr->output_range_compensator_fn = "";
-        d_ptr->output_sigma_fn = "";
-        d_ptr->output_wed_fn = "";
-        d_ptr->ap_filename= "";
-        d_ptr->rc_filename = "";
-        break;
-    case 3:
-        /* [PEAK] */
-        d_ptr->have_manual_peaks = true;
-        this->save_beam_parameters(d_ptr->beam_number, section);
-        break;
-    }
-}
-#endif
-
 Plm_return_code
 Rt_parms::parse_args (int argc, char** argv)
 {
@@ -603,7 +512,6 @@ Rt_parms::parse_args (int argc, char** argv)
     if (!argv[i]) {
         print_usage ();
     }
-
     Rt_parms_parser rpp (this);
     return rpp.parse_config_file (argv[i]);
 }
diff --git a/src/plastimatch/dose/rt_parms.h b/src/plastimatch/dose/rt_parms.h
index c4d1e9e..f5f6637 100644
--- a/src/plastimatch/dose/rt_parms.h
+++ b/src/plastimatch/dose/rt_parms.h
@@ -37,7 +37,6 @@ public:
     void append_peak ();
 
 protected:
-    void handle_end_of_section (int section);
     void parse_config (const char* config_fn);
 };
 
diff --git a/src/plastimatch/dose/rt_plan.cxx b/src/plastimatch/dose/rt_plan.cxx
index 9cfede6..1511ed4 100644
--- a/src/plastimatch/dose/rt_plan.cxx
+++ b/src/plastimatch/dose/rt_plan.cxx
@@ -9,6 +9,7 @@
 #include "aperture.h"
 #include "dose_volume_functions.h"
 #include "plm_image.h"
+#include "plm_exception.h"
 #include "plm_timer.h"
 #include "print_and_exit.h"
 #include "proj_matrix.h"
@@ -22,7 +23,7 @@
 #include "rt_parms.h"
 #include "rt_plan.h"
 #include "rt_sigma.h"
-#include "rt_sobp.h"
+#include "rt_mebs.h"
 #include "rt_study.h"
 #include "volume.h"
 #include "volume_macros.h"
@@ -33,9 +34,12 @@ class Rt_plan_private {
 
 public:
     bool debug;
-
+    float rdp[3];
+    bool have_rdp;
+    bool have_dose_norm;
     float normalization_dose; // dose prescription
-	float depth_dose_max;
+    char non_norm_dose;
+    float depth_dose_max;
 
     /* Filenames for input and output images */
     std::string patient_fn;
@@ -51,17 +55,25 @@ public:
     Rt_parms::Pointer rt_parms;
     Rt_study* rt_study;
 
+    /* Storage of beams */
+    std::vector<Rt_beam*> beam_storage;
+
 public: 
     Rt_plan_private ()
     {
         debug = false;
-        normalization_dose = 1.f;
-		depth_dose_max = 1.f;
+        this->rdp[0] = 0.f;
+        this->rdp[1] = 0.f;
+        this->rdp[2] = 0.f;
+        this->have_rdp = false;
+        this->have_dose_norm = false;
+        this->normalization_dose = 100.f;
+        this->non_norm_dose = 'n';
+        this->depth_dose_max = 1.f;
         
         patient = Plm_image::New();
         target = Plm_image::New();
         dose = Plm_image::New();
-
         rt_parms = Rt_parms::New ();
     }
 
@@ -73,7 +85,6 @@ public:
 Rt_plan::Rt_plan ()
 {
     this->d_ptr = new Rt_plan_private;
-    this->beam = 0;
 }
 
 Rt_plan::~Rt_plan ()
@@ -88,113 +99,6 @@ Rt_plan::parse_args (int argc, char* argv[])
     return d_ptr->rt_parms->parse_args (argc, argv);
 }
 
-bool
-Rt_plan::init ()
-{
-    if (!this->beam) return false;
-    if (!this->get_patient()) return false;
-
-    this->beam->aperture_vol = new Rpl_volume;
-
-    if (!this->beam->rpl_vol) {this->beam->rpl_vol = new Rpl_volume;}
-    this->beam->rpl_vol->set_geometry (
-        this->beam->get_source_position(),
-        this->beam->get_isocenter_position(),
-        this->beam->get_aperture()->vup,
-        this->beam->get_aperture()->get_distance(),
-        this->beam->get_aperture()->get_dim(),
-        this->beam->get_aperture()->get_center(),
-        this->beam->get_aperture()->get_spacing(),
-        this->beam->get_step_length());
-        
-    if (!this->beam->rpl_vol) return false;
-
-	/* building the ct_density_vol */
-    this->beam->rpl_ct_vol_HU = new Rpl_volume;
-    this->beam->rpl_ct_vol_HU->set_geometry (
-            this->beam->get_source_position(),
-            this->beam->get_isocenter_position(),
-            this->beam->get_aperture()->vup,
-            this->beam->get_aperture()->get_distance(),
-            this->beam->get_aperture()->get_dim(),
-            this->beam->get_aperture()->get_center(),
-            this->beam->get_aperture()->get_spacing(),
-            this->beam->get_step_length());
-    if (!this->beam->rpl_ct_vol_HU) return false;
-
-    if (this->beam->get_flavor() == 'f'|| this->beam->get_flavor() == 'g' || this->beam->get_flavor() == 'h')
-    {
-        /* building the sigma_vol */
-        this->beam->sigma_vol = new Rpl_volume;
-        this->beam->sigma_vol->set_geometry (
-            this->beam->get_source_position(),
-            this->beam->get_isocenter_position(),
-            this->beam->get_aperture()->vup,
-            this->beam->get_aperture()->get_distance(),
-            this->beam->get_aperture()->get_dim(),
-            this->beam->get_aperture()->get_center(),
-            this->beam->get_aperture()->get_spacing(),
-            this->beam->get_step_length());
-        
-        if (!this->beam->sigma_vol) return false;
-    }
-
-    /* Copy aperture from scene into rpl volume */
-    this->beam->rpl_vol->set_aperture (this->beam->get_aperture());
-	this->beam->rpl_ct_vol_HU->set_aperture (this->beam->get_aperture());
-	
-    if (this->beam->get_flavor() == 'f' || this->beam->get_flavor() == 'g' || this->beam->get_flavor() == 'h')
-    {
-        this->beam->sigma_vol->set_aperture (this->beam->get_aperture());
-    }
-
-    /* Scan through aperture to fill in rpl_volume */
-    this->beam->rpl_vol->set_ct_volume (d_ptr->patient);
-
-    if(this->beam->rpl_vol->get_ct() && this->beam->rpl_vol->get_ct_limit())
-    {
-        /* We don't do everything again, we just copy the ct & ct_limits as all the volumes geometrically equal*/
-		this->beam->rpl_ct_vol_HU->set_ct (this->beam->rpl_vol->get_ct());
-		this->beam->rpl_ct_vol_HU->set_ct_limit(this->beam->rpl_vol->get_ct_limit());
-        
-		if (this->beam->get_flavor() == 'f' || this->beam->get_flavor() == 'g' || this->beam->get_flavor() == 'h')
-		{
-            this->beam->sigma_vol->set_ct(this->beam->rpl_vol->get_ct());
-            this->beam->sigma_vol->set_ct_limit(this->beam->rpl_vol->get_ct_limit());
-		}
-    }
-    else
-    {
-        printf("ray_data or clipping planes to be copied from rpl volume don't exist\n");
-    }
-    
-    /*Now we can compute the rpl_volume*/
-	this->beam->rpl_vol->compute_rpl_PrSTRP_no_rgc ();
-    
-    /* and the others */
-    if(this->beam->rpl_vol->get_Ray_data() && this->beam->rpl_vol->get_front_clipping_plane() && this->beam->rpl_vol->get_back_clipping_plane())
-    {
-		this->beam->rpl_ct_vol_HU->set_ray(this->beam->rpl_vol->get_Ray_data());
-		this->beam->rpl_ct_vol_HU->set_front_clipping_plane(this->beam->rpl_vol->get_front_clipping_plane());
-		this->beam->rpl_ct_vol_HU->set_back_clipping_plane(this->beam->rpl_vol->get_back_clipping_plane());
-		this->beam->rpl_ct_vol_HU->compute_rpl_HU();
-
-         if (this->beam->get_flavor() == 'f' || this->beam->get_flavor() == 'g' || this->beam->get_flavor() == 'h')
-		{
-			/* We don't do everything again, we just copy the ray_data & clipping planes as all the volumes geometrically equal*/
-        
-			this->beam->sigma_vol->set_ray(this->beam->rpl_vol->get_Ray_data());
-			this->beam->sigma_vol->set_front_clipping_plane(this->beam->rpl_vol->get_front_clipping_plane());
-			this->beam->sigma_vol->set_back_clipping_plane(this->beam->rpl_vol->get_back_clipping_plane());
-		}
-	}
-    else
-    {
-        printf("ct or ct_limits to be copied from rpl_vol don't exist\n");
-    }
-    return true;
-}
-
 void
 Rt_plan::set_patient (const std::string& patient_fn)
 {
@@ -249,10 +153,7 @@ Rt_plan::set_target (const std::string& target_fn)
     /* Need float, because compute_segdepth_volume assumes float */
     d_ptr->target->convert (PLM_IMG_TYPE_GPUIT_FLOAT);
 
-    /* Loop through beams, and reset target on them */
-    for (size_t i = 0; i < this->beam_storage.size(); i++) {
-        this->beam_storage[i]->set_target(d_ptr->target);
-    }
+    this->propagate_target_to_beams ();
 }
 
 void
@@ -262,14 +163,16 @@ Rt_plan::set_target (UCharImageType::Pointer& target_vol)
 
     /* compute_segdepth_volume assumes float */
     d_ptr->target->convert (PLM_IMG_TYPE_GPUIT_FLOAT);
-	this->beam->set_target(d_ptr->target);
+
+    this->propagate_target_to_beams ();
 }
 
 void
 Rt_plan::set_target (FloatImageType::Pointer& target_vol)
 {
     d_ptr->target->set_itk (target_vol);
-	this->beam->set_target(d_ptr->target);
+
+    this->propagate_target_to_beams ();
 }
 
 Plm_image::Pointer&
@@ -300,7 +203,7 @@ Rt_plan::append_beam ()
     } else {
         new_beam = new Rt_beam;
     }
-    this->beam_storage.push_back (new_beam);
+    d_ptr->beam_storage.push_back (new_beam);
     new_beam->set_target (d_ptr->target);
     return new_beam;
 }
@@ -308,10 +211,10 @@ Rt_plan::append_beam ()
 Rt_beam*
 Rt_plan::get_last_rt_beam ()
 {
-    if (this->beam_storage.empty()) {
+    if (d_ptr->beam_storage.empty()) {
         return 0;
     }
-    return this->beam_storage.back();
+    return d_ptr->beam_storage.back();
 }
 
 bool
@@ -344,610 +247,758 @@ Rt_plan::get_normalization_dose()
     return d_ptr->normalization_dose;
 }
 
+const float*
+Rt_plan::get_ref_dose_point () const
+{
+    return d_ptr->rdp;
+}
+
+float
+Rt_plan::get_ref_dose_point (int dim) const
+{
+    return d_ptr->rdp[dim];
+}
+
 void
-Rt_plan::compute_dose ()
+Rt_plan::set_ref_dose_point (const float* rdp)
 {
-    printf ("-- compute_dose entry --\n");
-    Rt_beam* beam = this->beam;
-    Volume::Pointer ct_vol = this->get_patient_volume ();
-    Volume::Pointer dose_vol = ct_vol->clone_empty ();
-    float* dose_img = (float*) dose_vol->img;
+    for (int d = 0; d < 3; d++) {
+        d_ptr->rdp[d] = rdp[d];
+    }
+}
 
-    Volume* dose_volume_tmp = new Volume;
-    float* dose_img_tmp = (float*) dose_volume_tmp->img;
+void
+Rt_plan::set_ref_dose_point (const double* rdp)
+{
+    for (int d = 0; d < 3; d++) {
+        d_ptr->rdp[d] = rdp[d];
+    }
+}
 
-    UNUSED_VARIABLE (dose_img_tmp);
+void 
+Rt_plan::set_have_ref_dose_point(bool have_rdp)
+{
+	d_ptr->have_rdp = have_rdp;
+}
 
-    float margin = 0;
-    int margins[2] = {0,0};
-    double range = 0;
-    int new_dim[2]={0,0};
-    double new_center[2]={0,0};
-    double biggest_sigma_ever = 0;
-    Plm_timer timer;
-    double time_sigma_conv = 0.0;
-    double time_dose_calc = 0.0;
-    double time_dose_misc = 0.0;
-    double time_dose_reformat = 0.0;
+bool
+Rt_plan::get_have_ref_dose_point()
+{
+	return d_ptr->have_rdp;
+}
 
-	double dose_max = 0;
+void 
+Rt_plan::set_have_dose_norm(bool have_dose_norm)
+{
+	d_ptr->have_dose_norm = have_dose_norm;
+}
+
+bool
+Rt_plan::get_have_dose_norm()
+{
+	return d_ptr->have_dose_norm;
+}
+
+char 
+Rt_plan::get_non_norm_dose () const
+{
+    return d_ptr->non_norm_dose;
+}
+    
+void 
+Rt_plan::set_non_norm_dose (char non_norm_dose)
+{
+	d_ptr->non_norm_dose = non_norm_dose;
+}
+
+void
+Rt_plan::propagate_target_to_beams ()
+{
+    /* Loop through beams, and reset target on them */
+    for (size_t i = 0; i < d_ptr->beam_storage.size(); i++) {
+        d_ptr->beam_storage[i]->set_target(d_ptr->target);
+    }
+}
 
-	printf ("Computing rpl_ct\n");
-	this->beam->rpl_ct_vol_HU->compute_rpl_HU ();
+bool
+Rt_plan::prepare_beam_for_calc (Rt_beam *beam)
+{
+    if (!beam) return false;
+    if (!this->get_patient()) return false;
 
-    if (this->beam->get_flavor() == 'f' || this->beam->get_flavor() == 'g' || this->beam->get_flavor() == 'h')
+    if (beam->get_aperture()->get_distance() > beam->get_source_distance ())
+    {
+        throw Plm_exception ("Source distance must be greater than aperture distance");
+    }
+    
+    if (!beam->rpl_vol) {beam->rpl_vol = new Rpl_volume;}
+    beam->rpl_vol->set_geometry (
+        beam->get_source_position(),
+        beam->get_isocenter_position(),
+        beam->get_aperture()->vup,
+        beam->get_aperture()->get_distance(),
+        beam->get_aperture()->get_dim(),
+        beam->get_aperture()->get_center(),
+        beam->get_aperture()->get_spacing(),
+        beam->get_step_length());
+    if (!beam->rpl_vol) return false;
+    /* building the ct_density_vol */
+    beam->rpl_ct_vol_HU = new Rpl_volume;
+    beam->rpl_ct_vol_HU->set_geometry (
+        beam->get_source_position(),
+        beam->get_isocenter_position(),
+        beam->get_aperture()->vup,
+        beam->get_aperture()->get_distance(),
+        beam->get_aperture()->get_dim(),
+        beam->get_aperture()->get_center(),
+        beam->get_aperture()->get_spacing(),
+        beam->get_step_length());
+    if (!beam->rpl_ct_vol_HU) return false;
+    if (beam->get_flavor() == 'f'|| beam->get_flavor() == 'g' || beam->get_flavor() == 'h')
     {
-        float sigmaMax = 0;
-        float *sigma_max =&sigmaMax; // used to find the max sigma in the volume and add extra margins during the dose creation volume
+        /* building the sigma_vol */
+        beam->sigma_vol = new Rpl_volume;
+        beam->sigma_vol->set_geometry (
+            beam->get_source_position(),
+            beam->get_isocenter_position(),
+            beam->get_aperture()->vup,
+            beam->get_aperture()->get_distance(),
+            beam->get_aperture()->get_dim(),
+            beam->get_aperture()->get_center(),
+            beam->get_aperture()->get_spacing(),
+            beam->get_step_length());
+        
+        if (!beam->sigma_vol) return false;
+    }
+
+    /* Copy aperture from scene into rpl volume */
+    beam->rpl_ct_vol_HU->set_aperture (beam->get_aperture());
 
-        printf ("Computing_void_rpl\n");
-		this->beam->sigma_vol->compute_rpl_PrSTRP_no_rgc(); // we compute the rglength in the sigma_volume, without the range compensator as it will be added by a different process
+    beam->rpl_vol->set_aperture (beam->get_aperture());
 
-        Rpl_volume* rpl_vol = this->beam->rpl_vol;
-        Rpl_volume* sigma_vol = this->beam->sigma_vol;
+    if (beam->get_flavor() == 'f' || beam->get_flavor() == 'g' || beam->get_flavor() == 'h')
+    {
+        Aperture::Pointer ap_sigma = Aperture::New(beam->get_aperture());
+        beam->sigma_vol->set_aperture (ap_sigma);
+        beam->sigma_vol->set_aperture (beam->get_aperture());
+    }
 
-        float* sigma_img = (float*) sigma_vol->get_vol()->img;
-        UNUSED_VARIABLE (sigma_img);
+    /* Scan through aperture to fill in rpl_volume */
+    beam->rpl_vol->set_ct_volume (d_ptr->patient);
 
-        /* building the sigma_dose_vol */
-        if (this->beam->get_flavor() == 'g') {
-            this->beam->rpl_dose_vol = new Rpl_volume;
+    if(beam->rpl_vol->get_ct() && beam->rpl_vol->get_ct_limit())
+    {
+        /* We don't do everything again, we just copy the ct & ct_limits as all the volumes geometrically equal*/
+        beam->rpl_ct_vol_HU->set_ct (beam->rpl_vol->get_ct());
+        beam->rpl_ct_vol_HU->set_ct_limit(beam->rpl_vol->get_ct_limit());
+        
+        if (beam->get_flavor() == 'f' || beam->get_flavor() == 'g' || beam->get_flavor() == 'h')
+        {
+            beam->sigma_vol->set_ct(beam->rpl_vol->get_ct());
+            beam->sigma_vol->set_ct_limit(beam->rpl_vol->get_ct_limit());
         }
+    }
+    else
+    {
+        printf("ray_data or clipping planes to be copied from rpl volume don't exist\n");
+    }
+
+    /*Now we can compute the rpl_volume*/
+    beam->rpl_vol->compute_rpl_PrSTRP_no_rgc ();
+    /* and the others */
+    if(beam->rpl_vol->get_Ray_data() && beam->rpl_vol->get_front_clipping_plane() && beam->rpl_vol->get_back_clipping_plane())
+    {
+        beam->rpl_ct_vol_HU->set_ray(beam->rpl_vol->get_Ray_data());
+        beam->rpl_ct_vol_HU->set_front_clipping_plane(beam->rpl_vol->get_front_clipping_plane());
+        beam->rpl_ct_vol_HU->set_back_clipping_plane(beam->rpl_vol->get_back_clipping_plane());
+        beam->rpl_ct_vol_HU->compute_rpl_HU();
 
-        if (this->beam->get_flavor() == 'h') {
-            this->beam->rpl_vol_lg = new Rpl_volume;
-            this->beam->rpl_ct_vol_HU_lg = new Rpl_volume;
-            this->beam->sigma_vol_lg = new Rpl_volume;
+        if (beam->get_flavor() == 'f' || beam->get_flavor() == 'g' || beam->get_flavor() == 'h')
+        {
+            /* We don't do everything again, we just copy the ray_data & clipping planes as all the volumes geometrically equal*/
+        
+            beam->sigma_vol->set_ray(beam->rpl_vol->get_Ray_data());
+            beam->sigma_vol->set_front_clipping_plane(beam->rpl_vol->get_front_clipping_plane());
+            beam->sigma_vol->set_back_clipping_plane(beam->rpl_vol->get_back_clipping_plane());
         }
+    }
+    else
+    {
+        printf("ct or ct_limits to be copied from rpl_vol don't exist\n");
+    }
+    return true;
+}
 
-        printf ("More setup\n");
-        std::vector<const Rt_depth_dose*> peaks = this->beam->get_sobp()->getPeaks();
+    void
+        Rt_plan::compute_dose (Rt_beam *beam)
+    {
+        printf ("-- compute_dose entry --\n");
+        Volume::Pointer ct_vol = this->get_patient_volume ();
+        Volume::Pointer dose_vol = ct_vol->clone_empty ();
+        float* dose_img = (float*) dose_vol->img;
+
+        Volume* dose_volume_tmp = new Volume;
+        float* dose_img_tmp = (float*) dose_volume_tmp->img;
+
+        UNUSED_VARIABLE (dose_img_tmp);
+
+        float margin = 0;
+        int margins[2] = {0,0};
+        double range = 0;
+        int new_dim[2]={0,0};
+        double new_center[2]={0,0};
+        double biggest_sigma_ever = 0;
+        Plm_timer timer;
+        double time_sigma_conv = 0.0;
+        double time_dose_calc = 0.0;
+        double time_dose_misc = 0.0;
+        double time_dose_reformat = 0.0;
+
+        printf ("Computing rpl_ct\n");
+        beam->rpl_ct_vol_HU->compute_rpl_HU ();
+
+        if (beam->get_flavor() == 'f' || beam->get_flavor() == 'g' || beam->get_flavor() == 'h')
+        {
+            float sigmaMax = 0;
+            float *sigma_max =&sigmaMax; // used to find the max sigma in the volume and add extra margins during the dose creation volume
 
-        std::vector<const Rt_depth_dose*>::const_reverse_iterator it;
-        for (it = peaks.rbegin (); it <peaks.rend(); it++) {
-            const Rt_depth_dose *ppp = *it;
-            printf("Building dose matrix for %lg MeV beamlets - \n", ppp->E0);
-            timer.start ();
-            compute_sigmas(this, ppp->E0, sigma_max, "small", margins);
-            time_sigma_conv += timer.report ();
+            printf ("Computing_void_rpl\n");
 
-            if (this->beam->get_flavor() == 'f') // Desplanques' algorithm
-            {
-                range = 10 * getrange(ppp->E0); // range in mm
-                dose_volume_create(dose_volume_tmp, sigma_max, this->beam->rpl_vol, range);
-                compute_dose_ray_desplanques(dose_volume_tmp, ct_vol, rpl_vol, sigma_vol, this->beam->rpl_ct_vol_HU, this->beam, dose_vol, ppp, this->get_normalization_dose());
-            }
-            else if (this->beam->get_flavor() == 'g') // Sharp's algorithm
-            {
-                timer.start ();
+            beam->sigma_vol->compute_rpl_PrSTRP_no_rgc(); // we compute the rglength in the sigma_volume, without the range compensator as it will be added by a different process
+            Rpl_volume* rpl_vol = beam->rpl_vol;
+            Rpl_volume* sigma_vol = beam->sigma_vol;
 
-                if (*sigma_max > biggest_sigma_ever)
-                {
-                    biggest_sigma_ever = *sigma_max;
-                    /* Calculating the pixel-margins of the aperture to take into account the scattering*/
-                    margin = (float) 3 * (*sigma_max)/(this->beam->get_aperture()->get_distance() + this->beam->rpl_vol->get_front_clipping_plane()) * this->beam->get_aperture()->get_distance()+1;
-                    margins[0] = ceil (margin/vec3_len(this->beam->rpl_vol->get_proj_volume()->get_incr_c()));
-                    margins[1] = ceil (margin/vec3_len(this->beam->rpl_vol->get_proj_volume()->get_incr_r()));
-                    new_dim[0] = this->beam->rpl_vol->get_aperture()->get_dim(0) + 2 * margins[0];
-                    new_dim[1] = this->beam->rpl_vol->get_aperture()->get_dim(1) + 2 * margins[1];
-                    new_center[0] = this->beam->rpl_vol->get_aperture()->get_center(0) + margins[0];
-                    new_center[1] = this->beam->rpl_vol->get_aperture()->get_center(1) + margins[1];
-
-                    this->beam->rpl_dose_vol->get_aperture()->set_center(new_center);
-                    this->beam->rpl_dose_vol->get_aperture()->set_dim(new_dim);
-
-                    this->beam->rpl_dose_vol->get_aperture()->set_distance(this->beam->rpl_vol->get_aperture()->get_distance());
-                    this->beam->rpl_dose_vol->get_aperture()->set_spacing(this->beam->rpl_vol->get_aperture()->get_spacing());
-
-                    this->beam->rpl_dose_vol->set_geometry (
-                        this->beam->get_source_position(),
-                        this->beam->get_isocenter_position(),
-                        this->beam->get_aperture()->vup,
-                        this->beam->get_aperture()->get_distance(),
-                        this->beam->rpl_dose_vol->get_aperture()->get_dim(),
-                        this->beam->rpl_dose_vol->get_aperture()->get_center(),
-                        this->beam->get_aperture()->get_spacing(),
-                        this->beam->get_step_length());
-
-                    this->beam->rpl_dose_vol->set_ct(this->beam->rpl_vol->get_ct());
-                    this->beam->rpl_dose_vol->set_ct_limit(this->beam->rpl_vol->get_ct_limit());
-                    this->beam->rpl_dose_vol->compute_ray_data();
-				
-                    this->beam->rpl_dose_vol->set_front_clipping_plane(this->beam->rpl_vol->get_front_clipping_plane());
-                    this->beam->rpl_dose_vol->set_back_clipping_plane(this->beam->rpl_vol->get_back_clipping_plane());
-                }
+            float* sigma_img = (float*) sigma_vol->get_vol()->img;
+            UNUSED_VARIABLE (sigma_img);
 
-                /* update the dose_vol with the CT values before to calculate the dose */
-                this->beam->rpl_dose_vol->compute_rpl_void();
-                time_dose_misc += timer.report ();
 
-                /* dose calculation in the rpl_dose_volume */
-                timer.start ();
-                compute_dose_ray_sharp (ct_vol, rpl_vol, sigma_vol, 
-                    this->beam->rpl_ct_vol_HU, this->beam, this->beam->rpl_dose_vol, this->beam->get_aperture(), 
-                    ppp, margins, this->get_normalization_dose());
-                time_dose_calc += timer.report ();
+            /* building the sigma_dose_vol */
+            if (beam->get_flavor() == 'g') {
+                beam->rpl_dose_vol = new Rpl_volume;
+            }
 
-                timer.start ();
-                dose_volume_reconstruction(this->beam->rpl_dose_vol, dose_vol);
-                time_dose_reformat += timer.report ();
+            if (beam->get_flavor() == 'h') {
+                beam->rpl_vol_lg = new Rpl_volume;
+                beam->rpl_ct_vol_HU_lg = new Rpl_volume;
+                beam->sigma_vol_lg = new Rpl_volume;
             }
 
-            if (this->beam->get_flavor() == 'h') // Shackleford's algorithm
-            {
+            printf ("More setup\n");
 
-                /* Calculating the pixel-margins of the aperture to take into account the scattering*/
-                margin = (float) 3 * (*sigma_max)/(this->beam->get_aperture()->get_distance()+this->beam->rpl_vol->get_front_clipping_plane()) * this->beam->get_aperture()->get_distance()+1;
-                margins[0] = ceil (margin/vec3_len(this->beam->rpl_vol->get_proj_volume()->get_incr_c()));
-                margins[1] = ceil (margin/vec3_len(this->beam->rpl_vol->get_proj_volume()->get_incr_r()));
-                new_dim[0] = this->beam->rpl_vol->get_aperture()->get_dim(0) + 2 * margins[0];
-                new_dim[1] = this->beam->rpl_vol->get_aperture()->get_dim(1) + 2 * margins[1];
-                new_center[0] = this->beam->rpl_vol->get_aperture()->get_center(0) + margins[0];
-                new_center[1] = this->beam->rpl_vol->get_aperture()->get_center(1) + margins[1];
-
-                int radius_sample = 4;
-                int theta_sample = 8;
-                std::vector<double> xy_grid (2*(radius_sample * theta_sample),0); // contains the xy coordinates of the sectors in the plane; the central pixel is not included in this vector. 
-                std::vector<double> area (radius_sample, 0); // contains the areas of the sectors
-
-                this->beam->rpl_vol_lg->get_aperture()->set_center(new_center);
-                this->beam->rpl_vol_lg->get_aperture()->set_dim(new_dim);
-                this->beam->rpl_vol_lg->get_aperture()->set_distance(this->beam->rpl_vol->get_aperture()->get_distance());
-                this->beam->rpl_vol_lg->get_aperture()->set_spacing(this->beam->rpl_vol->get_aperture()->get_spacing());
-                this->beam->rpl_vol_lg->set_geometry (this->beam->get_source_position(), this->beam->get_isocenter_position(), this->beam->get_aperture()->vup, this->beam->get_aperture()->get_distance(), this->beam->rpl_vol_lg->get_aperture()->get_dim(), this->beam->rpl_vol_lg->get_aperture()->get_center(), this->beam->get_aperture()->get_spacing(), this->beam->get_step_length());
-                this->beam->rpl_vol_lg->set_ct(this->beam->rpl_vol->get_ct());
-                this->beam->rpl_vol_lg->set_ct_limit(this->beam->rpl_vol->get_ct_limit());
-                this->beam->rpl_vol_lg->compute_ray_data();
-                this->beam->rpl_vol_lg->compute_rpl_PrSTRP_no_rgc();
-
-                this->beam->rpl_ct_vol_HU_lg->get_aperture()->set_center(new_center);
-                this->beam->rpl_ct_vol_HU_lg->get_aperture()->set_dim(new_dim);
-                this->beam->rpl_ct_vol_HU_lg->get_aperture()->set_distance(this->beam->rpl_vol->get_aperture()->get_distance());
-                this->beam->rpl_ct_vol_HU_lg->get_aperture()->set_spacing(this->beam->rpl_vol->get_aperture()->get_spacing());
-                this->beam->rpl_ct_vol_HU_lg->set_geometry (this->beam->get_source_position(), this->beam->get_isocenter_position(), this->beam->get_aperture()->vup, this->beam->get_aperture()->get_distance(), this->beam->rpl_vol_lg->get_aperture()->get_dim(), this->beam->rpl_vol_lg->get_aperture()->get_center(), this->beam->get_aperture()->get_spacing(), this->beam->get_step_length());
-                this->beam->rpl_ct_vol_HU_lg->set_ct(this->beam->rpl_vol->get_ct());
-                this->beam->rpl_ct_vol_HU_lg->set_ct_limit(this->beam->rpl_vol->get_ct_limit());
-                this->beam->rpl_ct_vol_HU_lg->compute_ray_data();
-                this->beam->rpl_ct_vol_HU_lg->set_front_clipping_plane(this->beam->rpl_vol_lg->get_front_clipping_plane());
-                this->beam->rpl_ct_vol_HU_lg->set_back_clipping_plane(this->beam->rpl_vol_lg->get_back_clipping_plane());
-                this->beam->rpl_ct_vol_HU_lg->compute_rpl_HU();
-
-                this->beam->sigma_vol_lg->get_aperture()->set_center(new_center);
-                this->beam->sigma_vol_lg->get_aperture()->set_dim(new_dim);
-				
-                this->beam->sigma_vol_lg->get_aperture()->set_distance(this->beam->rpl_vol->get_aperture()->get_distance());
-                this->beam->sigma_vol_lg->get_aperture()->set_spacing(this->beam->rpl_vol->get_aperture()->get_spacing());
-                this->beam->sigma_vol_lg->set_geometry (this->beam->get_source_position(), this->beam->get_isocenter_position(), this->beam->get_aperture()->vup, this->beam->get_aperture()->get_distance(), this->beam->rpl_vol_lg->get_aperture()->get_dim(), this->beam->rpl_vol_lg->get_aperture()->get_center(), this->beam->get_aperture()->get_spacing(), this->beam->get_step_length());
-                this->beam->sigma_vol_lg->set_ct(this->beam->rpl_vol->get_ct());
-                this->beam->sigma_vol_lg->set_ct_limit(this->beam->rpl_vol->get_ct_limit());
-                this->beam->sigma_vol_lg->compute_ray_data();
-                this->beam->sigma_vol_lg->set_front_clipping_plane(this->beam->rpl_vol_lg->get_front_clipping_plane());
-                this->beam->sigma_vol_lg->set_back_clipping_plane(this->beam->rpl_vol_lg->get_back_clipping_plane());
-                this->beam->sigma_vol_lg->compute_rpl_PrSTRP_no_rgc();
-
-                if (this->beam->get_aperture()->have_aperture_image() == true)
-                {
-                    this->beam->aperture_vol = new Rpl_volume;
+            std::vector<Rt_depth_dose*> depth_dose = beam->get_mebs()->get_depth_dose();
+
+            for (int i = 0; i < depth_dose.size(); i++) {
+                const Rt_depth_dose *ppp = beam->get_mebs()->get_depth_dose()[i];
+                printf("Building dose matrix for %lg MeV beamlets - \n", ppp->E0);
+                timer.start ();
 
-                    this->beam->aperture_vol->get_aperture()->set_center(this->beam->get_aperture()->get_center());
-                    this->beam->aperture_vol->get_aperture()->set_dim(this->beam->get_aperture()->get_dim());
-				
-                    this->beam->aperture_vol->get_aperture()->set_distance(this->beam->rpl_vol->get_aperture()->get_distance());
-                    this->beam->aperture_vol->get_aperture()->set_spacing(this->beam->rpl_vol->get_aperture()->get_spacing());
+                compute_sigmas (this, beam, ppp->E0, sigma_max, "small", margins);
+                time_sigma_conv += timer.report ();
 
-                    this->beam->aperture_vol->set_geometry (this->beam->get_source_position(), this->beam->get_isocenter_position(), this->beam->get_aperture()->vup, this->beam->get_aperture()->get_distance(), this->beam->rpl_vol->get_aperture()->get_dim(), this->beam->rpl_vol->get_aperture()->get_center(), this->beam->get_aperture()->get_spacing(), this->beam->get_step_length());
+                if (beam->get_flavor() == 'f') // Desplanques' algorithm
+                {
+                    range = 10 * get_proton_range(ppp->E0); // range in mm
+                    dose_volume_create(dose_volume_tmp, sigma_max, beam->rpl_vol, range);
+                    compute_dose_ray_desplanques(dose_volume_tmp, ct_vol, beam, dose_vol, i);
+                }
+                else if (beam->get_flavor() == 'g') // Sharp's algorithm
+                {
+                    timer.start ();
 
-                    this->beam->aperture_vol->set_ct(this->beam->rpl_vol->get_ct());
-                    this->beam->aperture_vol->set_ct_limit(this->beam->rpl_vol->get_ct_limit());
-                    this->beam->aperture_vol->compute_ray_data();
-                    this->beam->aperture_vol->set_front_clipping_plane(this->beam->rpl_vol->get_front_clipping_plane());
-                    this->beam->aperture_vol->set_back_clipping_plane(this->beam->rpl_vol->get_back_clipping_plane());
-                    this->beam->aperture_vol->compute_rpl_void();
+                    if (*sigma_max > biggest_sigma_ever)
+                    {
+                        biggest_sigma_ever = *sigma_max;
+                        /* Calculating the pixel-margins of the aperture to take into account the scattering*/
+                        margin = (float) 3 * (*sigma_max)/(beam->get_aperture()->get_distance() + beam->rpl_vol->get_front_clipping_plane()) * beam->get_aperture()->get_distance()+1;
+                        margins[0] = ceil (margin/vec3_len(beam->rpl_vol->get_proj_volume()->get_incr_c()));
+                        margins[1] = ceil (margin/vec3_len(beam->rpl_vol->get_proj_volume()->get_incr_r()));
+                        new_dim[0] = beam->rpl_vol->get_aperture()->get_dim(0) + 2 * margins[0];
+                        new_dim[1] = beam->rpl_vol->get_aperture()->get_dim(1) + 2 * margins[1];
+                        new_center[0] = beam->rpl_vol->get_aperture()->get_center(0) + margins[0];
+                        new_center[1] = beam->rpl_vol->get_aperture()->get_center(1) + margins[1];
+
+                        beam->rpl_dose_vol->get_aperture()->set_center(new_center);
+                        beam->rpl_dose_vol->get_aperture()->set_dim(new_dim);
+                        beam->rpl_dose_vol->get_aperture()->set_distance(beam->rpl_vol->get_aperture()->get_distance());
+                        beam->rpl_dose_vol->get_aperture()->set_spacing(beam->rpl_vol->get_aperture()->get_spacing());
+
+                        beam->rpl_dose_vol->set_geometry (
+                            beam->get_source_position(),
+                            beam->get_isocenter_position(),
+                            beam->get_aperture()->vup,
+                            beam->get_aperture()->get_distance(),
+                            beam->rpl_dose_vol->get_aperture()->get_dim(),
+                            beam->rpl_dose_vol->get_aperture()->get_center(),
+                            beam->get_aperture()->get_spacing(),
+                            beam->get_step_length());
+
+                        beam->rpl_dose_vol->set_ct(beam->rpl_vol->get_ct());
+                        beam->rpl_dose_vol->set_ct_limit(beam->rpl_vol->get_ct_limit());
+                        beam->rpl_dose_vol->compute_ray_data();
+                        beam->rpl_dose_vol->set_front_clipping_plane(beam->rpl_vol->get_front_clipping_plane());
+                        beam->rpl_dose_vol->set_back_clipping_plane(beam->rpl_vol->get_back_clipping_plane());
+                    }
 
-                    this->beam->aperture_vol->compute_volume_aperture(this->beam->get_aperture());
+                    /* update the dose_vol with the CT values before to calculate the dose */
+                    beam->rpl_dose_vol->compute_rpl_void();
+                    time_dose_misc += timer.report ();
+
+                    /* dose calculation in the rpl_dose_volume */
+                    timer.start ();
+                    compute_dose_ray_sharp (ct_vol, beam, beam->rpl_dose_vol, i, margins);
+                    time_dose_calc += timer.report ();
+                    timer.start ();
+                    dose_volume_reconstruction(beam->rpl_dose_vol, dose_vol);
+                    time_dose_reformat += timer.report ();
                 }
 
-                compute_sigmas(this, ppp->E0, sigma_max, "large", margins);				
-                build_hong_grid(&area, &xy_grid, radius_sample, theta_sample);
-                compute_dose_ray_shackleford(dose_vol, this, ppp, &area, &xy_grid, radius_sample, theta_sample);
+                if (beam->get_flavor() == 'h') // Shackleford's algorithm
+                {
+                    /* Calculating the pixel-margins of the aperture to take into account the scattering*/
+                    margin = (float) 3 * (*sigma_max)/(beam->get_aperture()->get_distance()+beam->rpl_vol->get_front_clipping_plane()) * beam->get_aperture()->get_distance()+1;
+                    margins[0] = ceil (margin/vec3_len(beam->rpl_vol->get_proj_volume()->get_incr_c()));
+                    margins[1] = ceil (margin/vec3_len(beam->rpl_vol->get_proj_volume()->get_incr_r()));
+                    new_dim[0] = beam->rpl_vol->get_aperture()->get_dim(0) + 2 * margins[0];
+                    new_dim[1] = beam->rpl_vol->get_aperture()->get_dim(1) + 2 * margins[1];
+                    new_center[0] = beam->rpl_vol->get_aperture()->get_center(0) + margins[0];
+                    new_center[1] = beam->rpl_vol->get_aperture()->get_center(1) + margins[1];
+
+                    int radius_sample = 4;
+                    int theta_sample = 8;
+                    std::vector<double> xy_grid (2*(radius_sample * theta_sample),0); // contains the xy coordinates of the sectors in the plane; the central pixel is not included in this vector. 
+                    std::vector<double> area (radius_sample, 0); // contains the areas of the sectors
+
+                    beam->rpl_vol_lg->get_aperture()->set_center(new_center);
+                    beam->rpl_vol_lg->get_aperture()->set_dim(new_dim);
+                    beam->rpl_vol_lg->get_aperture()->set_distance(beam->rpl_vol->get_aperture()->get_distance());
+                    beam->rpl_vol_lg->get_aperture()->set_spacing(beam->rpl_vol->get_aperture()->get_spacing());
+                    beam->rpl_vol_lg->set_geometry (beam->get_source_position(), beam->get_isocenter_position(), beam->get_aperture()->vup, beam->get_aperture()->get_distance(), beam->rpl_vol_lg->get_aperture()->get_dim(), beam->rpl_vol_lg->get_aperture()->get_center(), beam->get_aperture()->get_spacing(), beam->get_step_length());
+                    beam->rpl_vol_lg->set_ct(beam->rpl_vol->get_ct());
+                    beam->rpl_vol_lg->set_ct_limit(beam->rpl_vol->get_ct_limit());
+                    beam->rpl_vol_lg->compute_ray_data();
+                    beam->rpl_vol_lg->compute_rpl_PrSTRP_no_rgc();
+
+                    beam->rpl_ct_vol_HU_lg->get_aperture()->set_center(new_center);
+                    beam->rpl_ct_vol_HU_lg->get_aperture()->set_dim(new_dim);
+                    beam->rpl_ct_vol_HU_lg->get_aperture()->set_distance(beam->rpl_vol->get_aperture()->get_distance());
+                    beam->rpl_ct_vol_HU_lg->get_aperture()->set_spacing(beam->rpl_vol->get_aperture()->get_spacing());
+                    beam->rpl_ct_vol_HU_lg->set_geometry (beam->get_source_position(), beam->get_isocenter_position(), beam->get_aperture()->vup, beam->get_aperture()->get_distance(), beam->rpl_vol_lg->get_aperture()->get_dim(), beam->rpl_vol_lg->get_aperture()->get_center(), beam->get_aperture()->get_spacing(), beam->get_step_length());
+                    beam->rpl_ct_vol_HU_lg->set_ct(beam->rpl_vol->get_ct());
+                    beam->rpl_ct_vol_HU_lg->set_ct_limit(beam->rpl_vol->get_ct_limit());
+                    beam->rpl_ct_vol_HU_lg->compute_ray_data();
+                    beam->rpl_ct_vol_HU_lg->set_front_clipping_plane(beam->rpl_vol_lg->get_front_clipping_plane());
+                    beam->rpl_ct_vol_HU_lg->set_back_clipping_plane(beam->rpl_vol_lg->get_back_clipping_plane());
+                    beam->rpl_ct_vol_HU_lg->compute_rpl_HU();
+
+                    beam->sigma_vol_lg->get_aperture()->set_center(new_center);
+                    beam->sigma_vol_lg->get_aperture()->set_dim(new_dim);	
+                    beam->sigma_vol_lg->get_aperture()->set_distance(beam->rpl_vol->get_aperture()->get_distance());
+                    beam->sigma_vol_lg->get_aperture()->set_spacing(beam->rpl_vol->get_aperture()->get_spacing());
+                    beam->sigma_vol_lg->set_geometry (beam->get_source_position(), beam->get_isocenter_position(), beam->get_aperture()->vup, beam->get_aperture()->get_distance(), beam->rpl_vol_lg->get_aperture()->get_dim(), beam->rpl_vol_lg->get_aperture()->get_center(), beam->get_aperture()->get_spacing(), beam->get_step_length());
+                    beam->sigma_vol_lg->set_ct(beam->rpl_vol->get_ct());
+                    beam->sigma_vol_lg->set_ct_limit(beam->rpl_vol->get_ct_limit());
+                    beam->sigma_vol_lg->compute_ray_data();
+                    beam->sigma_vol_lg->set_front_clipping_plane(beam->rpl_vol_lg->get_front_clipping_plane());
+                    beam->sigma_vol_lg->set_back_clipping_plane(beam->rpl_vol_lg->get_back_clipping_plane());
+                    beam->sigma_vol_lg->compute_rpl_PrSTRP_no_rgc();
+
+                    compute_sigmas (this, beam, ppp->E0, sigma_max, "large", margins);				
+                    build_hong_grid(&area, &xy_grid, radius_sample, theta_sample);
+                    compute_dose_ray_shackleford (
+                        dose_vol, this, beam,
+                        i, &area, &xy_grid,
+                        radius_sample, theta_sample);
+                }
+                printf("dose computed\n");
             }
-            printf("dose computed\n");
         }
-    }
-    if (this->beam->get_flavor() == 'a') // pull algorithm
-    {     
-        /* if (this->get_debug()) {
-           rpl_vol->save ("beam_debug/depth_vol.mha");
-           beam->dump ("beam_debug");
-           }*/
-
-		/* Dose D(POI) = Dose(z_POI) but z_POI =  rg_comp + depth in CT, if there is a range compensator */
-		if (this->beam->rpl_vol->get_aperture()->have_range_compensator_image())
-		{
-			add_rcomp_length_to_rpl_volume(this->beam);
-		}
-
-        /* Create 3D aperture volume */
-        if (this->beam->get_aperture()->have_aperture_image() == true)
-        {
-            this->beam->aperture_vol = new Rpl_volume;
-            this->beam->aperture_vol->get_aperture()->set_center(this->beam->get_aperture()->get_center());
-            this->beam->aperture_vol->get_aperture()->set_dim(this->beam->get_aperture()->get_dim());
-			
-            this->beam->aperture_vol->get_aperture()->set_distance(this->beam->rpl_vol->get_aperture()->get_distance());
-            this->beam->aperture_vol->get_aperture()->set_spacing(this->beam->rpl_vol->get_aperture()->get_spacing());
-			
-            this->beam->aperture_vol->set_geometry (this->beam->get_source_position(), this->beam->get_isocenter_position(), this->beam->get_aperture()->vup, this->beam->get_aperture()->get_distance(), this->beam->rpl_vol->get_aperture()->get_dim(), this->beam->rpl_vol->get_aperture()->get_center(), this->beam->get_aperture()->get_spacing(), this->beam->get_step_length());
-
-            this->beam->aperture_vol->set_ct(this->beam->rpl_vol->get_ct());
-            this->beam->aperture_vol->set_ct_limit(this->beam->rpl_vol->get_ct_limit());
-            this->beam->aperture_vol->compute_ray_data();
-            this->beam->aperture_vol->set_front_clipping_plane(this->beam->rpl_vol->get_front_clipping_plane());
-            this->beam->aperture_vol->set_back_clipping_plane(this->beam->rpl_vol->get_back_clipping_plane());
-            this->beam->aperture_vol->compute_rpl_void();
+        if (beam->get_flavor() == 'a') // pull algorithm
+        {    
+            /* Dose D(POI) = Dose(z_POI) but z_POI =  rg_comp + depth in CT, if there is a range compensator */
+            if (beam->rpl_vol->get_aperture()->have_range_compensator_image())
+            {
+                add_rcomp_length_to_rpl_volume(beam);
+            }
 
-            this->beam->aperture_vol->compute_volume_aperture(this->beam->get_aperture());
+            /* scan through patient CT Volume */
+            plm_long ct_ijk[3];
+            double ct_xyz[4];
+            plm_long idx = 0;
+            double idx_ap[2] = {0,0};
+            int idx_ap_int[2] = {0,0};
+            double rest[2] = {0,0};
+            unsigned char* ap_img = (unsigned char*) beam->get_aperture()->get_aperture_volume()->img;
+            double particle_number = 0;
+            float WER = 0;
+            float rgdepth = 0;
+
+            for (ct_ijk[2] = 0; ct_ijk[2] < ct_vol->dim[2]; ct_ijk[2]++) {
+                for (ct_ijk[1] = 0; ct_ijk[1] < ct_vol->dim[1]; ct_ijk[1]++) {
+                    for (ct_ijk[0] = 0; ct_ijk[0] < ct_vol->dim[0]; ct_ijk[0]++) {
+                        double dose = 0.0;
+                        bool voxel_debug = false;
+
+                        /* Transform vol index into space coords */
+                        ct_xyz[0] = (double) (ct_vol->origin[0] + ct_ijk[0] * ct_vol->spacing[0]);
+                        ct_xyz[1] = (double) (ct_vol->origin[1] + ct_ijk[1] * ct_vol->spacing[1]);
+                        ct_xyz[2] = (double) (ct_vol->origin[2] + ct_ijk[2] * ct_vol->spacing[2]);
+                        ct_xyz[3] = (double) 1.0;
+
+                        if (beam->get_intersection_with_aperture(idx_ap, idx_ap_int, rest, ct_xyz) == false)
+                        {
+                            continue;
+                        }
+
+                        /* Check that the ray cross the aperture */
+                        if (idx_ap[0] < 0 || idx_ap[0] > (double) beam->rpl_ct_vol_HU->get_proj_volume()->get_image_dim(0)-1
+                            || idx_ap[1] < 0 || idx_ap[1] > (double) beam->rpl_ct_vol_HU->get_proj_volume()->get_image_dim(1)-1)
+                        {
+                            continue;
+                        }
+
+                        /* Check that the ray cross the active part of the aperture */
+                        if (beam->get_aperture()->have_aperture_image() && beam->is_ray_in_the_aperture(idx_ap_int, ap_img) == false)
+                        {
+                            continue;
+                        }
+
+                        switch (beam->get_flavor()) {
+                        case 'a':
+                            dose = 0;
+                            rgdepth = beam->rpl_vol->get_rgdepth (ct_xyz);
+                            WER =  compute_PrWER_from_HU(beam->rpl_ct_vol_HU->get_rgdepth(ct_xyz));
+
+                            for (int beam_idx = 0; beam_idx < beam->get_mebs()->get_depth_dose().size(); beam_idx++)
+                            {
+                                particle_number = beam->get_mebs()->get_particle_number_xyz(idx_ap_int, rest, beam_idx, beam->get_aperture()->get_dim());
+                                if (particle_number != 0 && rgdepth >=0 && rgdepth < beam->get_mebs()->get_depth_dose()[beam_idx]->dend) 
+                                {
+                                    dose += particle_number * WER * energy_direct (rgdepth, beam, beam_idx);
+                                }
+                            }
+                            break;
+                        }
+
+                        /* Insert the dose into the dose volume */
+                        idx = volume_index (dose_vol->dim, ct_ijk);
+                        dose_img[idx] = dose;
+                    }
+                }
+            }
+            display_progress ((float)idx, (float)ct_vol->npix);
         }
 
-        /* scan through patient CT Volume */
-        plm_long ct_ijk[3];
-        double ct_xyz[4];
-        plm_long idx = 0;
-
-        for (ct_ijk[2] = 0; ct_ijk[2] < ct_vol->dim[2]; ct_ijk[2]++) {
-            for (ct_ijk[1] = 0; ct_ijk[1] < ct_vol->dim[1]; ct_ijk[1]++) {
-                for (ct_ijk[0] = 0; ct_ijk[0] < ct_vol->dim[0]; ct_ijk[0]++) {
-                    double dose = 0.0;
-
-                    bool voxel_debug = false;
-#if defined (commentout)
-                    if (ct_ijk[2] == 60 && ct_ijk[1] == 44 && ct_ijk[0] == 5) {
-                        voxel_debug = true;
+	/* Dose normalization process*/
+	if (this->get_non_norm_dose() != 'y')
+	{
+            if (this->get_have_ref_dose_point()) // case 1: ref dose point defined
+            {
+                float rdp_ijk[3] = {0,0,0};
+                float rdp[3] = {this->get_ref_dose_point(0), this->get_ref_dose_point(1), this->get_ref_dose_point(2)};
+                rdp_ijk[0] = (rdp[0] - dose_vol->origin[0]) / dose_vol->spacing[0];
+                rdp_ijk[1] = (rdp[1] - dose_vol->origin[1]) / dose_vol->spacing[1];
+                rdp_ijk[2] = (rdp[2] - dose_vol->origin[2]) / dose_vol->spacing[2];
+			
+                if (rdp_ijk[0] >=0 && rdp_ijk[1] >=0 && rdp_ijk[2] >=0 && rdp_ijk[0] < dose_vol->dim[0] && rdp_ijk[1] < dose_vol->dim[1] && rdp_ijk[2] < dose_vol->dim[2])
+                {
+                    printf("Dose normalized to the dose reference point.\n");
+                    dose_normalization_to_dose_and_point(dose_vol, beam->get_beam_weight() * this->get_normalization_dose(), rdp_ijk, rdp, beam); // if no normalization dose, norm_dose = 1 by default
+                    if (this->get_have_dose_norm())
+                    {
+                        printf("%lg x %lg Gy.\n", beam->get_beam_weight(), this->get_normalization_dose());
                     }
-#endif
-	
-                    /* Transform vol index into space coords */
-                    ct_xyz[0] = (double) (ct_vol->origin[0] + ct_ijk[0] * ct_vol->spacing[0]);
-                    ct_xyz[1] = (double) (ct_vol->origin[1] + ct_ijk[1] * ct_vol->spacing[1]);
-                    ct_xyz[2] = (double) (ct_vol->origin[2] + ct_ijk[2] * ct_vol->spacing[2]);
-                    ct_xyz[3] = (double) 1.0;
-	    
-                    if (voxel_debug) {
-                        printf ("Voxel (%d, %d, %d) -> (%f, %f, %f)\n",
-                            (int) ct_ijk[0], (int) ct_ijk[1], (int) ct_ijk[2], 
-                            ct_xyz[0], ct_xyz[1], ct_xyz[2]);
+                    else
+                    {
+                        printf("%lg x 100%%.\n", beam->get_beam_weight());
                     }
-
-                    if (this->beam->get_aperture()->have_aperture_image() == true && this->beam->aperture_vol->get_rgdepth(ct_xyz) < .999)
+                    printf("Primary PB num. x, y: %d, %d, primary PB res. x, y: %lg PB/mm, %lg PB/mm\n", beam->get_aperture()->get_dim(0), beam->get_aperture()->get_dim(1), 1.0 / (double) beam->get_aperture()->get_spacing(0), 1.0 / (double) beam->get_aperture()->get_spacing(1));
+                }
+                else
+                {
+                    printf("***WARNING***\nThe reference dose point is not in the image volume.\n");
+                    dose_normalization_to_dose(dose_vol, beam->get_beam_weight() * this->get_normalization_dose(), beam);
+                    if (this->get_have_dose_norm())
                     {
-                        continue;
+                        printf("%lg x %lg Gy.\n", beam->get_beam_weight(), this->get_normalization_dose());
                     }
-
-                    switch (beam->get_flavor()) {
-                    case 'a':
-                        dose = dose_direct (ct_xyz, this->beam);
-                        break;
-                    case 'b':
-                        dose = dose_scatter (ct_xyz, ct_ijk, this->beam); 
-                        break;
-                    case 'c':
-                        dose = dose_hong (ct_xyz, ct_ijk, this->beam);
-                        break;
-                    case 'd':
-                        dose = dose_debug (ct_xyz, this->beam);
-                        break;
-                    case 'e':
-                        dose = dose_hong_maxime (ct_xyz, ct_ijk, this->beam);
-                        break;
+                    else
+                    {
+                        printf("%lg x 100%%.\n", beam->get_beam_weight());
                     }
-
-                    /* Insert the dose into the dose volume */
-                    idx = volume_index (dose_vol->dim, ct_ijk);
-                    dose_img[idx] = dose;
+                    printf("Primary PB num. x, y: %d, %d, primary PB res. x, y: %lg PB/mm, %lg PB/mm\n", beam->get_aperture()->get_dim(0), beam->get_aperture()->get_dim(1), 1.0 / (double) beam->get_aperture()->get_spacing(0), 1.0 / (double) beam->get_aperture()->get_spacing(1));
                 }
             }
-        }
-        display_progress ((float)idx, (float)ct_vol->npix);
-    }
-
-	/* finding dose_max */
-	dose_max = 0;
-	for(int i = 0; i < ct_vol->dim[0] * ct_vol->dim[1] * ct_vol->dim[2]; i++)
-	{
-		if (dose_img[i] > dose_max)
-		{
-			dose_max = dose_img[i];
-		}
+            else // case 2: no red dose point defined
+            {				
+                dose_normalization_to_dose(dose_vol, beam->get_beam_weight() * this->get_normalization_dose(), beam); // normalization_dose = 1 if no dose_prescription is set
+                if (this->get_have_dose_norm())
+                {
+                    printf("%lg x %lg Gy.\n", beam->get_beam_weight(), this->get_normalization_dose());
+                }
+                else
+                {
+                    printf("%lg x 100%%.\n", beam->get_beam_weight());
+                }
+                printf("Primary PB num. x, y: %d, %d, primary PB res. x, y: %lg PB/mm, %lg PB/mm\n", beam->get_aperture()->get_dim(0), beam->get_aperture()->get_dim(1), 1.0 / (double) beam->get_aperture()->get_spacing(0), 1.0 / (double) beam->get_aperture()->get_spacing(1));
+            }
 	}
-
-	/* Normalize the dose for this beam to the dose prescription corrected by the SOBP max */
-	if (dose_max != 0)
+	else // raw dose, dose not normalized
 	{
-		for(int i = 0; i < ct_vol->dim[0] * ct_vol->dim[1] * ct_vol->dim[2]; i++)
-		{
-			dose_img[i] = dose_img[i] * this->get_normalization_dose() * this->beam->get_sobp()->GetDoseMax()/dose_max;
-		}
+            for (int i = 0; i < dose_vol->dim[0] * dose_vol->dim[1] * dose_vol->dim[2]; i++)
+            {
+                dose_img[i] *= beam->get_beam_weight();
+            }
 	}
 
-    Plm_image::Pointer dose = Plm_image::New();
-    dose->set_volume (dose_vol);
-    this->beam->set_dose(dose);
-
-    printf ("Sigma conversion: %f seconds\n", time_sigma_conv);
-    printf ("Dose calculation: %f seconds\n", time_dose_calc);
-    printf ("Dose reformat: %f seconds\n", time_dose_reformat);
-    printf ("Dose overhead: %f seconds\n", time_dose_misc);
-}
-
-Plm_return_code
-Rt_plan::compute_plan ()
-{
-    if (!d_ptr->rt_parms) {
-        print_and_exit ("Error: cannot compute_plan without an Rt_parms\n");
-    }
-
-    if (d_ptr->output_dose_fn == "") {
-        print_and_exit ("Error: Output dose filename "
-            "not specified in configuration file!\n");
-    }
-    if (d_ptr->patient_fn == "") {
-        print_and_exit ("Error: Patient image "
-            "not specified in configuration file!\n");
-    }
+        Plm_image::Pointer dose = Plm_image::New();
+        dose->set_volume (dose_vol);
+        beam->set_dose(dose);
 
-    /* Load the patient CT image and save into the plan */
-    Plm_image::Pointer ct = Plm_image::New (d_ptr->patient_fn,
-        PLM_IMG_TYPE_ITK_FLOAT);
-    if (!ct) {
-        print_and_exit ("Error: Unable to load patient volume.\n");
+        printf ("Sigma conversion: %f seconds\n", time_sigma_conv);
+        printf ("Dose calculation: %f seconds\n", time_dose_calc);
+        printf ("Dose reformat: %f seconds\n", time_dose_reformat);
+        printf ("Dose overhead: %f seconds\n", time_dose_misc); fflush(stdout);
     }
-    this->set_patient (ct);
 
-#if defined (commentout_TODO)
-    /* Check if the target prescription or the peaks (SOBP) were set 
-       for all the beams */
-    for (int i = 0; i < d_ptr->beam_number; i++)
+    Plm_return_code
+        Rt_plan::compute_plan ()
     {
-        if (d_ptr->have_manual_peaks == true 
-            && d_ptr->have_prescription == true)
-        {
-            fprintf (stderr, "\n** ERROR beam %d: SOBP generation from prescribed distance and manual peaks insertion are incompatible. Please select only one of the two options.\n", d_ptr->beam_number);
-            return PLM_ERROR;
+        if (!d_ptr->rt_parms) {
+            print_and_exit ("Error: cannot compute_plan without an Rt_parms\n");
         }
-        if (d_ptr->have_manual_peaks == false && d_ptr->have_prescription == false && d_ptr->target_fn == "") {
-            fprintf (stderr, "\n** ERROR beam %d: No prescription made, please use the functions prescription_min & prescription_max, or manually created peaks .\n", d_ptr->beam_number);
-            return PLM_ERROR;
-        }
-    }
-#endif
-
-    this->print_verif ();
 
-    Volume::Pointer ct_vol = this->get_patient_volume ();
-    Volume::Pointer dose_vol = ct_vol->clone_empty ();
-            
-    plm_long dim[3] = {dose_vol->dim[0], dose_vol->dim[1], dose_vol->dim[2]};
-                
-    float* total_dose_img = (float*) dose_vol->img;
-
-    for (size_t i = 0; i < this->beam_storage.size(); i++)
-    {
-        printf ("\nStart dose calculation Beam %d\n", (int) i + 1);
-        this->beam = this->beam_storage[i];
+        if (d_ptr->output_dose_fn == "") {
+            print_and_exit ("Error: Output dose filename "
+                "not specified in configuration file!\n");
+        }
+        if (d_ptr->patient_fn == "") {
+            print_and_exit ("Error: Patient image "
+                "not specified in configuration file!\n");
+        }
 
-        /* try to generate plan with the provided parameters */
-        if (!this->init ()) {
-            print_and_exit ("ERROR: Unable to initilize plan.\n");
+        /* Load the patient CT image and save into the plan */
+        Plm_image::Pointer ct = Plm_image::New (d_ptr->patient_fn,
+            PLM_IMG_TYPE_ITK_FLOAT);
+        if (!ct) {
+            print_and_exit ("Error: Unable to load patient volume.\n");
         }
-		if (this->beam->get_range_compensator_in() !="")
-		{
-			Plm_image::Pointer rgc = Plm_image::New (this->beam->get_range_compensator_in(), PLM_IMG_TYPE_ITK_FLOAT);
-			this->beam->get_aperture()->set_range_compensator_image(this->beam->get_range_compensator_in().c_str());
-			this->beam->get_aperture()->set_range_compensator_volume(rgc->get_volume_float());
-		}
-		else
-		{
-			/* handle auto-generated beam modifiers */
-			if (d_ptr->target_fn != "") {
-				printf ("Target fn = %s\n", d_ptr->target_fn.c_str());
-				this->set_target (d_ptr->target_fn);
-				this->beam->compute_beam_modifiers ();
-				this->beam->apply_beam_modifiers ();
-			}
-		}
-	
-        /* generate depth dose curve, might be manual peaks or 
-           optimized based on prescription, or automatic based on target */
+        this->set_patient (ct);
+        this->print_verif ();
 
-        /* Extension of the limits of the PTV - add margins */
+        Volume::Pointer ct_vol = this->get_patient_volume ();
+        Volume::Pointer dose_vol = ct_vol->clone_empty ();
+        plm_long dim[3] = {dose_vol->dim[0], dose_vol->dim[1], dose_vol->dim[2]};
+        float* total_dose_img = (float*) dose_vol->img;
 
-        this->beam->set_proximal_margin (this->beam->get_proximal_margin());
-        /* MDFIX: is it twice the same operation?? */
-        this->beam->set_distal_margin (this->beam->get_distal_margin());
+        for (size_t i = 0; i < d_ptr->beam_storage.size(); i++)
+        {
+            printf ("\nStart dose calculation Beam %d\n", (int) i + 1);
+            Rt_beam *beam = d_ptr->beam_storage[i];
 
-		if ((this->beam->get_have_copied_peaks() == false && this->beam->get_have_prescription() == false && d_ptr->target_fn == "")||(this->beam->get_have_manual_peaks() == true)) {
-            /* Manually specified, so do not optimize */
-            if (!this->beam->generate ()) {
-                return PLM_ERROR;
+            /* try to generate plan with the provided parameters */
+            if (!this->prepare_beam_for_calc (beam)) {
+                print_and_exit ("ERROR: Unable to initilize plan.\n");
+            }
+            /* Compute beam modifiers, SOBP etc. according to the teatment strategy */
+            beam->compute_prerequisites_beam_tools(this->get_target());
+            /*
+              if (beam->get_beam_line_type() == "passive")
+              {
+              /* handle auto-generated beam modifiers
+              if (d_ptr->target_fn != "") {
+              printf ("Target fn = %s\n", d_ptr->target_fn.c_str());
+              this->set_target (d_ptr->target_fn);
+              beam->compute_beam_modifiers(this->get_target()->get_vol());
+              }
+	
+              /* generate depth dose curve, might be manual peaks or 
+              optimized based on prescription, or automatic based on target
+
+              if ((beam->get_mebs()->get_have_copied_peaks() == false && beam->get_mebs()->get_have_prescription() == false && d_ptr->target_fn == "")||(beam->get_mebs()->get_have_manual_peaks() == true)) {
+		
+              /* Manually specified, so do not optimize
+              if (!beam->get_mebs()->generate ()) {
+              return PLM_ERROR;
+              }
+              } 
+              else if (d_ptr->target_fn != "" && !beam->get_mebs()->get_have_prescription()) {
+              /* Optimize based on target volume
+              Rpl_volume *rpl_vol = beam->rpl_vol;
+              beam->get_mebs()->set_prescription(rpl_vol->get_min_wed() - beam->get_mebs()->get_proximal_margin(), rpl_vol->get_max_wed() + beam->get_mebs()->get_distal_margin());
+              beam->get_mebs()->optimize_sobp ();
+              } else {
+              /* Optimize based on manually specified range and modulation
+              beam->get_mebs()->optimize_sobp ();
+              }
+			
+              /* compute the pencil beam spot matrix for passive beams
+              beam->get_mebs()->initialize_and_compute_particle_number_matrix_passive(beam->get_aperture());
+              }
+              else // active
+              {
+              // to be computed
+
+              /* Compute the aperture and wed matrices
+              if (beam->get_mebs()->get_have_particle_number_map() == false)
+              {
+              /* we extract the max and min energies to cover the target/prescription
+              beam->compute_beam_modifiers(
+              beam->get_mebs()->compute_particle_number_matrix_from_target_active(beam->rpl_vol, beam->get_target(), beam->get_aperture());
+              }
+              else // spot map exists as a txt file
+              {
+              beam->get_mebs()->initialize_and_read_particle_number_matrix_active(beam->get_aperture());
+              }
+              } */
+
+            /* Generate dose */
+            this->set_debug (true);
+            this->compute_dose (beam);
+
+            /* Save beam modifiers */
+            if (beam->get_aperture_out() != "") {
+                Rpl_volume *rpl_vol = beam->rpl_vol;
+                Plm_image::Pointer& ap = rpl_vol->get_aperture()->get_aperture_image();
+                ap->save_image (beam->get_aperture_out().c_str());
             }
-        } else if (d_ptr->target_fn != "" && !this->beam->get_have_prescription()) {
-            /* Optimize based on target volume */
-            Rpl_volume *rpl_vol = this->beam->rpl_vol;
-            this->beam->set_sobp_prescription_min_max (
-                rpl_vol->get_min_wed(), rpl_vol->get_max_wed());
-            this->beam->optimize_sobp ();
-        } else {
-            /* Optimize based on manually specified range and modulation */
-            this->beam->set_sobp_prescription_min_max (
-                this->beam->get_prescription_min(), this->beam->get_prescription_max());
-            this->beam->optimize_sobp ();
-        }
 
-        /* Generate dose */
-        this->set_debug (true);
-        this->compute_dose ();
+            if (beam->get_range_compensator_out() != "" && beam->get_beam_line_type() == "passive") {
+                Rpl_volume *rpl_vol = beam->rpl_vol;
+                Plm_image::Pointer& rc = rpl_vol->get_aperture()->get_range_compensator_image();
+                rc->save_image (beam->get_range_compensator_out().c_str());
+            }
 
-        /* Save beam modifiers */
-        if (this->beam->get_aperture_out() != "") {
-            Rpl_volume *rpl_vol = this->beam->rpl_vol;
-            Plm_image::Pointer& ap = rpl_vol->get_aperture()->get_aperture_image();
-            ap->save_image (this->beam->get_aperture_out().c_str());
-        }
+            /* Save projected density volume */
+            if (d_ptr->output_proj_img_fn != "") {
+                Rpl_volume* proj_img = beam->rpl_ct_vol_HU;
+                if (proj_img) {
+                    proj_img->save (beam->get_proj_img_out());
+                }
+            }
 
-        if (this->beam->get_range_compensator_out() != "") {
-            Rpl_volume *rpl_vol = this->beam->rpl_vol;
-            Plm_image::Pointer& rc = rpl_vol->get_aperture()->get_range_compensator_image();
-            rc->save_image (this->beam->get_range_compensator_out().c_str());
-        }
+            /* Save projected dose volume */
+            if (beam->get_proj_dose_out() != "") {
+                Rpl_volume* proj_dose = beam->rpl_dose_vol;
+                if (proj_dose) {
+                    proj_dose->save (beam->get_proj_dose_out());
+                }
+            }
 
-        /* Save projected density volume */
-        if (d_ptr->output_proj_img_fn != "") {
-            Rpl_volume* proj_img = this->beam->rpl_ct_vol_HU;
-            if (proj_img) {
-                proj_img->save (this->beam->get_proj_img_out().c_str());
+            /* Save sigma volume */
+            if (beam->get_sigma_out() != "") {
+                Rpl_volume* sigma_img = beam->sigma_vol;
+                if (sigma_img) {
+                    sigma_img->save (beam->get_sigma_out());
+                }
             }
-        }
 
-        /* Save projected dose volume */
-        if (this->beam->get_proj_dose_out() != "") {
-            Rpl_volume* proj_dose = this->beam->rpl_dose_vol;
-            if (proj_dose) {
-                proj_dose->save (this->beam->get_proj_dose_out().c_str());
+            /* Save wed volume */
+            if (beam->get_wed_out() != "") {
+                Rpl_volume* rpl_vol = beam->rpl_vol;
+                if (rpl_vol) {
+                    rpl_vol->save (beam->get_wed_out());
+                }
             }
-        }
 
-        /* Save sigma volume */
-        if (this->beam->get_sigma_out() != "") {
-            Rpl_volume* sigma_img = this->beam->sigma_vol;
-            if (sigma_img) {
-                sigma_img->save (this->beam->get_sigma_out().c_str());
+            /* Save the spot map */
+            if (beam->get_mebs()->get_particle_number_out() != "") {
+                beam->get_mebs()->export_spot_map_as_txt(beam->get_aperture());
             }
-        }
 
-        /* Save wed volume */
-        if (this->beam->get_wed_out() != "") {
-            Rpl_volume* rpl_vol = this->beam->rpl_vol;
-            if (rpl_vol) {
-                rpl_vol->save (this->beam->get_wed_out().c_str());
+            float* beam_dose_img = (float*) d_ptr->beam_storage[i]->get_dose()->get_volume()->img;
+
+            /* Dose cumulation to the plan dose volume */
+            for (int j = 0; j < dim[0] * dim[1] * dim[2]; j++)
+            {
+                total_dose_img[j] += beam_dose_img[j];
             }
         }
 
-        float* beam_dose_img = (float*) this->beam_storage[i]->get_dose()->get_volume()->img;
+        /* Save dose output */
+        Plm_image::Pointer dose = Plm_image::New();
+        dose->set_volume (dose_vol);
+        this->set_dose(dose);
+        this->get_dose()->save_image (d_ptr->output_dose_fn.c_str());
 
-        /* Dose cumulation to the plan dose volume */
-        for (int j = 0; j < dim[0] * dim[1] * dim[2]; j++)
-        {
-			total_dose_img[j] += beam->get_beam_weight() * beam_dose_img[j];
-        }
+        printf ("done.  \n\n");
+        return PLM_SUCCESS;
     }
-    /* Dose max */
-    double dose_maxmax =0;
-    for (int j = 0; j < dim[0] * dim[1] * dim[2]; j++)
+
+    Plm_image::Pointer
+        Rt_plan::get_dose ()
     {
-        if (total_dose_img[j] > dose_maxmax)
-        {
-            dose_maxmax = total_dose_img[j];
-        }
+        return d_ptr->dose;
     }
 
-    printf("\n dose max: %lg\n", dose_maxmax);
-
-    /* Save dose output */
-
-    Plm_image::Pointer dose = Plm_image::New();
-    dose->set_volume (dose_vol);
-    this->set_dose(dose);
-    this->get_dose()->save_image (d_ptr->output_dose_fn.c_str());
-
-    printf ("done.  \n\n");
-    return PLM_SUCCESS;
-}
-
-Plm_image::Pointer
-Rt_plan::get_dose ()
-{
-    return d_ptr->dose;
-}
-
-FloatImageType::Pointer
-Rt_plan::get_dose_itk ()
-{
-    return d_ptr->dose->itk_float();
-}
+    FloatImageType::Pointer
+        Rt_plan::get_dose_itk ()
+    {
+        return d_ptr->dose->itk_float();
+    }
 
-void 
-Rt_plan::set_output_dose (const std::string& output_dose_fn)
-{
-    d_ptr->output_dose_fn = output_dose_fn;
-}
+    void 
+        Rt_plan::set_output_dose (const std::string& output_dose_fn)
+    {
+        d_ptr->output_dose_fn = output_dose_fn;
+    }
 
-void 
-Rt_plan::set_dose(Plm_image::Pointer& dose)
-{
-    d_ptr->dose = dose;
-}
+    void 
+        Rt_plan::set_dose(Plm_image::Pointer& dose)
+    {
+        d_ptr->dose = dose;
+    }
 
-static inline void
-display_progress (
-    float is,
-    float of
-) 
-{
+    static inline void
+        display_progress (
+            float is,
+            float of
+        ) 
+    {
 #if defined (PROGRESS)
-    printf (" [%3i%%]\b\b\b\b\b\b\b",
-           (int)floorf((is/of)*100.0f));
-    fflush (stdout);
+        printf (" [%3i%%]\b\b\b\b\b\b\b",
+            (int)floorf((is/of)*100.0f));
+        fflush (stdout);
 #endif
-}
-
-void
-Rt_plan::print_verif ()
-{
-    printf("\n [PLAN]");
-    printf("\n patient : %s", d_ptr->patient_fn.c_str());
-    printf("\n target : %s", d_ptr->target_fn.c_str());
-    printf("\n dose_out : %s", d_ptr->output_dose_fn.c_str());
-    printf("\n debug : %d", this->get_debug());
-    printf("\n dose norm : %lg", this->get_normalization_dose());
-
-    printf("\n \n [SETTINGS]");
-    int num_beams = this->beam_storage.size();
-    printf("\n flavor: "); for (int i = 0; i < num_beams; i++) {printf("%c ** ", this->beam_storage[i]->get_flavor());}
-    printf("\n homo_approx: "); for (int i = 0; i < num_beams; i++) {printf("%c ** ", this->beam_storage[i]->get_homo_approx());}
-    printf("\n ray_step: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", this->beam_storage[i]->get_step_length());}
-    printf("\n aperture_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", this->beam_storage[i]->get_aperture_out().c_str());}
-    printf("\n proj_dose_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", this->beam_storage[i]->get_proj_dose_out().c_str());}
-    printf("\n proj_img_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", this->beam_storage[i]->get_proj_img_out().c_str());}
-    printf("\n range_comp_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", this->beam_storage[i]->get_range_compensator_out().c_str());}
-    printf("\n sigma_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", this->beam_storage[i]->get_sigma_out().c_str());}
-    printf("\n wed_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", this->beam_storage[i]->get_wed_out().c_str());}
-    printf("\n part_type: "); for (int i = 0; i < num_beams; i++) {printf("%d ** ", this->beam_storage[i]->get_particle_type());}
-    printf("\n detail: "); for (int i = 0; i < num_beams; i++) {printf("%d ** ", this->beam_storage[i]->get_detail());}
-    printf("\n beam_weight: "); for (int i = 0; i < num_beams; i++) {printf("%g ** ", this->beam_storage[i]->get_beam_weight());}
-    //printf("\n max_depth: "); for (int i = 0; i < num_beams; i++) { printf("P%d %d",i, this->beam_storage[i]->get_sobp()->get_num_peaks()); for (int j = 0; j < this->beam_storage[i]->get_sobp()->get_num_peaks(); j++) { printf(" %lg ** ", this->beam_storage[i]->get_sobp()->get_depth_dose()[j]->dmax);}}
-    //printf("\n depth_res: "); for (int i = 0; i < num_beams; i++) { printf("P%d ",i); for (int j = 0; j < this->beam_storage[i]->get_sobp()->get_num_peaks(); j++) { printf("%lg ** ", this->beam_storage[i]->get_sobp()->get_depth_dose()[j]->dres);}}
-
-    printf("\n \n [GEOMETRY & APERTURE]");
-    printf("\n source: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg %lg ** ", this->beam_storage[i]->get_source_position()[0], this->beam_storage[i]->get_source_position()[1], this->beam_storage[i]->get_source_position()[2]);}
-    printf("\n isocenter: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg %lg ** ", this->beam_storage[i]->get_isocenter_position()[0], this->beam_storage[i]->get_isocenter_position()[1], this->beam_storage[i]->get_isocenter_position()[2]);}
-    printf("\n vup: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg %lg ** ", this->beam_storage[i]->get_aperture()->vup[0], this->beam_storage[i]->get_aperture()->vup[1], this->beam_storage[i]->get_aperture()->vup[2]);}
-    printf("\n offset: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", this->beam_storage[i]->get_aperture()->get_distance());}
-    printf("\n ap_origin: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg ** ", this->beam_storage[i]->get_aperture()->get_center()[0], this->beam_storage[i]->get_aperture()->get_center()[1]);}
-    printf("\n i_res: "); for (int i = 0; i < num_beams; i++) {printf("%d %d ** ", this->beam_storage[i]->get_aperture()->get_dim()[0], this->beam_storage[i]->get_aperture()->get_dim()[1]);}
-    printf("\n spacing: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg ** ", this->beam_storage[i]->get_aperture()->get_spacing()[0], this->beam_storage[i]->get_aperture()->get_spacing()[1]);}
-    printf("\n source_size: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", this->beam_storage[i]->get_source_size());}
-    printf("\n ap_file_in: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", this->beam_storage[i]->get_aperture_in().c_str());}
-    printf("\n rc_file_in: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", this->beam_storage[i]->get_range_compensator_in().c_str());}
-    printf("\n smearing: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", this->beam_storage[i]->get_smearing());}
-    printf("\n prox_margin: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", this->beam_storage[i]->get_proximal_margin());}
-    printf("\n dist_margin: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", this->beam_storage[i]->get_distal_margin());}
-
-    printf("\n \n [PEAK]");
-    printf("\n E0: "); for (int i = 0; i < num_beams; i++) { printf("P%d ",i); for (int j = 0; j < this->beam_storage[i]->get_sobp()->get_num_peaks(); j++) { printf("%lg ** ", this->beam_storage[i]->get_sobp()->get_depth_dose()[j]->E0);}}
-    printf("\n spread: "); for (int i = 0; i < num_beams; i++) { printf("P%d ",i); for (int j = 0; j < this->beam_storage[i]->get_sobp()->get_depth_dose().size(); j++) { printf("%lg ** ", this->beam_storage[i]->get_sobp()->get_depth_dose()[j]->spread);}}
-    printf("\n weight: "); for (int i = 0; i < num_beams; i++) { printf("P%d ",i); for (int j = 0; j < this->beam_storage[i]->get_sobp()->get_depth_dose().size(); j++) { printf("%lg ** ", this->beam_storage[i]->get_sobp()->get_depth_dose()[j]->weight);}}
-
-    printf("\n \n [PHOTON_ENERGY]");
-    printf("\n photon energy: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", this->beam_storage[i]->get_photon_energy());}
-}
+    }
 
+    void
+        Rt_plan::print_verif ()
+    {
+        printf("\n [PLAN]");
+        printf("\n patient : %s", d_ptr->patient_fn.c_str());
+        printf("\n target : %s", d_ptr->target_fn.c_str());
+        printf("\n dose_out : %s", d_ptr->output_dose_fn.c_str());
+        printf("\n debug : %d", this->get_debug());
+        printf("\n dose norm : %lg", this->get_normalization_dose());
+
+        printf("\n \n [SETTINGS]");
+        int num_beams = d_ptr->beam_storage.size();
+        printf("\n flavor: "); for (int i = 0; i < num_beams; i++) {printf("%c ** ", d_ptr->beam_storage[i]->get_flavor());}
+        printf("\n homo_approx: "); for (int i = 0; i < num_beams; i++) {printf("%c ** ", d_ptr->beam_storage[i]->get_homo_approx());}
+        printf("\n ray_step: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", d_ptr->beam_storage[i]->get_step_length());}
+        printf("\n aperture_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", d_ptr->beam_storage[i]->get_aperture_out().c_str());}
+        printf("\n proj_dose_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", d_ptr->beam_storage[i]->get_proj_dose_out().c_str());}
+        printf("\n proj_img_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", d_ptr->beam_storage[i]->get_proj_img_out().c_str());}
+        printf("\n range_comp_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", d_ptr->beam_storage[i]->get_range_compensator_out().c_str());}
+        printf("\n sigma_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", d_ptr->beam_storage[i]->get_sigma_out().c_str());}
+        printf("\n wed_out: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", d_ptr->beam_storage[i]->get_wed_out().c_str());}
+        printf("\n beam_weight: "); for (int i = 0; i < num_beams; i++) {printf("%g ** ", d_ptr->beam_storage[i]->get_beam_weight());}
+
+        printf("\n \n [GEOMETRY & APERTURE]");
+        printf("\n source: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg %lg ** ", d_ptr->beam_storage[i]->get_source_position()[0], d_ptr->beam_storage[i]->get_source_position()[1], d_ptr->beam_storage[i]->get_source_position()[2]);}
+        printf("\n isocenter: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg %lg ** ", d_ptr->beam_storage[i]->get_isocenter_position()[0], d_ptr->beam_storage[i]->get_isocenter_position()[1], d_ptr->beam_storage[i]->get_isocenter_position()[2]);}
+        printf("\n vup: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg %lg ** ", d_ptr->beam_storage[i]->get_aperture()->vup[0], d_ptr->beam_storage[i]->get_aperture()->vup[1], d_ptr->beam_storage[i]->get_aperture()->vup[2]);}
+        printf("\n offset: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", d_ptr->beam_storage[i]->get_aperture()->get_distance());}
+        printf("\n ap_center in pixels: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg ** ", d_ptr->beam_storage[i]->get_aperture()->get_center()[0], d_ptr->beam_storage[i]->get_aperture()->get_center()[1]);}
+        printf("\n i_res: "); for (int i = 0; i < num_beams; i++) {printf("%d %d ** ", d_ptr->beam_storage[i]->get_aperture()->get_dim()[0], d_ptr->beam_storage[i]->get_aperture()->get_dim()[1]);}
+        printf("\n spacing: "); for (int i = 0; i < num_beams; i++) {printf("%lg %lg ** ", d_ptr->beam_storage[i]->get_aperture()->get_spacing()[0], d_ptr->beam_storage[i]->get_aperture()->get_spacing()[1]);}
+        printf("\n source_size: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", d_ptr->beam_storage[i]->get_source_size());}
+        printf("\n ap_file_in: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", d_ptr->beam_storage[i]->get_aperture_in().c_str());}
+        printf("\n rc_file_in: "); for (int i = 0; i < num_beams; i++) {printf("%s ** ", d_ptr->beam_storage[i]->get_range_compensator_in().c_str());}
+        printf("\n smearing: "); for (int i = 0; i < num_beams; i++) {printf("%lg ** ", d_ptr->beam_storage[i]->get_smearing());}
+
+        printf("\n \n [PEAK]");
+        printf("\n E0: "); for (int i = 0; i < num_beams; i++) { printf("P%d ",i); for (int j = 0; j < d_ptr->beam_storage[i]->get_mebs()->get_depth_dose().size(); j++) {printf("%lg ** ", d_ptr->beam_storage[i]->get_mebs()->get_depth_dose()[j]->E0);}}
+        printf("\n spread: "); for (int i = 0; i < num_beams; i++) { printf("P%d ",i); for (int j = 0; j < d_ptr->beam_storage[i]->get_mebs()->get_depth_dose().size(); j++) { printf("%lg ** ", d_ptr->beam_storage[i]->get_mebs()->get_depth_dose()[j]->spread);}}
+        printf("\n weight: "); for (int i = 0; i < num_beams; i++) { printf("P%d ",i); for (int j = 0; j < d_ptr->beam_storage[i]->get_mebs()->get_depth_dose().size(); j++) { printf("%lg ** ", d_ptr->beam_storage[i]->get_mebs()->get_weight()[j]);}}
+    }
diff --git a/src/plastimatch/dose/rt_plan.h b/src/plastimatch/dose/rt_plan.h
index 13586a6..3889d98 100644
--- a/src/plastimatch/dose/rt_plan.h
+++ b/src/plastimatch/dose/rt_plan.h
@@ -7,13 +7,13 @@
 #include "plmdose_config.h"
 #include "plm_image.h"
 #include "plm_return_code.h"
+#include "rt_beam.h"
 #include "smart_pointer.h"
 #include "threading.h"
 
 class Plm_image;
 class Proj_matrix;
 class Rpl_volume;
-class Rt_beam;
 class Rt_plan_private;
 class Rt_study;
 class Volume;
@@ -21,7 +21,6 @@ class Volume;
 class PLMDOSE_API Rt_plan {
 public:
     SMART_POINTER_SUPPORT (Rt_plan);
-public:
     Rt_plan_private *d_ptr;
 public:
     Rt_plan ();
@@ -30,8 +29,6 @@ public:
 public:
     Plm_return_code parse_args (int argc, char* argv[]);
 
-    bool init ();
-
     /* Set the CT volume for dose calculation.
        The Rt_plan takes ownership of this CT/Patient. */
     void set_patient (const std::string& patient_fn);
@@ -72,9 +69,32 @@ public:
     void set_normalization_dose (float normalization_dose);
     float get_normalization_dose ();
 
+    /* Get the position of the beam isocenter in world coordinates. */
+    const float* get_ref_dose_point () const;
+    /* Get the x, y, or z coordinate of the beam source 
+      in world coordinates. */
+    float get_ref_dose_point (int dim) const;
+    /* Set the position of the beam isocenter in world coordinates. */
+    void set_ref_dose_point (const float rdp[3]);
+    /* Set the position of the beam isocenter in world coordinates. */
+    void set_ref_dose_point (const double rdp[3]);
+
+	/* Set / Get the declaration of the normalization conditions*/
+	void set_have_ref_dose_point(bool have_rdp);
+	bool get_have_ref_dose_point();
+	void set_have_dose_norm(bool have_dose_norm);
+	bool get_have_dose_norm();
+
+	/*! \brief Get the "non normalized" dose option */
+    char get_non_norm_dose () const;
+    /*! \brief Set "non normalized" dose option */
+    void set_non_norm_dose (char non_norm_dose);
+
     /* Compute dose */
+    void propagate_target_to_beams ();
+    bool prepare_beam_for_calc (Rt_beam *beam);
+    void compute_dose (Rt_beam *beam);
     Plm_return_code compute_plan ();
-    void compute_dose ();
 
     /* Get outputs */
     Plm_image::Pointer get_dose ();
@@ -83,11 +103,6 @@ public:
     void set_dose(Plm_image::Pointer& dose);
 
     void print_verif ();
-
-public:
-
-    Rt_beam *beam;
-    std::vector<Rt_beam*> beam_storage;
 };
 
 #endif
diff --git a/src/plastimatch/dose/rt_sigma.cxx b/src/plastimatch/dose/rt_sigma.cxx
index 161a90c..d0afbe4 100644
--- a/src/plastimatch/dose/rt_sigma.cxx
+++ b/src/plastimatch/dose/rt_sigma.cxx
@@ -8,7 +8,13 @@
 #include "rt_lut.h"
 #include "rt_sigma.h"
 
-void compute_sigmas(Rt_plan* plan, float energy, float* sigma_max, std::string size, int* margins) //Rpl_volume* sigma_vol, Rpl_volume* ct_vol, float energy, float spacing_z, float* sigma_max)
+void compute_sigmas (
+    Rt_plan* plan,
+    const Rt_beam* beam,
+    float energy,
+    float* sigma_max, 
+	std::string size, 
+	int* margins)
 {
     /* We compute the sigmas for source, range compensator and patient as described in the Hong's paper */
     /* First we set the volume in which the sigmas have to be calculated: the normal rpl_sigma_volume,  */
@@ -20,46 +26,39 @@ void compute_sigmas(Rt_plan* plan, float energy, float* sigma_max, std::string s
 
     if (size == "small")
     {
-        sigma_vol = plan->beam->sigma_vol;
-        ct_vol = plan->beam->rpl_ct_vol_HU;
-        rgl_vol = plan->beam->rpl_vol;
-    }
-    else if (size == "large")
-    {
-        sigma_vol = plan->beam->sigma_vol_lg;
-        ct_vol = plan->beam->rpl_ct_vol_HU_lg;
-        rgl_vol = plan->beam->rpl_vol_lg;
+        sigma_vol = beam->sigma_vol;
+        ct_vol = beam->rpl_ct_vol_HU;
+        rgl_vol = beam->rpl_vol;
     }
     else
     {
-        printf("error: size of convert_radiologic_length-to-sigma must be \"small\" or \"large\" \n");
-        return;
+        sigma_vol = beam->sigma_vol_lg;
+        ct_vol = beam->rpl_ct_vol_HU_lg;
+        rgl_vol = beam->rpl_vol_lg;
     }
 
     /* Now that the volumes were defined, we can compute the sigmas in them and do the quadratic sigmas sum */
     /* sigma^2 patient */
-    compute_sigma_pt(sigma_vol, rgl_vol, ct_vol, plan, energy);
+    compute_sigma_pt (sigma_vol, rgl_vol, ct_vol, plan, beam, energy);
     /* + sigma^2 source */
-    if (plan->beam->get_source_size() > 0)
+    if (beam->get_source_size() > 0)
     {            
-        compute_sigma_source(sigma_vol, rgl_vol, plan, energy);
+        compute_sigma_source(sigma_vol, rgl_vol, plan, beam, energy);
     }
     else
     {
         printf("Sigma source computed - sigma_src_max = 0 mm. (Source size <= 0)\n");
-    }    
+    }
     /* + sigma^2 range compensator */
-	if (plan->beam->get_aperture()->have_range_compensator_image() && energy > 1)
+    if (beam->get_aperture()->have_range_compensator_image() && energy > 1)
     {            
-        compute_sigma_range_compensator(sigma_vol, rgl_vol, plan, energy, margins);
+        compute_sigma_range_compensator(sigma_vol, rgl_vol, plan, beam, energy, margins);
     }
     else
     {
         printf("Sigma range compensator computed - sigma_rc_max = 0 mm. (No range compensator or the energy is too small)\n");
     } 
-    /* Last step: sigma = sqrt(sigma_pt^2 + sigma_src^2 + sigma_rc^2), at this point sigma_vol contains the sum of the sigmas' square */
-	
-	/* We update also the value of sigma_max */
+    /* Last step: sigma = sqrt(sigma_pt^2 + sigma_src^2 + sigma_rc^2), at this point sigma_vol contains the sum of the sigmas' square */    /* We update also the value of sigma_max */
     float* sigma_img = (float*) sigma_vol->get_vol()->img;
     plm_long dim[3] = { 
         sigma_vol->get_vol()->dim[0], 
@@ -77,15 +76,20 @@ void compute_sigmas(Rt_plan* plan, float energy, float* sigma_max, std::string s
         }
     }
     printf("Global sigma computed - Global sigma_max = %lg mm.\n", *sigma_max);
-
     return;
 }
 
-void compute_sigma_pt(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rpl_volume* ct_vol, Rt_plan* plan, float energy)
+void compute_sigma_pt (
+    Rpl_volume* sigma_vol,
+    Rpl_volume* rpl_volume,
+    Rpl_volume* ct_vol,
+    Rt_plan* plan,
+    const Rt_beam* beam,
+    float energy)
 {
     float sigma_max = 0;
 
-    if (plan->beam->get_homo_approx() == 'y')
+    if (beam->get_homo_approx() == 'y')
     {
         sigma_max = compute_sigma_pt_homo(sigma_vol, rpl_volume, energy);
     }
@@ -93,87 +97,95 @@ void compute_sigma_pt(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rpl_volume*
     {
         sigma_max = compute_sigma_pt_hetero(sigma_vol, rpl_volume, ct_vol, energy);
     }
-
     printf("Sigma patient computed - sigma_pt_max = %lg mm.\n", sigma_max);
-	return;
+    return;
 }
 
-float compute_sigma_pt_homo(Rpl_volume* sigma_vol, Rpl_volume* rpl_vol, float energy)
+float compute_sigma_pt_homo (
+    Rpl_volume* sigma_vol,
+    Rpl_volume* rpl_vol,
+    float energy)
 {
     float sigma_max = 0;
-	int dim = sigma_vol->get_vol()->dim[0] * sigma_vol->get_vol()->dim[1] * sigma_vol->get_vol()->dim[2];
-	int dim_rpl = rpl_vol->get_vol()->dim[0] * rpl_vol->get_vol()->dim[1] * rpl_vol->get_vol()->dim[2];
-	if (dim != dim_rpl)
-	{
-		printf("Error: rpl_vol & sigma_vol have different dimensions. Sigma volume not built\n");
-		return 0;
-	}
+    const plm_long *dim = sigma_vol->get_vol()->dim;
+    const plm_long *dim_rpl = rpl_vol->get_vol()->dim;
+    int idx = 0;
 
+    if (dim[0] != dim_rpl[0] || dim[1] != dim_rpl[1] || dim[2] != dim_rpl[2])
+    {
+        printf("Error: rpl_vol & sigma_vol have different dimensions. Sigma volume not built\n");
+        return 0;
+    }
     /* At this time, sigma_vol contains the range length, WITHOUT range compensator */
     float* sigma_volume = (float*) sigma_vol->get_vol()->img;
-	float* rpl_img = (float*) rpl_vol->get_vol()->img;
-
-	unsigned char* ap_img = NULL;
-	
-	if (rpl_vol->get_aperture()->have_aperture_image())
-	{
-		ap_img = (unsigned char*) rpl_vol->get_aperture()->get_aperture_volume()->img;
-	}
+    float* rpl_img = (float*) rpl_vol->get_vol()->img;
+    unsigned char* ap_img = NULL;
 
     double x_over_range = 0;
-
+	
+    if (rpl_vol->get_aperture()->have_aperture_image())
+    {
+        ap_img = (unsigned char*) rpl_vol->get_aperture()->get_aperture_volume()->img;
+    } 
     /*  Hong method to calculate the sigma value for homogeneous medium */
     /* Range value in water extracted from a fit based on 1-250MeV from the NIST data - ranges in mm */
-    double range = 10 * getrange(energy);
+    double range = 10 * get_proton_range(energy);
     
     /* Sigma0 value from the Hong fit - See paper Hong "A pencil beam algorithm for proton dose calculation" - sigma in mm: x10 */
     double sigma0 = 0.02275 * range + 1.2085E-6 * range * range;
 
     /* Calculation of the sigma values from the medium equivalent depth  */
-    for (int i = 0; i < dim; i++)
+    for (int i = 0; i < dim[0] * dim [1]; i++)
     {
-        if (!rpl_vol->get_aperture()->have_aperture_image() || (rpl_vol->get_aperture()->have_aperture_image() && ap_img[i] > 0))
+        for (int k = 0; k < dim[2]; k++)
         {
-            if (rpl_img[i] <= 0) 
+            idx = k * dim[0] * dim[1] +i;
+            if (!rpl_vol->get_aperture()->have_aperture_image() || (rpl_vol->get_aperture()->have_aperture_image() && ap_img[i] > 0))
             {
-               sigma_volume[i] = 0;
-            }
-            else if (rpl_img[i] >= range)
-            {
-                sigma_volume[i] = sigma0 * sigma0; // sigma will contains the square of the sigmas to do the quadratic sum
-            
-               /* sigma_max update */
-                if (sigma0 > sigma_max)
+                if (rpl_img[idx] <= 0) 
                 {
-                   sigma_max = sigma0;
+                    sigma_volume[idx] = 0;
                 }
-            }
-            else
-            {
-                x_over_range = rpl_img[i] / range;
-
-                /* sigma = y0 * Hong (x/range) */
-                sigma_volume[i] = sigma0 * x_over_range * ( 0.26232 + 0.64298 * x_over_range + 0.0952393 * x_over_range * x_over_range);
-            
-                /* sigma_max update */
-                if (sigma_volume[i] > sigma_max)
+                else if (rpl_img[idx] >= range)
                 {
-                    sigma_max = sigma_volume[i];
+                    sigma_volume[idx] = sigma0 * sigma0; // sigma will contains the square of the sigmas to do the quadratic sum
+            
+                    /* sigma_max update */
+                    if (sigma0 > sigma_max)
+                    {
+                        sigma_max = sigma0;
+                    }
                 }
+                else
+                {
+                    x_over_range = rpl_img[idx] / range;
 
-                sigma_volume[i] *= sigma_volume[i]; // We return sigma^2 to sigma_vol
+                    /* sigma = y0 * Hong (x/range) */
+                    sigma_volume[idx] = sigma0 * x_over_range * ( 0.26232 + 0.64298 * x_over_range + 0.0952393 * x_over_range * x_over_range);
+            
+                    /* sigma_max update */
+                    if (sigma_volume[idx] > sigma_max)
+                    {
+                        sigma_max = sigma_volume[idx];
+                    }
+                    sigma_volume[idx] *= sigma_volume[idx]; // We return sigma^2 to sigma_vol
+                }
             }
         }
     }
     return sigma_max;
 }
 
-float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_volume* ct_vol, float energy)
+float compute_sigma_pt_hetero (
+    Rpl_volume* sigma_vol,
+    Rpl_volume* rgl_vol,
+    Rpl_volume* ct_vol,
+    float energy)
 {
     float sigma_max = 0;
 
     float* sigma_img = (float*) sigma_vol->get_vol()->img;
-	float* rpl_img = (float*) rgl_vol->get_vol()->img;
+    float* rpl_img = (float*) rgl_vol->get_vol()->img;
     float* ct_img = (float*) ct_vol->get_vol()->img;
     unsigned char* ap_img = 0;
     if (rgl_vol->get_aperture()->have_aperture_image())
@@ -191,17 +203,18 @@ float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_vo
     float spacing = sigma_vol->get_vol()->spacing[2]/10; // in cm to correspond to the Highland formula
 
     float E = energy;
-    float mc2 = 939.4f;                       /* proton mass at rest (MeV) */
-    float c = 299792458.0f;                   /* speed of light (m/s2) */
-    float p = 0.0;                            /* Proton momentum (passed in) */
-    float v = 0.0;                            /* Proton velocity (passed in) */
-    float function_to_be_integrated;          /* right term to be integrated in the Highland equation */
-    float inverse_rad_length_integrated = 0;  /* and left part */
+	float mc2 = (float) PROTON_REST_MASS;		/* proton mass at rest (MeV) */
+    float c = (float) LIGHT_SPEED;						/* speed of light (m/s) */
+    float p = 0.0;								/* Proton momentum (passed in) */
+    float v = 0.0;								/* Proton velocity (passed in) */
+    float function_to_be_integrated;			/* right term to be integrated in the Highland equation */
+    float inverse_rad_length_integrated = 0;	/* and left part */
 
     float sum = 0.0;		                      /* integration expressions, right part of equation */
     float POI_depth = 0.0;                          /* depth of the point of interest (where is calculated the sigma value)in cm - centered at the pixel center */
     float pixel_depth = 0.0;                        /* depth of the contributing pixel to total sigma (in cm) - center between 2 pixels, the difference in rglength comes from the center of the previous pixel to the center of this pixel */
     float step = 0.0;                               /* step of integration, will depends on the radiologic length */
+
     /* initializiation of all the rays on which the integration will be done */
     printf ("sigma_img: %d %d %d\n", (int) sigma_vol->get_vol()->dim[0], 
         (int) sigma_vol->get_vol()->dim[1], (int) sigma_vol->get_vol()->dim[2]);
@@ -217,11 +230,10 @@ float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_vo
                 idx = dim[0]*dim[1]*s + apert_idx;
                 range_length_ray[s] = rpl_img[idx];   // at this point sigma is still a range_length volume without range compensator
                 sigma_ray[s] = 0;
-				HU_ray[s] = ct_img[idx];           // the density ray is initialized with density
+                HU_ray[s] = ct_img[idx];           // the density ray is initialized with density
             }
     
             //Now we can compute the sigma rays!!!!
-  
             /* Step 1: the sigma is filled with zeros, so we let them = 0 as long as rg_length is 0, meaning the ray is out of the physical volume */
             /* we mark the first pixel in the volume, and the calculations will start with this one */
       
@@ -245,7 +257,7 @@ float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_vo
             std::vector<double> stop_cache (dim[2], 0);
 
             E = energy; // we set the energy of the particles to the nominal energy for this ray
-      
+
             for (int s = first_non_null_loc; s < dim[2]; s++)
             {
                 p = sqrt(2*E*mc2+E*E)/c; // in MeV.s.m-1
@@ -253,7 +265,7 @@ float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_vo
                 pv_cache[s] = p * v;
 
                 inv_rad_len[s] = 1.0f / compute_X0_from_HU(HU_ray[s]);
-				stop_cache[s] = compute_PrSTPR_from_HU(HU_ray[s]) * getstop(E); // dE/dx_mat = dE /dx_watter * STPR (lut in g/cm2)
+                stop_cache[s] = compute_PrSTPR_from_HU(HU_ray[s]) * get_proton_stop(E); // dE/dx_mat = dE /dx_watter * STPR (lut in g/cm2)
 
                 sum = 0;
                 inverse_rad_length_integrated = 0;
@@ -277,7 +289,6 @@ float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_vo
               
                     function_to_be_integrated = pow(((POI_depth - pixel_depth)/pv_cache[t]),2) * inv_rad_len[t]; //i in cm
                     sum += function_to_be_integrated*step;
-
                     inverse_rad_length_integrated += step * inv_rad_len[t];
   
                     /* energy is updated after passing through dz */
@@ -310,7 +321,6 @@ float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_vo
                 {
                     sigma_max = sigma_ray[s];
                 }
-
                 sigma_img[dim[0]*dim[1]*s + apert_idx] = sigma_ray[s] * sigma_ray[s]; // We want to have sigma^2 to make a quadratic sum of the sigmas            
             }
         }
@@ -318,14 +328,12 @@ float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_vo
     return sigma_max;
 }
 
-void compute_sigma_source(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan* plan, float energy)
+void compute_sigma_source (Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan* plan, const Rt_beam *beam, float energy)
 {
     /* Method of the Hong's algorithm - See Hong's paper */
-
     float* sigma_img = (float*) sigma_vol->get_vol()->img;
     float* rgl_img = (float*) rpl_volume->get_vol()->img;
-
-    unsigned char* ap_img = (unsigned char*) plan->beam->get_aperture()->get_aperture_volume()->img;
+    unsigned char* ap_img = (unsigned char*) beam->get_aperture()->get_aperture_volume()->img;
 
     int idx = 0;
     double proj = 0; // projection factor of the ray on the z axis
@@ -335,12 +343,11 @@ void compute_sigma_source(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan
 
     /* MD Fix: Why plan->ap->nrm is incorrect at this point??? */
     double nrm[3] = {0,0,0};
-    vec3_sub3(nrm, plan->beam->get_source_position(), plan->beam->get_isocenter_position());
+    vec3_sub3(nrm, beam->get_source_position(), beam->get_isocenter_position());
     vec3_normalize1(nrm);
 
     plm_long dim[3] = { sigma_vol->get_vol()->dim[0], sigma_vol->get_vol()->dim[1], sigma_vol->get_vol()->dim[2]};
-
-    float range = getrange(energy);
+    float range = get_proton_range(energy);
 
     for (int i = 0; i < dim[0] * dim[1]; i++)
     {
@@ -348,14 +355,14 @@ void compute_sigma_source(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan
         {
             Ray_data* ray_data = &sigma_vol->get_Ray_data()[i];
             proj = -vec3_dot(ray_data->ray, nrm);
-            dist_cp = vec3_dist(ray_data->cp,plan->beam->get_source_position());
+            dist_cp = vec3_dist(ray_data->cp,beam->get_source_position());
 
             for (int j = 0; j < dim[2]; j++)
             {
                 if ( rgl_img[idx] < range +10) // +10 because we calculate sigma_src a little bit farther after the range to be sure (+1 cm)
                 {
                     idx = dim[0]*dim[1]*j + i;
-                    sigma = plan->beam->get_source_size() * ((dist_cp + proj * (float) j * sigma_vol->get_vol()->spacing[2])/ plan->beam->get_aperture()->get_distance() - 1);
+                    sigma = beam->get_source_size() * ((dist_cp + proj * (float) j * sigma_vol->get_vol()->spacing[2])/ beam->get_aperture()->get_distance() - 1);
                     sigma_img[idx] += sigma * sigma; // we return the square of sigma_source for the quadratic sum, added to the sigma patient already contained in the sigma volume
                     /* We update sigma_max */
                     if (sigma_max < sigma)
@@ -374,43 +381,42 @@ void compute_sigma_source(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan
 	return;
 }
 
-void compute_sigma_range_compensator(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan* plan, float energy, int* margins)
+void compute_sigma_range_compensator(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan* plan, const Rt_beam *beam, float energy, int* margins)
 {
-    /* Method of the Hong's algorithm - See Hong's paper */
+	/* There are two methods for computing the beam spread due to a range compensator */
+    /* Method1 rc_MC_model:  Hong's algorithm (Highland)- See Hong's paper */
+	/* Method2:  model built on Monte Carlo simulations - Paper still unpublished */
     /* The range compensator is supposed to be made of lucite and placed right after the aperture*/
-    /* sigma = sigma_srm * (zPOI - zmax_rgc + rgc - rgc_effective) */
-    
+	
     if (energy < 1)
     {
         printf("Sigma range compensator = 0 mm, the energy is too small (<1 MeV).\n");
         return;
     }
-
     /* Range value in lucite extracted from a fit based on 1-250MeV from the NIST data - ranges in mm */
-    double range = 10 * getrange(energy);
-    
-    /* lucite sigma0 (in rads) computing- From the figure A2 of the Hong's paper (be careful, in this paper the fit shows sigma0^2)*/
-    double sigma0;
-    if (range > 150)
-    {
-        sigma0 = 0.05464 + 5.8348E-6 * range -5.21006E-9 * range * range;
-    }
-    else 
-    {
-        sigma0 = 0.05394 + 1.80222E-5 * range -5.5430E-8 * range * range;;
-    }
-	sigma0 = sigma0/0.915; // MD Fix... works only the hong's test
+    double range = 10 * get_proton_range((double) energy);
+
+	/* Theta0 computation */
+	double theta0 = 0;
+
+	if (beam->get_rc_MC_model() != 'y')
+	{
+			theta0 = get_theta0_Highland(range);
+	}
+	else
+	{
+			theta0 = get_theta0_MC(energy);
+	}
 
     /* sigma calculation and length computations */
-    
     float* sigma_img = (float*) sigma_vol->get_vol()->img;
     float* rgl_img = (float*) rpl_volume->get_vol()->img;
-    float* rc_img = (float*) plan->beam->get_aperture()->get_range_compensator_volume ()->img;
+    float* rc_img = (float*) beam->get_aperture()->get_range_compensator_volume ()->img;
 
     unsigned char* ap_img = 0;
     if (rpl_volume->get_aperture()->have_aperture_image())
     {
-        ap_img = (unsigned char*) plan->beam->get_aperture()->get_aperture_volume()->img;
+        ap_img = (unsigned char*) beam->get_aperture()->get_aperture_volume()->img;
     }
     plm_long dim[3] = { 
         sigma_vol->get_vol()->dim[0], 
@@ -426,53 +432,62 @@ void compute_sigma_range_compensator(Rpl_volume* sigma_vol, Rpl_volume* rpl_volu
 
     double rc_over_range;
     double rc_eff;
-    double sigma_srm;
+    double theta_srm;
     double sigma_max = 0;
 
-    float z_POI;
-    float z_eff;
+    float POI;
+    float l_eff;
     double sigma;
-
-    /* MD Fix: Why plan->ap->nrm is incorrect at this point??? */
     double nrm[3] = {0,0,0};
-
-    if (margins[0] == 0 && margins[1] == 0)
+		
+	/* MD Fix: Why plan->ap->nrm is incorrect at this point??? */
+    vec3_sub3(nrm, beam->get_source_position(), beam->get_isocenter_position());
+    vec3_normalize1(nrm);
+	
+    if (margins[0] == 0 && margins[1] == 0 || beam->get_flavor() != 'h')
     {
         for (int i = 0; i < dim[0] * dim[1]; i++)
         {
             /* calculation of sigma_srm, see graph A3 from the Hong's paper */
 			if (!rpl_volume->get_aperture()->have_aperture_image() || (ap_img && ap_img[i] > 0))
             {
-                rc_over_range = rc_img[i] *1.19 *0.98 / range; // energy is >1, so range > 0 (range is in water: rho * WER)
+                Ray_data* ray_data = &sigma_vol->get_Ray_data()[i];
+	        
+                proj = -vec3_dot(ray_data->ray, nrm);
+				if (proj == 0) 
+				{ 
+					printf("error: some rays are perpendicular to the beam axis \n");
+					return;
+				}
+
+                dist_cp = vec3_dist(ray_data->cp, beam->get_source_position());
+                rc_over_range =  rc_img[i] / proj * PMMA_DENSITY * PMMA_STPR / range; // energy is >1, so range > 0 (range is in water: rho * WER)
 	
                 if (rc_over_range < 1)
                 {
-                    sigma_srm = sigma0 * rc_over_range * ( 1.6047 -2.7295 * rc_over_range + 2.1578 * rc_over_range * rc_over_range);
-
-                    /* calculation of rc_eff - see Hong's paper graph A3 - linear interpolation of the curve */
-                    rc_eff = get_rc_eff(rc_over_range);
-        
-                    Ray_data* ray_data = &sigma_vol->get_Ray_data()[i];
-	        
-                    /* MD Fix: Why plan->ap->nrm is incorrect at this point??? */
-                    vec3_sub3(nrm, plan->beam->get_source_position(), plan->beam->get_isocenter_position());
-                    vec3_normalize1(nrm);
-	        
-                    proj = -vec3_dot(ray_data->ray, nrm);
-                    dist_cp = vec3_dist(ray_data->cp, plan->beam->get_source_position()) * proj;
+					if (beam->get_rc_MC_model() != 'y')
+					{
+						theta_srm =  theta0 * get_theta_rel_Highland(rc_over_range);
+						rc_eff = get_scat_or_Highland(rc_over_range) * rc_img[i];
+					}
+					else
+					{
+						theta_srm =  theta0 * get_theta_rel_MC(rc_over_range);
+						rc_eff = get_scat_or_MC(rc_over_range) * rc_img[i];
+					}
 	
                     for (int j = 0; j < dim[2]; j++)
                     {
                         idx = dim[0]*dim[1]*j + i;
                         if ( rgl_img[idx] < range +10) // +10 because we calculate sigma_rg_compensator a little bit farther after the range to be sure (+1 cm)
                         {
-                            z_POI = proj * (dist_cp + (float) j * sigma_vol->get_vol()->spacing[2]);
-                            z_eff = plan->beam->get_aperture()->get_distance() + proj * rc_eff;
+                            POI = dist_cp + (float) j * sigma_vol->get_vol()->spacing[2] - beam->get_aperture()->get_distance() / proj;
+                            l_eff = rc_eff * proj;
 	            
-                            /* sigma = sigma_srm * (z_POI- z_eff) */
-                            if (z_POI - z_eff >= 0)
+                            /* sigma = sigma_srm * (z_POI + z_eff) */
+                            if (POI + l_eff >= 0)
                             {
-                                sigma = sigma_srm * (z_POI - z_eff);
+                                sigma = theta_srm * (POI + l_eff);
                             }
                             else
                             {
@@ -511,35 +526,44 @@ void compute_sigma_range_compensator(Rpl_volume* sigma_vol, Rpl_volume* rpl_volu
                 /* calculation of sigma_srm, see graph A3 from the Hong's paper */
                 if (!rpl_volume->get_aperture()->have_aperture_image() || (rpl_volume->get_aperture()->have_aperture_image() && ap_img[idx2d_sm] > 0))
                 {
-                    rc_over_range = rc_img[idx2d_sm] / range; // energy is >1, so range > 0
+					Ray_data* ray_data = &sigma_vol->get_Ray_data()[idx2d_lg];
+	        
+					proj = -vec3_dot(ray_data->ray, nrm);
+
+					if (proj == 0) 
+					{ 
+						printf("error: some rays are perpendicular to the beam axis \n");
+						return;
+					}
+
+					dist_cp = vec3_dist(ray_data->cp, beam->get_source_position());
+                    rc_over_range = rc_img[idx2d_sm] / proj * PMMA_DENSITY * PMMA_STPR / range; // energy is >1, so range > 0
+
                     if (rc_over_range < 1)
                     {
-                        sigma_srm = sigma0 * rc_over_range * ( 0.26232 + 0.64298 * rc_over_range + 0.0952393 * rc_over_range * rc_over_range);
-
-                        /* calculation of rc_eff - see Hong's paper graph A3 - linear interpolation of the curve */
-                        rc_eff = get_rc_eff(rc_over_range);
-        
-                        Ray_data* ray_data = &sigma_vol->get_Ray_data()[idx2d_lg];
-	        
-                        /* MD Fix: Why plan->ap->nrm is incorrect at this point??? */
-                        vec3_sub3(nrm, plan->beam->get_source_position(), plan->beam->get_isocenter_position());
-                        vec3_normalize1(nrm);
-	        
-                        proj = -vec3_dot(ray_data->ray, nrm);
-                        dist_cp = vec3_dist(ray_data->cp, plan->beam->get_source_position()) * proj;
+						if (beam->get_rc_MC_model() != 'y')
+						{
+							theta_srm =  theta0 * get_theta_rel_Highland(rc_over_range);
+							rc_eff = get_scat_or_Highland(rc_over_range) * rc_img[idx2d_sm];
+						}
+						else
+						{
+							theta_srm =  theta0 * get_theta_rel_MC(rc_over_range);
+							rc_eff = get_scat_or_MC(rc_over_range) * rc_img[idx2d_sm];
+						}
 
                         for (int k = 0; k < dim[2]; k++)
                         {
                             idx = dim[0]*dim[1]*k + idx2d_lg;
                             if ( (rgl_img[idx] + rc_img[idx2d_sm]) < range +10) // +10 because we calculate sigma_rg_compensator a little bit farther after the range to be sure (+1 cm)
                             {
-                                z_POI = proj * (dist_cp + (float) k * sigma_vol->get_vol()->spacing[2]);
-                                z_eff = plan->beam->get_aperture()->get_distance() + proj * rc_eff;
+                                POI = dist_cp + (float) k * sigma_vol->get_vol()->spacing[2] - beam->get_aperture()->get_distance() / proj;
+                                l_eff =  rc_eff * proj;
 		            
                                 /* sigma = sigma_srm * (z_POI- z_eff) */
-                                if (z_POI - z_eff >= 0)
+                                if (POI + l_eff >= 0)
                                 {
-                                    sigma = sigma_srm * (z_POI - z_eff);
+                                    sigma = theta_srm * (POI - l_eff);
                                 }
                                 else
                                 {
@@ -564,40 +588,7 @@ void compute_sigma_range_compensator(Rpl_volume* sigma_vol, Rpl_volume* rpl_volu
                 }
             }
         }
-    }
-	
+    }	
     printf("Sigma range compensator computed - sigma_rc_max = %lg mm.\n", sigma_max);
     return;
 }
-
-double get_rc_eff(double rc_over_range) /* Values from the table A3 of the Hong's algorithm */
-{
-    if (rc_over_range >= 0 && rc_over_range < 0.5)
-    {
-        return rc_over_range * ( 0.49 + 0.060 / 0.5 * rc_over_range );
-    }
-    else if (rc_over_range >= 0.5 && rc_over_range <0.8)
-    {
-        return rc_over_range * ( 0.55 + 0.085 / 0.3 * (rc_over_range-0.5) );
-    }
-    else if (rc_over_range >= 0.8 && rc_over_range <0.9)
-    {
-        return rc_over_range * (0.635 + 0.055 / 0.1 * (rc_over_range-0.8) );
-    }
-    else if (rc_over_range >= 0.9 && rc_over_range <0.95)
-    {
-        return rc_over_range * (0.690 + (rc_over_range-0.9) );
-    }
-    else if (rc_over_range >= 0.95 && rc_over_range <= 1)
-    {
-        return rc_over_range * (0.740 + 0.26/0.05 * (rc_over_range-0.95) );
-    }
-    else if (rc_over_range > 1)
-    {
-        return 1;
-    }
-    else
-    {
-        return 0;
-    }
-}
diff --git a/src/plastimatch/dose/rt_sigma.h b/src/plastimatch/dose/rt_sigma.h
index 8dbed55..b09ef0a 100644
--- a/src/plastimatch/dose/rt_sigma.h
+++ b/src/plastimatch/dose/rt_sigma.h
@@ -1,22 +1,17 @@
 /* -----------------------------------------------------------------------
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
-#ifndef _Rt_sigma_h_
-#define _Rt_sigma_h_
+#ifndef _rt_sigma_h_
+#define _rt_sigma_h_
 
 #include "rt_plan.h"
 #include "rpl_volume.h"
 
-void compute_sigmas(Rt_plan* plan, float energy, float* sigma_max, std::string size, int* margins);
-
-void compute_sigma_pt(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rpl_volume* ct_vol, Rt_plan* plan, float energy);
+void compute_sigmas(Rt_plan* plan, const Rt_beam* beam, float energy, float* sigma_max, std::string size, int* margins);
+void compute_sigma_pt(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rpl_volume* ct_vol, Rt_plan* plan, const Rt_beam* beam, float energy);
 float compute_sigma_pt_homo(Rpl_volume* sigma_vol, Rpl_volume* rpl_vol, float energy);
 float compute_sigma_pt_hetero(Rpl_volume* sigma_vol, Rpl_volume* rgl_vol, Rpl_volume* ct_vol, float energy);
+void compute_sigma_source(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan* plan, const Rt_beam* beam, float energy);
+void compute_sigma_range_compensator(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan* plan, const Rt_beam* beam, float energy, int* margins);
 
-void compute_sigma_source(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan* plan, float energy);
-
-void compute_sigma_range_compensator(Rpl_volume* sigma_vol, Rpl_volume* rpl_volume, Rt_plan* plan, float energy, int* margins);
-
-double get_rc_eff(double rc_over_range);
-
-#endif
\ No newline at end of file
+#endif
diff --git a/src/plastimatch/dose/rt_sobp.cxx b/src/plastimatch/dose/rt_sobp.cxx
deleted file mode 100644
index abe94d7..0000000
--- a/src/plastimatch/dose/rt_sobp.cxx
+++ /dev/null
@@ -1,780 +0,0 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-#include "plmdose_config.h"
-#include <string>
-#include <utility>
-#include <vector>
-#include <stdio.h>
-#include <math.h>
-
-#include "bragg_curve.h"
-#include "file_util.h"
-#include "path_util.h"
-#include "print_and_exit.h"
-#include "rt_depth_dose.h"
-#include "rt_sobp.h"
-#include "rt_sobp_p.h"
-#include "string_util.h"
-
-Rt_sobp::Rt_sobp ()
-{
-    d_ptr = new Rt_sobp_private;
-}
-
-Rt_sobp::Rt_sobp (Particle_type part)
-{
-    d_ptr = new Rt_sobp_private(part);
-}
-
-Rt_sobp::Rt_sobp (const Rt_sobp::Pointer& rt_sobp)
-{
-    d_ptr = new Rt_sobp_private (rt_sobp->d_ptr);
-}
-
-Rt_sobp::~Rt_sobp ()
-{
-    delete d_ptr;
-}
-
-void
-Rt_sobp::clear_peaks ()
-{
-    d_ptr->clear_peaks ();
-}
-
-void
-Rt_sobp::add_peak (Rt_depth_dose* depth_dose)
-{
-    d_ptr->depth_dose.push_back (depth_dose);
-
-    /* GCS FIX: This should probably update the max depth too */
-}
-
-void
-Rt_sobp::add_peak (double E0, double spread, 
-    double dres, double dmax, double weight)
-{
-    switch(d_ptr->particle_type)
-    {
-    case PARTICLE_TYPE_P:			// proton
-    {
-        printf ("Adding peak to sobp (%f, %f, %f) [%f, %f]\n", 
-            (float) E0, (float) spread, (float) weight,
-            (float) dres, (float) dmax);
-        Rt_depth_dose *depth_dose = new Rt_depth_dose (
-            E0, spread, dres, dmax, weight);
-        d_ptr->depth_dose.push_back (depth_dose);
-
-	/* Update maximum */
-	if (dmax > d_ptr->dmax) {
-            d_ptr->dmax = dmax;
-            break;
-        }
-    }
-    case PARTICLE_TYPE_HE:			// helium
-    {
-        //to be implemented
-    }
-    break;
-    case PARTICLE_TYPE_LI:			// lithium
-    {
-        //to be implemented
-    }
-    break;
-    case PARTICLE_TYPE_BE:			// berilium
-    {
-        //to be implemented
-    }
-    break;
-    case PARTICLE_TYPE_B:			// bore
-    {
-        //to be implemented
-    }
-    break;
-    case PARTICLE_TYPE_C:			// carbon
-    {
-        //to be implemented
-    }
-    break;
-    case PARTICLE_TYPE_O:			// oxygen
-    {
-        //to be implemented
-    }
-    break;
-    default:
-    {
-        //to be implemented
-    }
-    }
-}
-
-void
-Rt_sobp::set_prescription_min_max (float d_min, float d_max)
-{
-    d_ptr->prescription_dmin = d_min;
-    d_ptr->prescription_dmax = d_max;
-}
-
-void 
-Rt_sobp::set_energyResolution(double eres)
-{
-    d_ptr->eres = eres;
-}
-
-double 
-Rt_sobp::get_energyResolution()
-{
-    return d_ptr->eres;
-}
-
-void
-Rt_sobp::optimize ()
-{	
-    this->SetMinMaxDepths(
-        d_ptr->prescription_dmin,
-        d_ptr->prescription_dmax,
-        d_ptr->dres);
-    this->Optimizer3 ();
-}
-
-float
-Rt_sobp::lookup_energy (
-    float depth
-)
-{	
-    int i;
-    float energy = 0.0f;
-
-    /* Sanity check */
-    if (depth < 0) {
-        return 0.0f;
-    }
-
-    /* Find index into profile arrays */
-    for (i = 0; i < d_ptr->num_samples-1; i++) {
-        if (d_ptr->d_lut[i] > depth) {
-            i--;
-            break;
-        }
-    }
-
-    /* Clip input depth to maximum in lookup table */
-    if (i == d_ptr->num_samples-1) {
-        depth = d_ptr->d_lut[i];
-    }
-
-    /* Use index to lookup and interpolate energy */
-    if (i >= 0 || i < d_ptr->num_samples) {
-        // linear interpolation
-        energy = d_ptr->e_lut[i]
-            + (depth - d_ptr->d_lut[i])
-            * ((d_ptr->e_lut[i+1] - d_ptr->e_lut[i]) 
-                / (d_ptr->d_lut[i+1] - d_ptr->d_lut[i]));
-    } else {
-        // we wen't past the end of the lookup table
-        energy = 0.0f;
-    }
-
-    return energy;   
-}
-
-bool
-Rt_sobp::generate ()
-{
-    printf("samples: %d\n", d_ptr->depth_dose.size());
-    std::vector<const Rt_depth_dose*>::const_iterator it 
-        = d_ptr->depth_dose.begin();
-    while (it != d_ptr->depth_dose.end ()) {
-        const Rt_depth_dose *ppp = *it;
-
-        /* Construct the data structure first time through */
-        if (!d_ptr->d_lut || d_ptr->num_samples != ppp->num_samples) {
-            d_ptr->num_samples = ppp->num_samples;
-            d_ptr->dres = ppp->dres;
-
-	    if (d_ptr->d_lut) delete[] d_ptr->d_lut;
-	    if (d_ptr->e_lut) delete[] d_ptr->e_lut;
-
-            d_ptr->d_lut = new float [ppp->num_samples];
-            d_ptr->e_lut = new float [ppp->num_samples];
-
-            for (int i = 0; i < d_ptr->num_samples; i++) {
-                d_ptr->d_lut[i] = ppp->d_lut[i];
-                d_ptr->e_lut[i] = 0;
-            }
-        }
-
-        /* Check that this peak has the same num_samples, dres */
-        if (ppp->num_samples != d_ptr->num_samples) {
-            print_and_exit ("Error, mismatch in num_samples of SOBP\n");
-        }
-        if (ppp->dres != d_ptr->dres) {
-            print_and_exit ("Error, mismatch in dres of SOBP\n");
-        }
-
-        /* Add weighted pristine peak to sobp */
-        for (int i = 0; i < d_ptr->num_samples; i++) {
-            d_ptr->e_lut[i] += ppp->weight * ppp->e_lut[i];
-        }
-
-        /* Go on to next pristine peak */
-        it++;
-    }
-    return true;
-}
-
-void
-Rt_sobp::dump (const char* dir)
-{
-    std::string dirname = dir;
-
-    /* Dump SOBP */
-    std::string sobp_fn = string_format ("%s/bragg_curve.txt", dir);
-    FILE* fp = fopen (sobp_fn.c_str(), "w");
-    for (int i=0; i < d_ptr->num_samples; i++) {
-        fprintf (fp, "%3.2f %3.2f\n", d_ptr->d_lut[i], d_ptr->e_lut[i]);
-    }
-    fclose (fp);
-
-    /* Dump pristine peaks */
-    std::vector<const Rt_depth_dose*>::const_iterator it 
-        = d_ptr->depth_dose.begin();
-    while (it != d_ptr->depth_dose.end ()) {
-        std::string fn = string_format ("%s/pristine_%4.2f.txt", dir, 
-            (float) (*it)->E0);
-        (*it)->dump (fn.c_str());
-        it++;
-    }
-}
-
-void 
-Rt_sobp::SetParticleType (Particle_type particle_type)
-{
-    d_ptr->set_particle_type (particle_type);
-    if (d_ptr->dmin !=0 && d_ptr->dmax !=0) {
-        // we redefined the energies used to create the sobp
-        SetMinMaxDepths(d_ptr->dmin, d_ptr->dmax);
-    }
-}
-
-// return on the command line the parameters of the sobp to be build
-void Rt_sobp::printparameters()
-{
-    printf ("Particle type : %s\n", 
-        particle_type_string (d_ptr->particle_type));
-		
-    printf("\nNumber of peaks : %d\n",d_ptr->num_peaks);
-    printf("E_resolution : %d MeV \n",d_ptr->eres);
-    printf("E_min : %d MeV \n",d_ptr->E_min);
-    printf("E_max : %d MeV \n\n",d_ptr->E_max);
-	
-    printf("z_resolution : %3.2f mm \n",d_ptr->dres);
-    printf("z_min : %3.2f mm\n",d_ptr->dmin);
-    printf("z_max : %3.2f mm\n",d_ptr->dmax);
-    printf("z_end : %3.2f mm\n\n",d_ptr->dend);
-}
-
-void 
-Rt_sobp::set_dose_lut(float* d_lut, float* e_lut, int num_samples)
-{
-    for (int i = 0; i < num_samples-1; i++)
-    {
-        d_ptr->d_lut[i] = d_lut[i];
-        d_ptr->e_lut[i] = e_lut[i];
-        if (i == 0) 
-        {
-            d_ptr->f_lut[i] = e_lut[i];
-        } 
-        else 
-        {
-            d_ptr->f_lut[i] = d_ptr->f_lut[i-1] + e_lut[i];
-        }
-    }
-    d_ptr->num_samples = num_samples;
-}
-
-float* 
-Rt_sobp::get_d_lut()
-{
-    return d_ptr->d_lut;
-}
-
-float* 
-Rt_sobp::get_e_lut()
-{
-    return d_ptr->e_lut;
-}
-
-void 
-Rt_sobp::set_dres(double dres)
-{
-    if (dres != 0)
-    {
-        d_ptr->dres = dres;
-        d_ptr->num_samples = d_ptr->dmax / dres;
-    }
-    else
-    {
-        printf("the depth resolution for depth dose cannot be null\n");
-    }
-}
-
-double
-Rt_sobp::get_dres()
-{
-    return d_ptr->dres;
-}
-
-void 
-Rt_sobp::set_num_samples(int num_samples)
-{
-    d_ptr->num_samples = num_samples;
-}
-
-int 
-Rt_sobp::get_num_samples()
-{
-    return d_ptr->num_samples;
-}
-
-void 
-Rt_sobp::set_eres(int eres)
-{
-    d_ptr->eres = eres;
-}
-
-int 
-Rt_sobp::get_eres()
-{
-    return d_ptr->eres;
-}
-
-size_t
-Rt_sobp::get_num_peaks()
-{
-    return d_ptr->depth_dose.size();
-}
-
-int 
-Rt_sobp::optimizer_num_peaks ()
-{
-    return (int)(((d_ptr->E_max - d_ptr->E_min) / d_ptr->eres) + 1);
-}
-
-void 
-Rt_sobp::set_E_min(int E_min)
-{
-    d_ptr->E_min = E_min;
-}
-
-int 
-Rt_sobp::get_E_min()
-{
-    return d_ptr->E_min;
-}
-
-void 
-Rt_sobp::set_E_max(int E_max)
-{
-    d_ptr->E_max = E_max;
-}
-
-int 
-Rt_sobp::get_E_max()
-{
-    return d_ptr->E_max;
-}
-
-void 
-Rt_sobp::set_dmin(float dmin)
-{
-    d_ptr->dmin = dmin;
-}
-
-float 
-Rt_sobp::get_dmin()
-{
-    return d_ptr->dmin;
-}
-
-void 
-Rt_sobp::set_dmax(float dmax)
-{
-    d_ptr->dmax = dmax;
-    float num_sample = dmax / (float) d_ptr->dres;
-    if (num_sample - (float) ((int) num_sample) == 0)
-    {
-        d_ptr->num_samples = num_sample;
-    }
-    else
-    {
-        d_ptr->num_samples = num_sample + 1;
-    }
-}
-
-float 
-Rt_sobp::get_dmax()
-{
-    return d_ptr->dmax;
-}
-
-void 
-Rt_sobp::set_dend(float dend)
-{
-    d_ptr->dend = dend;
-}
-
-float
-Rt_sobp::get_dend()
-{
-    return d_ptr->dend;
-}
-
-void 
-Rt_sobp::set_particle_type(Particle_type particle_type)
-{
-    d_ptr->particle_type = particle_type;
-}
-
-Particle_type
-Rt_sobp::get_particle_type()
-{
-    return d_ptr->particle_type;
-}
-
-void 
-Rt_sobp::set_p(double p)
-{
-    d_ptr->p = p;
-}
-
-double 
-Rt_sobp::get_p()
-{
-    return d_ptr->p;
-}
-
-void 
-Rt_sobp::set_alpha(double alpha)
-{
-    d_ptr->alpha = alpha;
-}
-
-double 
-Rt_sobp::get_alpha()
-{
-    return d_ptr->alpha;
-}
-
-void 
-Rt_sobp::set_prescription_min(float prescription_min)
-{
-    d_ptr->prescription_dmin = prescription_min;
-}
-
-float 
-Rt_sobp::get_prescription_min()
-{
-    return d_ptr->prescription_dmin;
-}
-
-void 
-Rt_sobp::set_prescription_max(float prescription_max)
-{
-    d_ptr->prescription_dmax = prescription_max;
-}
-
-float
-Rt_sobp::get_prescription_max()
-{
-    return d_ptr->prescription_dmax;
-}
-
-void 
-Rt_sobp::add_weight(double weight)
-{
-    d_ptr->sobp_weight.push_back(weight);
-}
-
-std::vector<double> 
-Rt_sobp::get_weight()
-{
-    return d_ptr->sobp_weight;
-}
-
-std::vector<const Rt_depth_dose*> 
-Rt_sobp::get_depth_dose()
-{
-    return d_ptr->depth_dose;
-}
-
-void 
-Rt_sobp::add_depth_dose(const Rt_depth_dose* depth_dose)
-{
-    Rt_depth_dose* dose = new Rt_depth_dose;	
-    for (int i = 0; i < depth_dose->num_samples; i++)
-    {
-        dose->e_lut[i] = depth_dose->e_lut[i];
-        dose->f_lut[i] = depth_dose->f_lut[i];
-        dose->d_lut[i] = depth_dose->d_lut[i];
-        dose->dmax = depth_dose->dmax;
-        dose->dres = depth_dose->dres;
-        dose->E0 = depth_dose->E0;
-        dose->num_samples = depth_dose->num_samples;
-        dose->spread = depth_dose->spread;
-    }
-    d_ptr->depth_dose.push_back(dose);
-}
-
-void Rt_sobp::print_sobp_curve()
-{
-    printf("\n print sobp curve : \n");
-    if (d_ptr->num_samples != 0)
-    {
-        for ( int i = 0; i < d_ptr->num_samples ; i++)
-        {
-            printf("\n %f : %f", d_ptr->d_lut[i], d_ptr->e_lut[i]);
-        }
-    }
-    else
-    {
-        printf(" void sobp curve");
-    }
-    printf("\n");
-}
-
-void Rt_sobp::SetMinMaxEnergies(int new_E_min, int new_E_max) // set the sobp parameters by introducing the min and max energies
-{
-    if (new_E_max <= 0 || new_E_min <= 0)
-    {
-        printf("The energies min and max of the Sobp must be positive!\n");
-        printf("Emin = %d, Emax = %d \n", new_E_min, new_E_max);
-    }
-    else
-    {	
-        if (new_E_max >= new_E_min)
-        {
-            d_ptr->E_min = new_E_min;
-            d_ptr->E_max = new_E_max;
-        }
-        else
-        {
-            d_ptr->E_min = new_E_max;
-            d_ptr->E_max = new_E_min;
-        }
-
-        d_ptr->dmin = ((10*d_ptr->alpha)*pow(d_ptr->E_min, d_ptr->p));
-        d_ptr->dmax = ((10*d_ptr->alpha)*pow(d_ptr->E_max, d_ptr->p))+1;
-        d_ptr->dend = d_ptr->dmax + 20;
-        d_ptr->num_samples = (int)((d_ptr->dend/d_ptr->dres)+1);
-        if ((d_ptr->num_samples-1)*d_ptr->dres < d_ptr->dend)
-        {
-            d_ptr->num_samples++;
-        }
-
-        if (d_ptr->d_lut) delete[] d_ptr->d_lut;
-        d_ptr->d_lut = new float[d_ptr->num_samples];
-        if (d_ptr->e_lut) delete[] d_ptr->e_lut;
-        d_ptr->e_lut = new float[d_ptr->num_samples];
-        if (d_ptr->f_lut) delete[] d_ptr->f_lut;
-        d_ptr->f_lut = new float[d_ptr->num_samples];
-
-        for (int i = 0; i < d_ptr->num_samples-1; i++)
-        {
-            d_ptr->d_lut[i] = i*d_ptr->dres;
-            d_ptr->e_lut[i] = 0;
-            d_ptr->f_lut[i] = 0;
-        }
-
-        d_ptr->d_lut[d_ptr->num_samples-1] = d_ptr->dend;
-        d_ptr->e_lut[d_ptr->num_samples-1] = 0;
-        d_ptr->f_lut[d_ptr->num_samples-1] = 0;
-    }
-}
-
-void Rt_sobp::SetMinMaxEnergies(int new_E_min, int new_E_max, int new_step) // set the sobp parameters by introducing the min and max energies
-{
-    if (new_E_max <= 0 || new_E_min <= 0 || new_step < 0)
-    {
-        printf("The energies min and max of the Sobp must be positive and the step must be positive!\n");
-        printf("Emin = %d, Emax = %d, step = %d \n", new_E_min, new_E_max, new_step);
-    }
-    else
-    {	
-        if (new_E_max >= new_E_min)
-        {
-            d_ptr->E_min = new_E_min;
-            d_ptr->E_max = new_E_max;
-            d_ptr->eres = new_step;
-        }
-        else
-        {
-            d_ptr->E_min = new_E_max;
-            d_ptr->E_max = new_E_min;
-            d_ptr->eres = new_step;
-        }
-
-        d_ptr->dmin = ((10*d_ptr->alpha)*pow(d_ptr->E_min, d_ptr->p));
-        d_ptr->dmax = ((10*d_ptr->alpha)*pow(d_ptr->E_max, d_ptr->p))+1;
-        d_ptr->dend = d_ptr->dmax + 20;
-        d_ptr->num_samples = (int)((d_ptr->dend/d_ptr->dres)+1);
-		
-        if ((d_ptr->num_samples-1)*d_ptr->dres < d_ptr->dend)
-        {
-            d_ptr->num_samples++;
-        }
-
-        if (d_ptr->d_lut) delete[] d_ptr->d_lut;
-        d_ptr->d_lut = new float[d_ptr->num_samples];
-        if (d_ptr->e_lut) delete[] d_ptr->e_lut;
-        d_ptr->e_lut = new float[d_ptr->num_samples];
-        if (d_ptr->f_lut) delete[] d_ptr->f_lut;
-        d_ptr->f_lut = new float[d_ptr->num_samples];
-
-
-        for (int i = 0; i < d_ptr->num_samples-1; i++)
-        {
-            d_ptr->d_lut[i] = i*d_ptr->dres;
-            d_ptr->e_lut[i] = 0;
-            d_ptr->f_lut[i] = 0;
-        }
-
-        d_ptr->d_lut[d_ptr->num_samples-1] = d_ptr->dend;
-        d_ptr->e_lut[d_ptr->num_samples-1] = 0;
-        d_ptr->f_lut[d_ptr->num_samples-1] = 0;
-    }
-}
-
-void Rt_sobp::SetMinMaxDepths(float new_z_min, float new_z_max) // set the sobp parameters by introducing the proximal and distal distances
-{
-    if (new_z_max <= 0 || new_z_min <= 0)
-    {
-        printf("Error: The depth min and max of the Sobp must be positive!\n");
-        printf("zmin = %f, zmax = %f\n", new_z_min, new_z_max);
-    }
-    else
-    {	
-        if (new_z_max >= new_z_min)
-        {
-            d_ptr->dmin = new_z_min;
-            d_ptr->dmax = new_z_max;
-        }
-        else
-        {
-            d_ptr->dmin = new_z_max;
-            d_ptr->dmax = new_z_min;
-        }
-
-        d_ptr->E_min = int(pow((d_ptr->dmin/(10*d_ptr->alpha)),(1/d_ptr->p)));
-        d_ptr->E_max = int(pow((d_ptr->dmax/(10*d_ptr->alpha)),(1/d_ptr->p)))+1;
-        d_ptr->dend = d_ptr->dmax + 20;
-        d_ptr->num_samples = (int)((d_ptr->dend/d_ptr->dres)+1);
-
-        if ((d_ptr->num_samples-1)*d_ptr->dres < d_ptr->dend)
-        {
-            d_ptr->num_samples++;
-        }
-
-        if (d_ptr->d_lut) delete[] d_ptr->d_lut;
-        d_ptr->d_lut = new float[d_ptr->num_samples];
-        if (d_ptr->e_lut) delete[] d_ptr->e_lut;
-        d_ptr->e_lut = new float[d_ptr->num_samples];
-        if (d_ptr->f_lut) delete[] d_ptr->f_lut;
-        d_ptr->f_lut = new float[d_ptr->num_samples];
-
-        for (int i = 0; i < d_ptr->num_samples-1; i++)
-        {
-            d_ptr->d_lut[i] = i*d_ptr->dres;
-            d_ptr->e_lut[i] = 0;
-            d_ptr->f_lut[i] = 0;
-        }
-
-        d_ptr->d_lut[d_ptr->num_samples-1] = d_ptr->dend;
-        d_ptr->e_lut[d_ptr->num_samples-1] = 0;
-        d_ptr->f_lut[d_ptr->num_samples-1] = 0;
-    }
-}
-
-void Rt_sobp::SetMinMaxDepths(float new_z_min, float new_z_max, float new_step) // set the sobp parameters by introducing the proximal and distal distances
-{
-    if (new_z_max <= 0 || new_z_min <= 0)
-    {
-        printf("Error: The depth min and max and the step of the Sobp must be positive!\n");
-        printf("zmin = %f, zmax = %f and z_step = %f\n", new_z_min, new_z_max, new_step);
-    }
-    else
-    {	
-        if (new_z_max >= new_z_min)
-        {
-            d_ptr->dmin = new_z_min;
-            d_ptr->dmax = new_z_max;
-            d_ptr->dres = new_step;
-        }
-        else
-        {
-            d_ptr->dmin = new_z_max;
-            d_ptr->dmax = new_z_min;
-            d_ptr->dres = new_step;
-        }
-
-        d_ptr->E_min = int(pow((d_ptr->dmin/(10*d_ptr->alpha)),(1/d_ptr->p)));
-        d_ptr->E_max = int(pow((d_ptr->dmax/(10*d_ptr->alpha)),(1/d_ptr->p)))+d_ptr->eres;
-        d_ptr->dend = d_ptr->dmax + 20;
-        d_ptr->num_samples = (int)((d_ptr->dend/d_ptr->dres)+1);
-
-        if ((d_ptr->num_samples-1)*d_ptr->dres < d_ptr->dend)
-        {
-            d_ptr->num_samples++;
-        }
-
-        if (d_ptr->d_lut) delete[] d_ptr->d_lut;
-        d_ptr->d_lut = new float[d_ptr->num_samples];
-        if (d_ptr->e_lut) delete[] d_ptr->e_lut;
-        d_ptr->e_lut = new float[d_ptr->num_samples];
-        if (d_ptr->f_lut) delete[] d_ptr->f_lut;
-        d_ptr->f_lut = new float[d_ptr->num_samples];
-
-        for (int i = 0; i < d_ptr->num_samples-1; i++)
-        {
-            d_ptr->d_lut[i] = i*d_ptr->dres;
-            d_ptr->e_lut[i] = 0;
-            d_ptr->f_lut[i] = 0;
-        }
-
-        d_ptr->d_lut[d_ptr->num_samples-1] = d_ptr->dend;
-        d_ptr->e_lut[d_ptr->num_samples-1] = 0;
-        d_ptr->f_lut[d_ptr->num_samples-1] = 0;		
-    }
-}
-
-void Rt_sobp::SetEnergyStep(int new_step)
-{
-    SetMinMaxEnergies(d_ptr->E_min, d_ptr->E_max, new_step);
-}
-
-void Rt_sobp::SetDepthStep(float new_step)
-{
-    SetMinMaxDepths(d_ptr->dmin, d_ptr->dmax, new_step);
-}
-
-void Rt_sobp::SetDoseMax(float dose_max)
-{
-    d_ptr->dose_max = dose_max;
-}
-
-float Rt_sobp::GetDoseMax()
-{
-    return d_ptr->dose_max;
-}
-
-float Rt_sobp::get_maximum_depth()
-{
-    return d_ptr->dmax;
-}
-
-std::vector<const Rt_depth_dose*>
-Rt_sobp::getPeaks()
-{
-    return d_ptr->depth_dose;
-}
diff --git a/src/plastimatch/dose/rt_sobp.h b/src/plastimatch/dose/rt_sobp.h
deleted file mode 100644
index 737d845..0000000
--- a/src/plastimatch/dose/rt_sobp.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-/* -----------------------------------------------------------------------
-   rt_sobp (Ion Spread Out Bragg Peak) is a class that creates 
-   a sobp from minimal and
-   maximal depth to be targeted, or minimal and maximal energies of the
-   pristine Bragg Peaks used to create the sobp. This class return the 
-   weights of each pristine peak or the created sobp. It contains also the
-   optimization process to get the weight of the pristine peaks. 
-   Mother class from which
-   will derive the class for proton and other ions.
-   ----------------------------------------------------------------------- */
-#ifndef _rt_sobp_h_
-#define _rt_sobp_h_
-
-#include "plmdose_config.h"
-#include <stdio.h>
-#include <vector>
-#include "particle_type.h"
-#include "plmdose_config.h"
-#include "plm_config.h"
-#include "rt_lut.h"
-#include "smart_pointer.h"
-
-class Rt_depth_dose;
-class Rt_sobp_private;
-
-class PLMDOSE_API Rt_sobp {
-public:
-    SMART_POINTER_SUPPORT (Rt_sobp);
-    Rt_sobp_private *d_ptr;
-public:
-    Rt_sobp ();
-    Rt_sobp (Particle_type part);
-    Rt_sobp (const Rt_sobp::Pointer& rt_sobp);
-    ~Rt_sobp ();
-
-    void set_resolution (double dres, int num_samples);
-    void set_energyResolution(double eres);
-    double get_energyResolution();
-
-    /* set the type of particle (proton, helium ions, carbon ions...)*/
-    void SetParticleType(Particle_type particle_type);
-
-    /* Remove all peaks */
-    void clear_peaks ();
-
-    /* Add a pristine peak to a sobp */
-    void add_peak (Rt_depth_dose* depth_dose);
-    void add_peak (double E0, double spread, 
-        double dres, double dmax, double weight);
-
-    /* Set the min & max depth for automatic sobp optimization */
-    void set_prescription_min_max (float d_min, float d_max);
-
-    /* Save the depth dose to a file */
-    void dump (const char* dir);
-
-    /* Optimize, then generate sobp depth curve from prescription 
-       range and modulation */
-    void optimize ();
-
-    /* Compute the sobp depth dose curve from weighted pristine peaks */
-    bool generate ();
-
-    /* Return simple depth dose result at depth */
-    float lookup_energy (float depth);
-
-    /* Return zmax */
-    float get_maximum_depth();
-
-    /* Print the parameters of the sobp */
-    void printparameters();
-
-    /* print sobp curve */
-    void print_sobp_curve();
-
-    /* Set private members */
-    void set_dose_lut(float* d_lut, float* e_lut, int num_samples);
-    float* get_d_lut();
-    float* get_e_lut();
-    void set_dres(double dres);
-    double get_dres();
-    void set_num_samples(int num_samples);
-    int get_num_samples();
-    void set_eres(int eres);
-    int get_eres();
-    void set_E_min(int E_min);
-    int get_E_min();
-    void set_E_max(int E_max);
-    int get_E_max();
-    void set_dmin(float dmin);
-    float get_dmin();
-    void set_dmax(float dmax);
-    float get_dmax();
-    void set_dend(float dend);
-    float get_dend();
-    void set_particle_type(Particle_type particle_type);
-    Particle_type get_particle_type();
-    void set_p(double p);
-    double get_p();
-    void set_alpha(double alpha);
-    double get_alpha();
-    void set_prescription_min(float prescription_min);
-    float get_prescription_min();
-    void set_prescription_max(float prescription_max);
-    float get_prescription_max();
-    void add_weight(double sobp_weight);
-    std::vector<double> get_weight();
-    std::vector<const Rt_depth_dose*> get_depth_dose();
-    void add_depth_dose(const Rt_depth_dose* depth_dose);
-
-    size_t get_num_peaks();
-
-    /* Compute number of peaks used when doing automatic peak selection. */
-    int optimizer_num_peaks ();
-
-    /* set the minimal and maximal energy to buld the sobp peak */
-    void SetMinMaxEnergies(int new_E_min, int new_E_max);
-    /* set the minimal and maximal energy to buld the sobp peak and energy step */
-    void SetMinMaxEnergies(int new_E_min, int new_E_max, int new_step); 
-    /* set the minimal and maximal depth covered by the sobp */
-    void SetMinMaxDepths(float new_z_min, float new_z_max);
-    /* set the minimal and maximal depth covered by the sobp */
-    void SetMinMaxDepths(float new_z_min, float new_z_max, float new_step);
-    /* set the energy step only */
-    void SetEnergyStep(int new_step);
-    /* set energy step */
-    void SetDepthStep(float new_step);
-	/* set/get the dose max of the normalized SOBP */
-	void SetDoseMax(float dose_max);
-	float GetDoseMax();
-    /* get peaks - not a pointer */
-    std::vector<const Rt_depth_dose*> getPeaks();
-    /* Weight optimizer */
-    void Optimizer (int num_peaks);
-    void Optimizer2 (int num_peaks);
-	void Optimizer3 ();
-
-};
-
-#endif
diff --git a/src/plastimatch/dose/rt_sobp_optimize.cxx b/src/plastimatch/dose/rt_sobp_optimize.cxx
deleted file mode 100644
index f13918b..0000000
--- a/src/plastimatch/dose/rt_sobp_optimize.cxx
+++ /dev/null
@@ -1,429 +0,0 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-#include "plmdose_config.h"
-#include <string>
-#include <utility>
-#include <vector>
-#include <stdio.h>
-#include <math.h>
-
-#include "bragg_curve.h"
-#include "file_util.h"
-#include "path_util.h"
-#include "print_and_exit.h"
-#include "rt_depth_dose.h"
-#include "rt_sobp.h"
-#include "rt_sobp_p.h"
-#include "string_util.h"
-#include "vnl/algo/vnl_amoeba.h"
-#include "vnl/vnl_cost_function.h"
-
-/* cost function used to optimize the sobp shape */
-double cost_function_calculation (
-    std::vector<std::vector<double> > depth_dose, 
-    std::vector<double> weights, 
-    int num_peaks, 
-    int num_samples, 
-    std::vector<int> depth_in, 
-    std::vector<int> depth_out
-);
-
-/* vxl needs you to wrap the function within a class */
-class cost_function : public vnl_cost_function
-{
-public:
-    std::vector<std::vector<double> > depth_dose;
-    std::vector<double> weights;
-    std::vector<int> depth_in;
-    int num_peaks;
-    int num_samples;
-    double z_end;
-    std::vector<int> depth_out;
-
-    virtual double f (vnl_vector<double> const& vnl_x) {
-        /* vxl requires you using their own vnl_vector type, 
-           therefore we copy into a standard C/C++ array. */
-        for (int i=0; i < num_peaks; i++)
-        {
-            weights[i] =vnl_x[i];
-        }
-        return cost_function_calculation (depth_dose,weights, 
-            num_peaks, num_samples, depth_in, depth_out);
-    }
-};
-
-// Nelder-Mead type optimizer to get the optimized weights of the beams, 
-// optimized by a cost function (see below)
-void Rt_sobp::Optimizer (int num_peaks)
-{
-    double E_max = 0;
-    /* Create function object (for function to be minimized) */
-    cost_function cf;
-
-    cf.num_samples = d_ptr->num_samples;
-    cf.num_peaks = num_peaks;
-	
-    for (int i = 0; i < num_peaks; i++)
-    {
-        cf.weights.push_back(0);
-    }
-	
-    std::vector<int> energies (num_peaks,0);
-    std::vector<double> init_vector (d_ptr->num_samples,0);
-
-
-    cf.depth_dose.push_back(init_vector);
-
-    printf("\n %d Mono-energetic BP used: ", cf.num_peaks);
-
-    energies[0]= d_ptr->E_min;
-    printf("%d ", energies[0]);
-
-    cf.depth_dose[0][0] = bragg_curve((double)energies[0],1,0);  // creation of the matrix gathering all the depth dose of the BP constituting the sobp
-
-    for (int j = 0; j < d_ptr->num_samples; j++)
-    {
-        cf.depth_dose[0][j] = bragg_curve((double)energies[0],1,(double)d_ptr->d_lut[j]);
-        if (cf.depth_dose[0][j] > E_max)
-        {
-            E_max = cf.depth_dose[0][j];
-        }
-    }
-    for (int j = 0; j < d_ptr->num_samples; j++) // we normalize the depth dose curve to 1
-    {
-        cf.depth_dose[0][j] = cf.depth_dose[0][j] / E_max;
-    }
-
-
-    for (int i=1; i < cf.num_peaks-1; i++)
-    {
-        energies[i]=energies[i-1]+d_ptr->eres;
-        printf("%d ",energies[i]);
-		
-        cf.depth_dose.push_back(init_vector);
-        E_max = 0;
-
-        for (int j = 0; j < d_ptr->num_samples; j++)
-        {
-            cf.depth_dose[i][j] = bragg_curve(energies[i],1,d_ptr->d_lut[j]);
-            if (cf.depth_dose[i][j] > E_max)
-            {
-                E_max = cf.depth_dose[i][j];
-            }
-        }
-        for (int j = 0; j < d_ptr->num_samples; j++) // we normalize the depth dose curve to 1
-        {
-            cf.depth_dose[i][j] = cf.depth_dose[i][j] / E_max;
-        }
-    }
-
-    energies[cf.num_peaks-1]= d_ptr->E_max;
-    printf("%d \n", energies[cf.num_peaks-1]);
-
-    cf.depth_dose.push_back(init_vector);
-    for (int j = 0; j < d_ptr->num_samples; j++)
-    {
-        cf.depth_dose[cf.num_peaks-1][j] = bragg_curve(energies[cf.num_peaks-1],1,d_ptr->d_lut[j]);
-    }
-
-
-    for (int i = 0; i < d_ptr->num_samples ; i++) // creation of the two intervals that represents the inner part of the sobp and the outer part
-    {
-        cf.depth_in.push_back(0);
-        cf.depth_out.push_back(0);
-
-        if (d_ptr->d_lut[i]>=d_ptr->dmin && d_ptr->d_lut[i]<=d_ptr->dmax)
-        {
-            cf.depth_in[i] = 1;
-            cf.depth_out[i] = 0;
-        }
-        else
-        {
-            cf.depth_in[i] = 0;
-            cf.depth_out[i] = 1;
-        }
-    }	
-
-    /* Create optimizer object */
-    vnl_amoeba nm (cf);
-
-
-    /* Set some optimizer parameters */
-    nm.set_x_tolerance (0.0001);
-    nm.set_f_tolerance (0.0000001);
-    nm.set_max_iterations (1000000);
-
-    /* Set the starting point */
-    vnl_vector<double> x(cf.num_peaks, 1.0 / (double) cf.num_peaks);
-    const vnl_vector<double> y(cf.num_peaks, 0.01 / (double) cf.num_peaks);
-
-    /* Run the optimizer */
-    nm.minimize (x,y);
-
-    while (!d_ptr->depth_dose.empty())
-    {
-        d_ptr->depth_dose.pop_back();
-    }
-
-    for(int i = 0; i < d_ptr->num_peaks; i++)
-    {
-        this->add_peak ((double)energies[i],1, 
-            d_ptr->dres, (double)d_ptr->dend, cf.weights[i]);
-        d_ptr->sobp_weight.push_back(cf.weights[i]);
-    }
-
-    d_ptr->num_samples = d_ptr->depth_dose[0]->num_samples;
-
-    this->generate();
-}
-
-// Maxime Desplanques's better optimizer to get the optimized weights 
-// for pristine peaks
-void Rt_sobp::Optimizer2 (int num_peaks)
-{
-    double dose_max = 0;
-    int num_samples = d_ptr->num_samples;
-    std::vector<double> weight (num_peaks, 0);
-    int depth_max = 0;
-	
-    std::vector<int> energies (num_peaks,0);
-    std::vector<double> init_vector (num_samples,0);
-    std::vector< std::vector<double> > depth_dose (num_peaks, init_vector);
-
-    printf("\n %d Mono-energetic BP used:\n", num_peaks);
-
-    for (int i = 0; i < num_peaks; i++)
-    {
-        energies[i]= d_ptr->E_min + i * d_ptr->eres;
-        printf("%d ", energies[i]);
-    }
-    printf("\n");
-
-    for (int i = 0; i < num_peaks; i++)
-    {
-        dose_max = 0;
-
-        for (int j = 0; j < num_samples; j++)
-        {
-            depth_dose[i][j] = bragg_curve((double)energies[i],1,(double)d_ptr->d_lut[j]);
-		
-            if (depth_dose[i][j] > dose_max)
-            {
-                dose_max = depth_dose[i][j];
-            }
-        }
-
-        for (int j = 0; j < num_samples; j++)
-        {
-            depth_dose[i][j] = depth_dose[i][j] / dose_max;
-        }
-    }
-
-    for (int i = num_peaks -1 ; i >= 0; i--)
-    {
-        if (i == num_peaks - 1)
-        {
-            weight[i] = 1.0;
-        }
-        else
-        {
-            depth_max = max_depth_proton[ energies[i] ];
-            weight[i] = 1.0 - d_ptr->e_lut[depth_max];
-            if (weight[i] < 0)
-            {
-                weight[i] = 0;
-            }
-        }
-
-        for (int j = 0; j < num_samples; j++)
-        {
-            d_ptr->e_lut[j] += weight[i] * depth_dose[i][j];
-        }
-    }
-
-    for (int n = 0; n < 2; n++)
-    {
-        for (int i = 0; i < num_peaks; i++)
-        {
-            depth_max = max_depth_proton[ energies[i] ];
-            weight[i] = weight[i] / d_ptr->e_lut[depth_max];
-        }
-
-        for (int j = 0 ; j < num_samples; j++)
-        {
-            d_ptr->e_lut[j] = 0;
-            for (int i = 0; i < num_peaks; i++)
-            {
-                d_ptr->e_lut[j] += weight[i] * depth_dose[i][j];
-            }
-        }
-    }
-
-    while (!d_ptr->depth_dose.empty())
-    {
-        d_ptr->depth_dose.pop_back();
-    }
-
-    d_ptr->num_peaks = num_peaks;
-    for(int i = 0; i < d_ptr->num_peaks; i++)
-    {
-        this->add_peak ((double)energies[i],1, d_ptr->dres, 
-            (double)d_ptr->dend, weight[i]);
-        d_ptr->sobp_weight.push_back(weight[i]);
-    }
-}
-
-// Maxime Desplanques's better optimizer to get the optimized weights 
-// for pristine peaks -- working also for beams with different sigmaE
-void Rt_sobp::Optimizer3 ()
-{
-    int num_samples = d_ptr->num_samples;
-    int num_peaks = optimizer_num_peaks();
-
-    std::vector<int> energies (num_peaks,0);
-    std::vector<double> weight (num_peaks, 0);
-  
-    std::vector<double> init_vector (num_samples, 0);
-    std::vector< std::vector<double> > depth_dose (num_peaks, init_vector);
-
-    printf("\n %d Mono-energetic BP used:\n", num_peaks);
-
-    /* updating the energies in the table) */
-    for (int i = 0; i < num_peaks; i++)
-    {
-        energies[i]= d_ptr->E_min + i * d_ptr->eres;
-        printf("%d ", energies[i]);
-    }
-    printf("\n");
-
-
-    for (int i = 0; i < num_peaks; i++)
-    {
-        for (int j = 0; j < num_samples; j++)
-        {
-            depth_dose[i][j] = bragg_curve_norm((double)energies[i],1,(double)d_ptr->d_lut[j]);
-        }
-    }
-
-    for (int i = num_peaks -1 ; i >= 0; i--)
-    {
-        if (i == num_peaks - 1)
-        {
-            weight[i] = 1.0;
-        }
-        else
-        {
-            weight[i] = 1.0 - d_ptr->e_lut[get_depth_max(energies[i])];
-            if (weight[i] < 0)
-            {
-                weight[i] = 0;
-            }
-        }
-
-        for (int j = 0; j < num_samples; j++)
-        {
-            d_ptr->e_lut[j] += weight[i] * depth_dose[i][j];
-        }
-    }
-
-    for (int n = 0; n < 100; n++)
-    {
-        for (int i = 0; i < num_peaks; i++)
-        {
-            weight[i] = weight[i] / d_ptr->e_lut[get_depth_max(energies[i])];
-        }
-
-        for (int j = 0 ; j < num_samples; j++)
-        {
-            d_ptr->e_lut[j] = 0;
-            for (int i = 0; i < num_peaks; i++)
-            {
-                d_ptr->e_lut[j] += weight[i] * depth_dose[i][j];
-            }
-        }
-    }
-
-    while (!d_ptr->depth_dose.empty())
-    {
-        d_ptr->depth_dose.pop_back();
-    }
-
-    d_ptr->num_peaks = num_peaks;
-    for(int i = 0; i < d_ptr->num_peaks; i++)
-    {
-        this->add_peak ((double)energies[i],1, d_ptr->dres, 
-            (double)d_ptr->dend, weight[i]);
-        d_ptr->sobp_weight.push_back(weight[i]);
-    }
-
-    /* look for the max */
-    double dose_max = 0;
-    for(int i = d_ptr->num_samples-1; i >=0; i--)
-    {
-        if (d_ptr->e_lut[i] > dose_max)
-        {
-            dose_max = d_ptr->e_lut[i];
-        }
-    }
-    this->SetDoseMax(dose_max);
-}
-
-// cost function to be optimized in order to find the 
-// best weights and fit a perfect sobp
-double cost_function_calculation (
-    std::vector<std::vector<double> > depth_dose, 
-    std::vector<double> weights, 
-    int num_peaks, 
-    int num_samples, 
-    std::vector<int> depth_in, 
-    std::vector<int> depth_out
-)
-{
-    std::vector<double> diff (num_samples, 0);
-    std::vector<double> excess (num_samples, 0);
-    std::vector<double> f (num_samples, 0);
-    double f_tot = 0;
-    double sobp_max = 0;
-    double sum = 0;
-
-    for (int j = 0; j < num_samples; j++) // we fit the curve on all the depth
-    {
-        sum = 0;
-        for (int k = 0; k < num_peaks; k++)
-        {
-            sum = sum + weights[k]*depth_dose[k][j];
-        }
-        diff[j] = depth_in[j] * fabs(sum-1); // first parameters: the difference sqrt(standard deviation) between the curve and the perfect sobp, in the sobp area
-        if (diff[j] > sobp_max)
-        {
-            sobp_max = diff[j];					// second parameters: the max difference between the curve and the perfect sobp, in the sobp area
-        }
-
-        excess[j] = depth_out[j] * (sum-1);// first parameters: the excess difference sqrt(standard deviation) between the curve and the perfect sobp, out of the sobp area (we want it far lower that the sobp flat region
-        if (excess[j] < 0)
-        {
-            excess[j] = 0;
-        }
-        f[j]= 0.05 * diff[j]*diff[j] + 0.1 * excess[j] * excess[j]; // this 3 parameters are assessed, and weighted by 3 coefficient (to be optimized to get a beautiful sobp) and the value of the global function is returned
-        f_tot = f_tot+f[j];
-    }
-
-    f_tot += 0.005 * sobp_max * num_samples;
-
-    for(int i=0; i < num_peaks; i++)
-    {
-        if (weights[i] < 0)
-        {
-            f_tot = 2* f_tot;
-        }
-    }
-    /*printf("\n f_tot = %lg", f_tot);
-      for (int i = 0; i < num_peaks; i++)
-      {
-      printf (" %lg ", weights[i]);
-      }*/
-
-    return f_tot; //we return the fcost value
-}
-
diff --git a/src/plastimatch/dose/rt_sobp_p.cxx b/src/plastimatch/dose/rt_sobp_p.cxx
deleted file mode 100644
index 3cc00fb..0000000
--- a/src/plastimatch/dose/rt_sobp_p.cxx
+++ /dev/null
@@ -1,131 +0,0 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-#include "plmdose_config.h"
-
-#include "logfile.h"
-#include "rt_depth_dose.h"
-#include "rt_sobp_p.h"
-#include "rt_lut.h"
-
-Rt_sobp_private::Rt_sobp_private ()
-{
-    d_lut = new float[0];
-    e_lut = new float[0];
-	f_lut = new float[0];
-    dres = .01;
-	dose_max = 1.f;
-    num_samples = 0;
-    eres = 1.0;
-    E_min = 0;
-    E_max = 0;
-    dmin = 0.0;
-    dmax = 0.0;
-    dend = 0.0;
-    prescription_dmin = 50.f;
-    prescription_dmax = 100.f;
-    set_particle_type (PARTICLE_TYPE_P);
-}
-
-Rt_sobp_private::Rt_sobp_private (Particle_type particle_type)
-{
-    d_lut = new float[0];
-    e_lut = new float[0];
-    dres = 1.0;
-	dose_max = 1.f;
-    num_samples = 0;
-    eres = 2.0;
-    E_min = 0;
-    E_max = 0;
-    dmin = 0.0;
-    dmax = 0.0;
-    dend = 0.0;
-    prescription_dmin = 50.f;
-    prescription_dmax = 100.f;
-    set_particle_type (particle_type);
-}
-
-Rt_sobp_private::Rt_sobp_private (const Rt_sobp_private* rsp)
-{
-    d_lut = new float[0];
-    e_lut = new float[0];
-    dres = rsp->dres;
-	dose_max = 1.f;
-    num_samples = rsp->num_samples;
-    eres = rsp->eres;
-    E_min = rsp->E_min;
-    E_max = rsp->E_max;
-    dmin = rsp->dmin;
-    dmax = rsp->dmax;
-    dend = rsp->dend;
-    prescription_dmin = rsp->prescription_dmin;
-    prescription_dmax = rsp->prescription_dmax;
-    set_particle_type (rsp->particle_type);
-}
-
-Rt_sobp_private::~Rt_sobp_private ()
-{
-    if (d_lut) delete[] d_lut;
-    if (e_lut) delete[] e_lut;
-	if (f_lut) delete[] f_lut;
-    clear_peaks ();
-}
-
-void
-Rt_sobp_private::set_particle_type (Particle_type particle_type)
-{
-    this->particle_type = particle_type;
-    switch (particle_type) {
-    case PARTICLE_TYPE_P:
-        alpha = particle_parameters[0][0];
-        p = particle_parameters[0][1];
-        break;
-    case PARTICLE_TYPE_HE:
-        alpha = particle_parameters[1][0];
-        p = particle_parameters[1][1];
-        lprintf ("data for helium particle are not available - based on proton beam data");
-        break;
-    case PARTICLE_TYPE_LI:
-        alpha = particle_parameters[2][0];
-        p = particle_parameters[2][1];
-        lprintf ("data for lithium particle type are not available - based on proton beam data");
-        break;
-    case PARTICLE_TYPE_BE:
-        alpha = particle_parameters[3][0];
-        p = particle_parameters[3][1];
-        lprintf ("data for berilium particle type are not available - based on proton beam data");
-        break;
-    case PARTICLE_TYPE_B:
-        alpha = particle_parameters[4][0];
-        p = particle_parameters[4][1];
-        lprintf ("data for bore particle type are not available - based on proton beam data");
-        break;
-    case PARTICLE_TYPE_C:
-        alpha = particle_parameters[5][0];
-        p = particle_parameters[5][1];
-        lprintf ("data for carbon particle type are not available - based on proton beam data");
-        break;
-    case PARTICLE_TYPE_O:
-        alpha = particle_parameters[7][0];
-        p = particle_parameters[7][1];
-        lprintf ("data for oxygen particle type are not available - based on proton beam data");
-        break;
-    default:
-        alpha = particle_parameters[0][0];
-        p = particle_parameters[0][1];
-        particle_type = PARTICLE_TYPE_P;
-        lprintf ("particle not found - proton beam chosen");
-        break;
-    }
-}
-
-void
-Rt_sobp_private::clear_peaks ()
-{
-    std::vector<const Rt_depth_dose*>::iterator it;
-    for (it = depth_dose.begin(); it != depth_dose.end(); ++it) {
-        delete *it;
-    }
-    depth_dose.clear();
-    sobp_weight.clear();
-}
diff --git a/src/plastimatch/dose/rt_sobp_p.h b/src/plastimatch/dose/rt_sobp_p.h
deleted file mode 100644
index 9e2025d..0000000
--- a/src/plastimatch/dose/rt_sobp_p.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-/* -----------------------------------------------------------------------
-   rt_sobp (Ion Spread Out Bragg Peak) is a class that creates 
-   a sobp from minimal and
-   maximal depth to be targeted, or minimal and maximal energies of the
-   pristine Bragg Peaks used to create the sobp. This class return the 
-   weights of each pristine peak or the created sobp. It contains also the
-   optimization process to get the weight of the pristine peaks. 
-   Mother class from which
-   will derive the class for proton and other ions.
-   ----------------------------------------------------------------------- */
-#ifndef _rt_sobp_p_h_
-#define _rt_sobp_p_h_
-
-#include "plmdose_config.h"
-#include <vector>
-#include "particle_type.h"
-
-class Rt_depth_dose;
-
-class Rt_sobp_private {
-public:
-    std::vector<const Rt_depth_dose*> depth_dose;
-
-    float* d_lut;               /* depth array (mm) */
-    float* e_lut;               /* energy array (MeV) */
-	float* f_lut;				/* integrated energy array (MeV) */
-    double dres;
-	float dose_max;
-    int num_samples;	        /* number of depths */
-
-    int eres;			/* energy resolution */
-    int num_peaks;		/* number of peaks */
-
-    std::vector<double> sobp_weight;
-
-    int E_min;			/* lower energy */
-    int E_max;			/* higher energy */
-
-    float dmin;			/* lower depth */
-    float dmax;			/* higher depth */
-    float dend;			/* end of the depth array */
-
-    /* p  & alpha are parameters that bind depth and energy 
-       according to ICRU */
-    Particle_type particle_type;
-    double p;			
-    double alpha;
-
-    float prescription_dmin;
-    float prescription_dmax;
-
-public:
-    Rt_sobp_private ();
-    Rt_sobp_private (Particle_type);
-    Rt_sobp_private (const Rt_sobp_private*);
-    ~Rt_sobp_private ();
-public:
-    void set_particle_type (Particle_type);
-    void clear_peaks ();
-};
-
-#endif
diff --git a/src/plastimatch/dose/wed_parms.cxx b/src/plastimatch/dose/wed_parms.cxx
index 8f3e195..39a13a2 100644
--- a/src/plastimatch/dose/wed_parms.cxx
+++ b/src/plastimatch/dose/wed_parms.cxx
@@ -25,8 +25,6 @@ Wed_Parms::Wed_Parms ()
     this->group = 0;
     this->mode = 0;
     this->ray_step = 1.0f;
-    this->input_ct_fn[0] = '\0';
-    this->output_ct_fn[0] = '\0';
 
     this->src[0] = -1000.f;
     this->src[1] = 0.f;
@@ -95,6 +93,7 @@ Wed_Parms::get_group_lines(char* groupfile)
     return numlines;
 }
 
+#if defined (commentout)
 void
 Wed_Parms::parse_group(int argc, char** argv, int linenumber)
 {
@@ -107,7 +106,6 @@ Wed_Parms::parse_group(int argc, char** argv, int linenumber)
                 while (text.good()) {
                     getline(text,line);
                     if ( (!line.empty()) && (line.compare(0,1,"#")) )  {
-
                         if (linecounter == linenumber)  {
 
                             std::string pvol_file;
@@ -134,9 +132,6 @@ Wed_Parms::parse_group(int argc, char** argv, int linenumber)
                             }
                             else {print_and_exit ("%s is not in <name>.mha format.\n", dose_file.c_str());}
 
-
-                            //	      std::cout<<pvol_file<<" "<<dose_file<<" "<<dose_wed_file<<std::endl;
-
                             this->input_ct_fn = pvol_file;
                             this->input_dose_fn = dose_file.c_str();
 
@@ -144,7 +139,6 @@ Wed_Parms::parse_group(int argc, char** argv, int linenumber)
                             pvol_file.insert (pvol_file.size()-4,"_wed");   
                             this->output_ct_fn = pvol_file;
                             this->output_dose_fn = dose_wed_file.c_str();
-
                         }
                         linecounter++;
                     }
@@ -153,6 +147,7 @@ Wed_Parms::parse_group(int argc, char** argv, int linenumber)
         }
     }
 }
+#endif
 
 int
 Wed_Parms::set_key_val (
@@ -165,83 +160,72 @@ Wed_Parms::set_key_val (
 
         /* [INPUT SETTINGS] */
     case 0:
-        if (!strcmp (key, "ray_step")) {
-            if (sscanf (val, "%f", &this->ray_step) != 1) {
-                goto error_exit;
-            }
-        }
         //Whether wed or reverse, input patient and rpl vol
-        else if (!strcmp (key, "patient")) {
+        if (!strcmp (key, "ct")) {
             this->input_ct_fn = val;
         }
-        else if (!strcmp (key, "rpl_vol")) {
-            this->rpl_vol_fn = val;
+        else if (!strcmp (key, "proj_wed")) {
+            this->input_proj_wed_fn = val;
         }
-	//Any mode will use the skin dose if specified
+        //Any mode will use the skin dose if specified
         else if (!strcmp (key, "skin")) {
-            this->skin_fn = val;
+            this->input_skin_fn = val;
         }
         //If normal wed procedure, input dose
-	if (this->mode==0)  {
-            if (!strcmp (key, "dose")) {
-                this->input_dose_fn = val;
-            }
-	}
-	//If reverse wed procedure, input dose_wed
-	if (this->mode==1)  {
-            if (!strcmp (key, "dose_wed")) {
-                this->input_dose_fn = val;
-            }
-	}
-	//If in depth/segmentation mode, input segment
-	if (this->mode==2)  {
-            if (!strcmp (key, "segment")) {
-                this->input_dose_fn = val;
-            }
-	}
-	
-
+        else if (!strcmp (key, "dose")) {
+            this->input_dose_fn = val;
+        }
+        //If reverse wed procedure, input dose_wed
+        else if (!strcmp (key, "wed_dose")) {
+            this->input_wed_dose_fn = val;
+        }
+        //If in depth/segmentation mode, input segment
+        else if (!strcmp (key, "target")) {
+            this->input_target_fn = val;
+        }
         break;
+        
         /* [OUTPUT SETTINGS] */
     case 1:
-        //If normal wed procedure, output patient_wed and dose_wed
-        if (this->mode==0)  {
-            if (!strcmp (key, "patient_wed")) {
-                this->output_ct_fn = val;
-            }
-            else if (!strcmp (key, "dose_wed")) {
-                this->output_dose_fn = val;
-            }
-	}
-	//If reverse wed  procedure, output only dose
-        if (this->mode==1)  {
-            if (!strcmp (key, "dose")) {
-                this->output_dose_fn = val;
-            }
-	}
-
-	//If in depth/segmentation mode, output depth matrix
-        if (this->mode==2)  {
-            if (!strcmp (key, "depth")) {
-                this->output_depth_fn = val;
-            }
-            else if (!strcmp (key, "aperture")) {
-                this->output_ap_fn = val;
-            }
-	}
-
-	//If in proj wed mode, output projection wed volume
-        if (this->mode==3)  {
-            if (!strcmp (key, "proj_wed")) {
-                this->output_proj_wed_fn = val;
-            }
-	}
-
-
+        if (!strcmp (key, "proj_ct")) {
+            this->output_proj_ct_fn = val;
+        }
+        else if (!strcmp (key, "proj_wed")) {
+            this->output_proj_wed_fn = val;
+        }
+        else if (!strcmp (key, "proj_dose")) {
+            this->output_proj_dose_fn = val;
+        }
+        else if (!strcmp (key, "wed_ct")) {
+            this->output_wed_ct_fn = val;
+        }
+        else if (!strcmp (key, "wed_dose")) {
+            this->output_wed_dose_fn = val;
+        }
+        else if (!strcmp (key, "ct")) {
+            this->output_ct_fn = val;
+        }
+        else if (!strcmp (key, "dew_ct")) {
+            this->output_dew_ct_fn = val;
+        }
+        else if (!strcmp (key, "dew_dose")) {
+            this->output_dew_dose_fn = val;
+        }
+#if defined (commentout)
+        else if (!strcmp (key, "aperture")) {
+            this->output_ap_fn = val;
+        }
+#endif
         break;
+
         /* [BEAM] */
     case 2:
-        if (!strcmp (key, "pos")) {
+        if (!strcmp (key, "ray_step")) {
+            if (sscanf (val, "%f", &this->ray_step) != 1) {
+                goto error_exit;
+            }
+        }
+        else if (!strcmp (key, "pos")) {
             if (sscanf (val, "%f %f %f", 
                     &(this->src[0]), 
                     &(this->src[1]), 
@@ -264,7 +248,6 @@ Wed_Parms::set_key_val (
                 goto error_exit;
             }
         }
-
         break;
 
         /* [APERTURE] */
@@ -300,12 +283,10 @@ Wed_Parms::set_key_val (
             {
                 goto error_exit;
             }
-	    this->have_ires = true;
-
+            this->have_ires = true;
         }
         break;
 
-
         /* [DEW VOLUME] */
     case 4:
         if (!strcmp (key, "dew_dim")) {
@@ -331,16 +312,13 @@ Wed_Parms::set_key_val (
             if (sscanf (val, "%f", &(this->sinogram)) != 1) {
                 goto error_exit;
             }
-
         }
         else if (!strcmp (key, "resolution")) {
-	    if (sscanf (val, "%i", &(this->sinogram_res)) != 1) {
+            if (sscanf (val, "%i", &(this->sinogram_res)) != 1) {
                 goto error_exit;
             }
         }
-
         break;
-
     }
     return 0;
 
@@ -349,8 +327,6 @@ error_exit:
     return -1;
 }
 
-
-
 void
 Wed_Parms::parse_config (
     const char* config_fn
@@ -445,7 +421,7 @@ Wed_Parms::parse_args (int argc, char** argv)
         if (!strcmp (argv[i], "--debug")) {
             this->debug = 1;
         }
-	if (!strcmp (argv[i], "--group")) {
+        if (!strcmp (argv[i], "--group")) {
             if (!argv[i+1])  { //group needs an argument
                 print_usage ();
                 return false;
@@ -455,18 +431,15 @@ Wed_Parms::parse_args (int argc, char** argv)
                 return true;
             }
         }
-	if (!strcmp (argv[i], "--dew")) {
+        if (!strcmp (argv[i], "--dew")) {
             this->mode = 1;
         }
-
-	else if (!strcmp (argv[i], "--segdepth")) {
+        else if (!strcmp (argv[i], "--segdepth")) {
             this->mode = 2;
         }
-
         else if (!strcmp (argv[i], "--projwed")) {
             this->mode = 3;
         }
-
         else {
             print_usage ();
             break;
@@ -479,69 +452,9 @@ Wed_Parms::parse_args (int argc, char** argv)
         this->parse_config (argv[i]);
     }
 
-#if 0
-    if (this->d_lut == NULL) {
-        /* measured bragg curve not supplied, try to generate */
-        if (!this->generate ()) {
-            return false;
-        }
-    }
-    // JAS 2012.08.10
-    //   Hack so that I can reuse the proton code.  The values
-    //   don't actually matter.
-    scene->beam->E0 = 1.0;
-    scene->beam->spread = 1.0;
-    scene->beam->dmax = 1.0;
-#endif
-
     //Input CT always required
-    if (this->input_ct_fn[0] == '\0') {
-        fprintf (stderr, "\n** ERROR: Input patient image not specified in configuration file!\n");
-        return false;
-    }
-
-    //Input "dose" always required
-    if ((this->mode==0)||(this->mode==1))  {
-        if (this->input_dose_fn[0] == '\0') {
-            fprintf (stderr, "\n** ERROR: Input dose not specified in configuration file!\n");
-            return false;
-        }
-    }
-
-    //For wed mode, patient wed name is required.
-    if (this->mode==0)  {
-        if (this->output_ct_fn[0] == '\0') {
-            fprintf (stderr, "\n** ERROR: Output file for patient water equivalent depth volume not specified in configuration file!\n");
-            return false;
-        }
-    }
-
-    //For wed or dew mode, output dose name required.
-    if ((this->mode==0)||(this->mode==1))  {
-	if (this->output_dose_fn[0] == '\0') {
-            fprintf (stderr, "\n** ERROR: Output file for dose volume not specified in configuration file!\n");
-            return false;
-	}
-    }
-
-    //For depth/segmentation  mode, aperture and depth volumes required.
-    if (this->mode==2)  {
-        if (this->output_depth_fn[0] == '\0') {
-            fprintf (stderr, "\n** ERROR: Output file for depths not specified in configuration file!\n");
-            return false;
-        }
-        if (this->output_ap_fn[0] == '\0') {
-            fprintf (stderr, "\n** ERROR: Output file for aperture not specified in configuration file!\n");
-            return false;
-        }
-    }
-
-    //For projection wed mode, proj_wed output volume is required.
-    if (this->mode==3)  {
-        if (this->output_proj_wed_fn[0] == '\0') {
-            fprintf (stderr, "\n** ERROR: Output file for projection wed not specified in configuration file!\n");
-            return false;
-        }
+    if (this->input_ct_fn == "") {
+        print_and_exit ("** ERROR: Input patient image not specified in configuration file!\n");
     }
 
     return true;
diff --git a/src/plastimatch/dose/wed_parms.h b/src/plastimatch/dose/wed_parms.h
index e65e065..ad03b93 100644
--- a/src/plastimatch/dose/wed_parms.h
+++ b/src/plastimatch/dose/wed_parms.h
@@ -13,7 +13,7 @@ public:
     ~Wed_Parms ();
 
     bool parse_args (int argc, char** argv);
-    void parse_group (int argc, char** argv, int line);
+    //void parse_group (int argc, char** argv, int line);
 
 private:
     void parse_config (const char* config_fn);
@@ -24,20 +24,29 @@ public:
     /* [SETTINGS] */
     int debug;
     int group;
-    short mode;                     /*Running in wed, dew, or segdepth?*/
-    bool have_ray_step;
-    float ray_step;                 /* Uniform ray step size (mm) */
-    std::string input_ct_fn;        /* input:  patient volume */
-    std::string input_dose_fn;      /* input:  dose volume */
-    std::string skin_fn;            /* input:  skin matrix */
-    std::string output_ct_fn;       /* output: patient volume */
-    std::string output_dose_fn;     /* output: dose volume */
-    std::string rpl_vol_fn;         /* output: rpl volume */
-    std::string output_ap_fn;       /* output: aperture volume */
-    std::string output_depth_fn;    /* output: depth volume */
-    std::string output_proj_wed_fn;  /* output: projective wed volume */
+    short mode;                      /*Running in wed, dew, or segdepth?*/
+    std::string input_ct_fn;         /* input:  patient volume */
+    std::string input_dose_fn;       /* input:  dose volume */
+    std::string input_proj_ct_fn;    /* input:  ct in proj coordinates */
+    std::string input_proj_wed_fn;   /* input:  wed in proj coordinates */
+    std::string input_wed_dose_fn;   /* input:  dose in wed coordinates */
+    std::string input_target_fn;     /* input:  segment volume */
+    std::string input_skin_fn;       /* input:  skin volume */
+    std::string output_proj_ct_fn;   /* output: ct in proj coordinates */
+    std::string output_proj_wed_fn;  /* output: wed in proj coordinates */
+    std::string output_proj_dose_fn; /* output: dose in proj coordinates */
+    std::string output_wed_ct_fn;    /* output: ct in wed coordinates */
+    std::string output_wed_dose_fn;  /* output: dose in wed coordinates */
+    std::string output_ct_fn;        /* output: ct in world coordinates */
+    std::string output_dew_ct_fn;    /* output: ct in world coordinates */
+    std::string output_dew_dose_fn;  /* output: dose in world coordinates */
+
+//    std::string output_ap_fn;        /* output: aperture volume */
+//    std::string output_depth_fn;     /* output: depth volume */
 
     /* [BEAM] */
+    bool have_ray_step;
+    float ray_step;                  /* Uniform ray step size (mm) */
     float src[3];
     float isocenter[3];
     float beam_res;
@@ -59,7 +68,6 @@ public:
     /* [PROJ VOLUME] */
     float sinogram;
     int sinogram_res;
-
 };
 
 #endif
diff --git a/src/plastimatch/opencl/CMakeLists.txt b/src/plastimatch/opencl/CMakeLists.txt
index 78a4ec3..d5eef58 100755
--- a/src/plastimatch/opencl/CMakeLists.txt
+++ b/src/plastimatch/opencl/CMakeLists.txt
@@ -22,5 +22,6 @@ if (OPENCL_FOUND)
     "${PLMOPENCL_LIBRARY_SRC}" 
     "${PLMOPENCL_LIBRARY_DEPENDENCIES}"
     "${PLMOPENCL_LIBRARY_LDFLAGS}"
+    "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
     "")
 endif ()
diff --git a/src/plastimatch/plastimatch_version.txt b/src/plastimatch/plastimatch_version.txt
index 7a2a8af..a924a6c 100644
--- a/src/plastimatch/plastimatch_version.txt
+++ b/src/plastimatch/plastimatch_version.txt
@@ -1 +1 @@
-1.6.2 (5130)
\ No newline at end of file
+1.6.3 (5341)
\ No newline at end of file
diff --git a/src/plastimatch/reconstruct/CMakeLists.txt b/src/plastimatch/reconstruct/CMakeLists.txt
index a19d5e8..66b0bd8 100644
--- a/src/plastimatch/reconstruct/CMakeLists.txt
+++ b/src/plastimatch/reconstruct/CMakeLists.txt
@@ -77,6 +77,7 @@ plm_add_library (
   "${PLMRECONSTRUCT_LIBRARY_SRC}" 
   "${PLMRECONSTRUCT_LIBRARY_DEPENDENCIES}"
   "${PLMRECONSTRUCT_LIBRARY_LDFLAGS}"
+  "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
   "")
 
 
diff --git a/src/plastimatch/reconstruct/cuda/CMakeLists.txt b/src/plastimatch/reconstruct/cuda/CMakeLists.txt
index 70a285e..e8810b1 100644
--- a/src/plastimatch/reconstruct/cuda/CMakeLists.txt
+++ b/src/plastimatch/reconstruct/cuda/CMakeLists.txt
@@ -77,6 +77,7 @@ if (CUDA_FOUND)
       "${PLMRECONSTRUCTCUDA_LIBRARY_SRC}"
       ""
       ""
+      "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
       "")
   endif ()
 endif ()
diff --git a/src/plastimatch/reconstruct/drr.h b/src/plastimatch/reconstruct/drr.h
index 6c2dbf4..f4393e6 100644
--- a/src/plastimatch/reconstruct/drr.h
+++ b/src/plastimatch/reconstruct/drr.h
@@ -40,6 +40,7 @@ public:
     int image_window[4];             /* In pixels */
     float isocenter[3];              /* In mm */
 
+    float start_angle;               /* Source gantry angle */
     int num_angles;
     int have_angle_diff;             /* Was angle_diff spec'd in options? */
     float angle_diff;                /* In degrees */
@@ -54,23 +55,28 @@ public:
     int exponential_mapping;
     int output_format;
     bool preprocess_attenuation;
-    std::string output_details_fn;
     Drr_algorithm algorithm;
     char* input_file;
     int geometry_only;
     char* output_prefix;
+
+    /* The option specified by the user goes in output_details_prefix, 
+       and the individual filename for a specific angle goes in 
+       output_details_fn */
+    std::string output_details_prefix;
+    std::string output_details_fn;
 };
 PLMRECONSTRUCT_C_API void drr_render_volume_perspective (
-        Proj_image *proj,
-        Volume *vol, 
-        double ps[2], 
-        void *dev_state, 
-        Drr_options *options
+    Proj_image *proj,
+    Volume *vol, 
+    double ps[2], 
+    void *dev_state, 
+    Drr_options *options
 );
 PLMRECONSTRUCT_C_API void drr_preprocess_attenuation (Volume* vol);
 PLMRECONSTRUCT_C_API void preprocess_attenuation_and_drr_render_volume_cl (
-        Volume* vol,
-        Drr_options* options
+    Volume* vol,
+    Drr_options* options
 );
 
 #endif
diff --git a/src/plastimatch/register/CMakeLists.txt b/src/plastimatch/register/CMakeLists.txt
index af31ea2..7fad5df 100644
--- a/src/plastimatch/register/CMakeLists.txt
+++ b/src/plastimatch/register/CMakeLists.txt
@@ -68,7 +68,8 @@ set (PLMREGISTER_LIBRARY_SRC
   rbf_wendland.cxx
   registration.cxx registration.h
   registration_data.cxx
-  registration_parms.cxx
+  registration_metric_type.cxx registration_metric_type.h
+  registration_parms.cxx registration_parms.h
   registration_resample.cxx registration_resample.h 
   shared_parms.cxx
   stage_parms.cxx
@@ -155,7 +156,7 @@ endif ()
 
 # bspline registration benefits from SSE2
 if (SSE2_FOUND AND NOT BUILD_AGAINST_SLICER3)
-  plm_set_sse2_flags (bspline.cxx bspline_mi.cxx bspline_mse.cxx)
+  plm_set_sse2_flags (bspline.cxx bspline_gm.cxx bspline_mi.cxx bspline_mse.cxx)
 endif ()
 
 ##-----------------------------------------------------------------------------
@@ -166,6 +167,7 @@ plm_add_library (
   "${PLMREGISTER_LIBRARY_SRC}" 
   "${PLMREGISTER_LIBRARY_DEPENDENCIES}"
   "${PLMREGISTER_LIBRARY_LDFLAGS}"
+  "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
   "")
 
 # because plmregistercuda is dynamically loaded (not linked)
diff --git a/src/plastimatch/register/bspline.cxx b/src/plastimatch/register/bspline.cxx
index b99a869..de644ee 100644
--- a/src/plastimatch/register/bspline.cxx
+++ b/src/plastimatch/register/bspline.cxx
@@ -34,9 +34,6 @@
 #endif
 
 #include "bspline.h"
-#if (CUDA_FOUND)
-#include "bspline_cuda.h"
-#endif
 #include "bspline_gm.h"
 #include "bspline_interpolate.h"
 #include "bspline_landmarks.h"
@@ -53,6 +50,8 @@
 #include "interpolate_macros.h"
 #include "logfile.h"
 #include "plm_math.h"
+#include "plm_timer.h"
+#include "print_and_exit.h"
 #include "string_util.h"
 #include "volume.h"
 #include "volume_macros.h"
@@ -147,7 +146,7 @@ void find_knots (
    Debugging routines
    ----------------------------------------------------------------------- */
 void
-dump_gradient (Bspline_xform* bxf, Bspline_score* ssd, const char* fn)
+dump_total_gradient (Bspline_xform* bxf, Bspline_score* ssd, const char* fn)
 {
     int i;
     FILE* fp;
@@ -155,7 +154,7 @@ dump_gradient (Bspline_xform* bxf, Bspline_score* ssd, const char* fn)
     make_parent_directories (fn);
     fp = fopen (fn, "wb");
     for (i = 0; i < bxf->num_coeff; i++) {
-        fprintf (fp, "%20.20f\n", ssd->grad[i]);
+        fprintf (fp, "%20.20f\n", ssd->total_grad[i]);
     }
     fclose (fp);
 }
@@ -189,20 +188,15 @@ bspline_save_debug_state (
         std::string fn;
         char buf[1024];
 
-        if (parms->metric_type[0] == REGISTRATION_METRIC_MI_MATTES) {
-            sprintf (buf, "%02d_grad_mi_%03d_%03d.txt", 
-                parms->debug_stage, bst->it, bst->feval);
-        } else {
-            sprintf (buf, "%02d_grad_mse_%03d_%03d.txt", 
-                parms->debug_stage, bst->it, bst->feval);
-        }
+        sprintf (buf, "%02d_grad_%03d_%03d.txt", 
+            parms->debug_stage, bst->it, bst->feval);
         fn = parms->debug_dir + "/" + buf;
-        dump_gradient (bxf, &bst->ssd, fn.c_str());
+        dump_total_gradient (bxf, &bst->ssd, fn.c_str());
 
         sprintf (buf, "%02d_coeff_%03d_%03d.txt", 
             parms->debug_stage, bst->it, bst->feval);
         fn = parms->debug_dir + "/" + buf;
-        bspline_xform_save (bxf, fn.c_str());
+        bxf->save (fn.c_str());
 
         if (parms->metric_type[0] == REGISTRATION_METRIC_MI_MATTES) {
             sprintf (buf, "%02d_", parms->debug_stage);
@@ -296,72 +290,27 @@ bspline_sort_sets (float* cond_x, float* cond_y, float* cond_z,
 }
 
 void
-bspline_update_grad (
-    Bspline_state *bst, 
-    Bspline_xform* bxf, 
-    plm_long p[3], plm_long qidx, float dc_dv[3])
+bspline_condense_smetric_grad (float* cond_x, float* cond_y, float* cond_z,
+    Bspline_xform* bxf, Bspline_score* ssd)
 {
-    Bspline_score* ssd = &bst->ssd;
-    plm_long i, j, k, m;
-    plm_long cidx;
-    float* q_lut = &bxf->q_lut[qidx*64];
-
-    m = 0;
-    for (k = 0; k < 4; k++) {
-        for (j = 0; j < 4; j++) {
-            for (i = 0; i < 4; i++) {
-                cidx = (p[2] + k) * bxf->cdims[1] * bxf->cdims[0]
-                        + (p[1] + j) * bxf->cdims[0]
-                        + (p[0] + i);
-                cidx = cidx * 3;
-                ssd->grad[cidx+0] += dc_dv[0] * q_lut[m];
-                ssd->grad[cidx+1] += dc_dv[1] * q_lut[m];
-                ssd->grad[cidx+2] += dc_dv[2] * q_lut[m];
-                m ++;
-            }
-        }
-    }
-}
+    plm_long kidx, sidx;
 
-void
-bspline_update_grad_b (
-    Bspline_score* bscore,
-    const Bspline_xform* bxf, 
-    plm_long pidx, 
-    plm_long qidx, 
-    const float dc_dv[3])
-{
-    plm_long i, j, k, m;
-    plm_long cidx;
-    float* q_lut = &bxf->q_lut[qidx*64];
-    plm_long* c_lut = &bxf->c_lut[pidx*64];
-
-    m = 0;
-    for (k = 0; k < 4; k++) {
-        for (j = 0; j < 4; j++) {
-            for (i = 0; i < 4; i++) {
-                cidx = 3 * c_lut[m];
-                bscore->grad[cidx+0] += dc_dv[0] * q_lut[m];
-                bscore->grad[cidx+1] += dc_dv[1] * q_lut[m];
-                bscore->grad[cidx+2] += dc_dv[2] * q_lut[m];
-                m ++;
-            }
+    for (kidx=0; kidx < bxf->num_knots; kidx++) {
+        for (sidx=0; sidx<64; sidx++) {
+            ssd->smetric_grad[3*kidx + 0] += cond_x[64*kidx + sidx];
+            ssd->smetric_grad[3*kidx + 1] += cond_y[64*kidx + sidx];
+            ssd->smetric_grad[3*kidx + 2] += cond_z[64*kidx + sidx];
         }
     }
 }
 
-void
-bspline_condense_grad (float* cond_x, float* cond_y, float* cond_z,
-    Bspline_xform* bxf, Bspline_score* ssd)
+static void
+logfile_print_score (float score)
 {
-    plm_long kidx, sidx;
-
-    for (kidx=0; kidx < (bxf->cdims[0] * bxf->cdims[1] * bxf->cdims[2]); kidx++) {
-        for (sidx=0; sidx<64; sidx++) {
-            ssd->grad[3*kidx + 0] += cond_x[64*kidx + sidx];
-            ssd->grad[3*kidx + 1] += cond_y[64*kidx + sidx];
-            ssd->grad[3*kidx + 2] += cond_z[64*kidx + sidx];
-        }
+    if (score < 10. && score > -10.) {
+        logfile_printf (" %1.8f ", score);
+    } else {
+        logfile_printf (" %9.3f ", score);
     }
 }
 
@@ -373,50 +322,62 @@ report_score (
 ) 
 {
     Bspline_score* ssd = &bst->ssd;
-    Reg_parms* reg_parms = parms->reg_parms;
+    Regularization_parms* reg_parms = parms->reg_parms;
     Bspline_landmarks* blm = parms->blm;
 
     int i;
-    float ssd_grad_norm, ssd_grad_mean;
+    double ssd_grad_norm, ssd_grad_mean;
 
-    /* Normalize gradient */
+    /* Compute gradient statistics */
     ssd_grad_norm = 0;
     ssd_grad_mean = 0;
     for (i = 0; i < bxf->num_coeff; i++) {
-        ssd_grad_mean += bst->ssd.grad[i];
-        ssd_grad_norm += fabs (bst->ssd.grad[i]);
+        ssd_grad_mean += bst->ssd.total_grad[i];
+        ssd_grad_norm += (double) bst->ssd.total_grad[i]
+            * (double) bst->ssd.total_grad[i];
     }
 
-    /* First line, part 1 - iterations */
+    /* Compute total time */
+    double total_time = 0;
+    std::vector<double>::const_iterator it_time = ssd->time_smetric.begin();
+    while (it_time != ssd->time_smetric.end()) {
+        total_time += *it_time;
+        ++it_time;
+    }
+    total_time += ssd->time_rmetric;
+    
+    /* First line, iterations, score, misc stats */
     logfile_printf ("[%2d,%3d] ", bst->it, bst->feval);
-    /* First line, part 2 - score 
-       JAS 04.19.2010 MI scores are between 0 and 1
-       The extra decimal point resolution helps in seeing
-       if the optimizer is performing adequately. */
-    if (reg_parms->lambda > 0 || blm->num_landmarks > 0) {
+    if (reg_parms->lambda > 0 || blm->num_landmarks > 0
+        || parms->metric_type.size() > 1)
+    {
         logfile_printf ("SCORE ");
-    } else if (parms->metric_type[0] == REGISTRATION_METRIC_MI_MATTES) {
-        logfile_printf ("MI  ");
     } else {
-        logfile_printf ("MSE ");
+        logfile_printf ("%-6s", registration_metric_type_string (
+                parms->metric_type[0]));
     }
-    if (parms->metric_type[0] == REGISTRATION_METRIC_MI_MATTES) {
-        logfile_printf ("%1.8f ", ssd->score);
-    } else {
-        logfile_printf ("%9.3f ", ssd->score);
-    }
-    /* First line, part 3 - misc stats */
+    logfile_print_score (ssd->score);
     logfile_printf (
-        "NV %6d GM %9.3f GN %9.3f [ %9.3f s ]\n",
-        ssd->num_vox, ssd_grad_mean, ssd_grad_norm, 
-        ssd->time_smetric + ssd->time_rmetric);
-
+        "NV %6d GM %9.3f GN %9.3g [ %9.3f s ]\n",
+        ssd->num_vox, ssd_grad_mean, sqrt (ssd_grad_norm), total_time);
+    
+    /* Second line - smetric(s) */
+    if (ssd->smetric.size() > 1) {
+        std::vector<float>::const_iterator it_sm = ssd->smetric.begin();
+        std::vector<Registration_metric_type>::const_iterator it_st
+            = parms->metric_type.begin();
+        logfile_printf ("         ");
+        while (it_sm != ssd->smetric.end()) {
+            logfile_printf ("%-6s",
+                registration_metric_type_string (*it_st));
+            logfile_print_score (*it_sm);
+            ++it_sm, ++it_st;
+        }
+        logfile_printf ("\n");
+    }
+    
     /* Second line - extra stats if regularization is enabled */
     if (reg_parms->lambda > 0 || blm->num_landmarks > 0) {
-        /* Part 1 - similarity metric */
-        logfile_printf (
-            "         %s %9.3f ", 
-            (parms->metric_type[0] == REGISTRATION_METRIC_MI_MATTES) ? "MI   " : "MSE  ", ssd->smetric);
         /* Part 2 - regularization metric */
         if (reg_parms->lambda > 0) {
             logfile_printf ("RM %9.3f ", 
@@ -430,14 +391,13 @@ report_score (
         /* Part 4 - timing */
         if (reg_parms->lambda > 0) {
             logfile_printf ("[ %9.3f | %9.3f ]\n", 
-                ssd->time_smetric, ssd->time_rmetric);
+                ssd->time_smetric[0], ssd->time_rmetric);
         } else {
             logfile_printf ("\n");
         }
     }
 }
 
-
 void
 bspline_score (Bspline_optimize *bod)
 {
@@ -445,26 +405,46 @@ bspline_score (Bspline_optimize *bod)
     Bspline_state *bst = bod->get_bspline_state ();
     Bspline_xform *bxf = bod->get_bspline_xform ();
 
-    Reg_parms* reg_parms = parms->reg_parms;
+    Regularization_parms* reg_parms = parms->reg_parms;
     Bspline_landmarks* blm = parms->blm;
 
     /* Zero out the score for this iteration */
     bst->ssd.reset_score ();
 
-    if (parms->metric_type[0] == REGISTRATION_METRIC_MSE) {
-        bspline_score_mse (bod);
-    }
-    else if (parms->metric_type[0] == REGISTRATION_METRIC_MI_MATTES) {
-        bspline_score_mi (bod);
-    }
-    else if (parms->metric_type[0] == REGISTRATION_METRIC_GM) {
-        bspline_score_gm (bod);
-    }
-    else {
-        /* ?? */
+    /* Compute similarity metrics */
+    std::vector<Registration_metric_type>::const_iterator it_metric
+        = parms->metric_type.begin();
+    std::vector<float>::const_iterator it_lambda
+        = parms->metric_lambda.begin();
+    bst->sm = 0;
+    while (it_metric != parms->metric_type.end()
+        && it_lambda != parms->metric_lambda.end())
+    {
+        Plm_timer timer;
+        timer.start ();
+        bst->ssd.smetric.push_back (0.f);
+        if (*it_metric == REGISTRATION_METRIC_MSE) {
+            bspline_score_mse (bod);
+        }
+        else if (*it_metric == REGISTRATION_METRIC_MI_MATTES) {
+            bspline_score_mi (bod);
+        }
+        else if (*it_metric == REGISTRATION_METRIC_GM) {
+            bspline_score_gm (bod);
+        }
+        else {
+            print_and_exit ("Unknown similarity metric in bspline_score()\n");
+        }
+
+        bst->ssd.accumulate_grad (*it_lambda);
+
+        bst->ssd.time_smetric.push_back (timer.report ());
+        bst->sm ++;
+        ++it_metric;
+        ++it_lambda;
     }
 
-    /* Regularize */
+    /* Compute regularization */
     if (reg_parms->lambda > 0.0f) {
         bst->rst.compute_score (&bst->ssd, reg_parms, bxf);
     }
@@ -474,8 +454,8 @@ bspline_score (Bspline_optimize *bod)
         bspline_landmarks_score (parms, bst, bxf);
     }
 
-    /* Compute total score to send of optimizer */
-    bst->ssd.score = bst->ssd.smetric + reg_parms->lambda * bst->ssd.rmetric;
+    /* Compute total score */
+    bst->ssd.score = bst->ssd.smetric[0] + reg_parms->lambda * bst->ssd.rmetric;
     if (blm->num_landmarks > 0) {
         bst->ssd.score += blm->landmark_stiffness * bst->ssd.lmetric;
     }
diff --git a/src/plastimatch/register/bspline.h b/src/plastimatch/register/bspline.h
index 6ad1b02..6c30b0f 100644
--- a/src/plastimatch/register/bspline.h
+++ b/src/plastimatch/register/bspline.h
@@ -19,18 +19,6 @@ class Volume;
 PLMREGISTER_API Volume* bspline_compute_vf (const Bspline_xform* bxf);
 void bspline_display_coeff_stats (Bspline_xform* bxf);
 PLMREGISTER_API void bspline_score (Bspline_optimize *bod);
-void bspline_update_grad (
-    Bspline_state *bst, 
-    Bspline_xform* bxf, 
-    plm_long p[3], plm_long qidx, float dc_dv[3]
-);
-void bspline_update_grad_b (
-    Bspline_score* bscore,
-    const Bspline_xform* bxf, 
-    plm_long pidx, 
-    plm_long qidx, 
-    const float dc_dv[3]
-);
 int* calc_offsets (int* tile_dims, int* cdims);
 void find_knots (plm_long* knots, plm_long tile_num, plm_long* rdims, plm_long* cdims);
 void report_score (
@@ -40,14 +28,13 @@ void report_score (
 );
 
 /* Debugging routines */
-void dump_gradient (Bspline_xform* bxf, Bspline_score* ssd, char* fn);
 PLMREGISTER_API void bspline_save_debug_state (
     Bspline_parms *parms, 
     Bspline_state *bst, 
     Bspline_xform* bxf
 );
 void dump_xpm_hist (Bspline_mi_hist_set* mi_hist, char* file_base, int iter);
-void bspline_condense_grad (
+void bspline_condense_smetric_grad (
     float* cond_x, float* cond_y, float* cond_z,
     Bspline_xform* bxf,
     Bspline_score* ssd
diff --git a/src/plastimatch/register/bspline_gm.cxx b/src/plastimatch/register/bspline_gm.cxx
index 1c88ff5..578879e 100644
--- a/src/plastimatch/register/bspline_gm.cxx
+++ b/src/plastimatch/register/bspline_gm.cxx
@@ -49,15 +49,6 @@ bspline_score_k_gm (
     Bspline_optimize *bod
 )
 {
-    /* The timer should be moved back into bspline_loop, however 
-       it requires that start/end routines for bspline_loop_user 
-       have consistent interface for all users */
-       
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
-    Bspline_score *ssd = &bod->get_bspline_state()->ssd;
-
     /* Create/initialize bspline_loop_user */
     Bspline_gm_k blu (bod);
 
@@ -66,9 +57,6 @@ bspline_score_k_gm (
 
     /* Normalize score for MSE */
     bspline_score_normalize (bod, blu.score_acc);
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 void
diff --git a/src/plastimatch/register/bspline_gm.txx b/src/plastimatch/register/bspline_gm.txx
index 27c1792..a14c55c 100755
--- a/src/plastimatch/register/bspline_gm.txx
+++ b/src/plastimatch/register/bspline_gm.txx
@@ -69,7 +69,7 @@ public:
         dc_dv[2] = diff * m_grad[3*mvr+2];  /* z component */
 
         /* Update cost function gradient */
-        bspline_update_grad_b (ssd, bxf, pidx, qidx, dc_dv);
+        ssd->update_smetric_grad_b (bxf, pidx, qidx, dc_dv);
         ssd->num_vox++;
     }
 };
diff --git a/src/plastimatch/register/bspline_landmarks.cxx b/src/plastimatch/register/bspline_landmarks.cxx
index dc59011..30a12ba 100644
--- a/src/plastimatch/register/bspline_landmarks.cxx
+++ b/src/plastimatch/register/bspline_landmarks.cxx
@@ -111,7 +111,7 @@ bspline_landmarks_score_a (
         dc_dv[0] = - land_grad_coeff * diff[0];
         dc_dv[1] = - land_grad_coeff * diff[1];
         dc_dv[2] = - land_grad_coeff * diff[2];
-        bspline_update_grad (bst, bxf, p, qidx, dc_dv);
+        ssd->update_total_grad (bxf, p, qidx, dc_dv);
 
 #if defined (commentout)
         /* Note: Slicer landmarks are in RAS coordinates. Change LPS to RAS */
diff --git a/src/plastimatch/register/bspline_loop.txx b/src/plastimatch/register/bspline_loop.txx
index 0b52af9..0236260 100755
--- a/src/plastimatch/register/bspline_loop.txx
+++ b/src/plastimatch/register/bspline_loop.txx
@@ -253,8 +253,8 @@ bspline_loop_tile_serial (
                     // Compute physical coordinates of fixed image voxel
                     /* To remove DCOS support, switch to 
                        GET_REAL_SPACE_COORDS (fxyz, fijk, bxf); */
-                    GET_WORLD_COORDS (fxyz, fijk, 
-                        fixed, bxf);
+                    POSITION_FROM_COORDS (fxyz, fijk, bxf->img_origin, 
+                        fixed->step);
 
                     // Construct the image volume index
                     fidx = volume_index (fixed->dim, fijk);
@@ -325,7 +325,7 @@ bspline_loop_tile_serial (
      * The number of total bins is equal to the number of control
      * points in the control grid.
      */
-    bspline_condense_grad (cond_x, cond_y, cond_z, bxf, ssd);
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z, bxf, ssd);
 
     free (cond_x);
     free (cond_y);
@@ -462,8 +462,8 @@ bspline_loop_tile_parallel (
                     // Compute physical coordinates of fixed image voxel
                     /* To remove DCOS support, switch to 
                        GET_REAL_SPACE_COORDS (fxyz, fijk, bxf); */
-                    GET_WORLD_COORDS (fxyz, fijk, 
-                        fixed, bxf);
+                    POSITION_FROM_COORDS (fxyz, fijk, bxf->img_origin, 
+                        fixed->step);
 
                     // Construct the image volume index
                     fidx = volume_index (fixed->dim, fijk);
@@ -534,7 +534,7 @@ bspline_loop_tile_parallel (
      * The number of total bins is equal to the number of control
      * points in the control grid.
      */
-    bspline_condense_grad (cond_x, cond_y, cond_z, bxf, ssd);
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z, bxf, ssd);
 
     free (cond_x);
     free (cond_y);
diff --git a/src/plastimatch/register/bspline_mi.cxx b/src/plastimatch/register/bspline_mi.cxx
index 3cf5eca..5091f60 100644
--- a/src/plastimatch/register/bspline_mi.cxx
+++ b/src/plastimatch/register/bspline_mi.cxx
@@ -30,7 +30,6 @@
 #include "logfile.h"
 #include "mha_io.h"
 #include "plm_math.h"
-#include "plm_timer.h"
 #include "volume_macros.h"
 #include "volume.h"
 #include "xpm.h"
@@ -981,7 +980,7 @@ bspline_mi_pvi_8_dc_dv (
         idx_jbin = offset_fbin + idx_mbin;
         if (j_hist[idx_jbin] > 0.0001) {
             dS_dP = logf((num_vox_f * j_hist[idx_jbin]) 
-                / (f_hist[idx_fbin] * m_hist[idx_mbin])) - ssd->smetric;
+                / (f_hist[idx_fbin] * m_hist[idx_mbin])) - ssd->smetric[0];
             dc_dv[0] -= dw[3*idx_pv+0] * dS_dP;
             dc_dv[1] -= dw[3*idx_pv+1] * dS_dP;
             dc_dv[2] -= dw[3*idx_pv+2] * dS_dP;
@@ -1043,11 +1042,12 @@ bspline_mi_pvi_6_dc_dv (
         mkqs, fzqs);
 
     /* PARTIAL VALUE INTERPOLATION - 6 neighborhood */
+    float smetric = ssd->smetric[0];
     midx_f = (mkqs[1] * moving->dim[1] + mjqs[1]) * moving->dim[0] + miqs[1];
     bspline_mi_hist_lookup (j_idxs, m_idxs, f_idxs, fxs, 
         mi_hist, f_img[fidx], m_img[midx_f]);
     dS_dP = compute_dS_dP (j_hist, f_hist, m_hist, j_idxs, f_idxs, m_idxs, 
-        num_vox_f, fxs, ssd->smetric, debug);
+        num_vox_f, fxs, smetric, debug);
     dc_dv[0] += - fxqs[1] * dS_dP;
     dc_dv[1] += - fyqs[1] * dS_dP;
     dc_dv[2] += - fzqs[1] * dS_dP;
@@ -1056,42 +1056,42 @@ bspline_mi_pvi_6_dc_dv (
     bspline_mi_hist_lookup (j_idxs, m_idxs, f_idxs, fxs, 
         mi_hist, f_img[fidx], m_img[midx_f]);
     dS_dP = compute_dS_dP (j_hist, f_hist, m_hist, j_idxs, f_idxs, m_idxs, 
-        num_vox_f, fxs, ssd->smetric, debug);
+        num_vox_f, fxs, smetric, debug);
     dc_dv[0] += - fxqs[0] * dS_dP;
 
     midx_f = (mkqs[1] * moving->dim[1] + mjqs[1]) * moving->dim[0] + miqs[2];
     bspline_mi_hist_lookup (j_idxs, m_idxs, f_idxs, fxs, 
         mi_hist, f_img[fidx], m_img[midx_f]);
     dS_dP = compute_dS_dP (j_hist, f_hist, m_hist, j_idxs, f_idxs, m_idxs, 
-        num_vox_f, fxs, ssd->smetric, debug);
+        num_vox_f, fxs, smetric, debug);
     dc_dv[0] += - fxqs[2] * dS_dP;
 
     midx_f = (mkqs[1] * moving->dim[1] + mjqs[0]) * moving->dim[0] + miqs[1];
     bspline_mi_hist_lookup (j_idxs, m_idxs, f_idxs, fxs, 
         mi_hist, f_img[fidx], m_img[midx_f]);
     dS_dP = compute_dS_dP (j_hist, f_hist, m_hist, j_idxs, f_idxs, m_idxs, 
-        num_vox_f, fxs, ssd->smetric, debug);
+        num_vox_f, fxs, smetric, debug);
     dc_dv[1] += - fyqs[0] * dS_dP;
 
     midx_f = (mkqs[1] * moving->dim[1] + mjqs[2]) * moving->dim[0] + miqs[1];
     bspline_mi_hist_lookup (j_idxs, m_idxs, f_idxs, fxs, 
         mi_hist, f_img[fidx], m_img[midx_f]);
     dS_dP = compute_dS_dP (j_hist, f_hist, m_hist, j_idxs, f_idxs, m_idxs, 
-        num_vox_f, fxs, ssd->smetric, debug);
+        num_vox_f, fxs, smetric, debug);
     dc_dv[1] += - fyqs[2] * dS_dP;
 
     midx_f = (mkqs[0] * moving->dim[1] + mjqs[1]) * moving->dim[0] + miqs[1];
     bspline_mi_hist_lookup (j_idxs, m_idxs, f_idxs, fxs, 
         mi_hist, f_img[fidx], m_img[midx_f]);
     dS_dP = compute_dS_dP (j_hist, f_hist, m_hist, j_idxs, f_idxs, m_idxs, 
-        num_vox_f, fxs, ssd->smetric, debug);
+        num_vox_f, fxs, smetric, debug);
     dc_dv[2] += - fzqs[0] * dS_dP;
 
     midx_f = (mkqs[2] * moving->dim[1] + mjqs[1]) * moving->dim[0] + miqs[1];
     bspline_mi_hist_lookup (j_idxs, m_idxs, f_idxs, fxs, 
         mi_hist, f_img[fidx], m_img[midx_f]);
     dS_dP = compute_dS_dP (j_hist, f_hist, m_hist, j_idxs, f_idxs, m_idxs, 
-        num_vox_f, fxs, ssd->smetric, debug);
+        num_vox_f, fxs, smetric, debug);
     dc_dv[2] += - fzqs[2] * dS_dP;
 
     dc_dv[0] = dc_dv[0] / moving->spacing[0] / num_vox_f;
@@ -1146,9 +1146,6 @@ bspline_score_i_mi (
     float* cond_y = (float*)malloc(cond_size);
     float* cond_z = (float*)malloc(cond_size);
 
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     memset (f_hist, 0, mi_hist->fixed.bins * sizeof(double));
     memset (m_hist, 0, mi_hist->moving.bins * sizeof(double));
     memset (j_hist, 0, mi_hist->fixed.bins * mi_hist->moving.bins * sizeof(double));
@@ -1327,7 +1324,7 @@ bspline_score_i_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist_score_omp (mi_hist, ssd->num_vox);
+    ssd->smetric[0] = mi_hist_score_omp (mi_hist, ssd->num_vox);
     num_vox_f = (float) ssd->num_vox;
 
     /* PASS 2 - Compute Gradient (Parallel across tiles) */
@@ -1414,7 +1411,7 @@ bspline_score_i_mi (
 
     /* Now we have a ton of bins and each bin's 64 slots are full.
      * Let's sum each bin's 64 slots.  The result with be dc_dp. */
-    bspline_condense_grad (cond_x, cond_y, cond_z,
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z,
         bxf, ssd);
 
     free (cond_x);
@@ -1428,9 +1425,6 @@ bspline_score_i_mi (
 #endif
 
     mse_score = mse_score / ssd->num_vox;
-    
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 #endif
 
@@ -1504,9 +1498,6 @@ bspline_score_h_mi (
         it ++;
     }
 
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     memset (f_hist, 0, mi_hist->fixed.bins * sizeof(double));
     memset (m_hist, 0, mi_hist->moving.bins * sizeof(double));
     memset (j_hist, 0, mi_hist->fixed.bins * mi_hist->moving.bins * sizeof(double));
@@ -1589,7 +1580,7 @@ bspline_score_h_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist_score_omp (mi_hist, ssd->num_vox);
+    ssd->smetric[0] = mi_hist_score_omp (mi_hist, ssd->num_vox);
     num_vox_f = (float) ssd->num_vox;
 
     /* PASS 2 - Compute Gradient (Parallel across tiles) */
@@ -1633,7 +1624,8 @@ bspline_score_h_mi (
                     if (fijk[2] >= bxf->roi_offset[2] + bxf->roi_dim[2]) { continue; }
 
                     /* Compute space coordinates of fixed image voxel */
-                    GET_WORLD_COORDS (fxyz, fijk, fixed, bxf);
+                    POSITION_FROM_COORDS (fxyz, fijk, bxf->img_origin, 
+                        fixed->step);
 
                     /* JAS 2012.03.26: Tends to break the optimizer (PGTOL)   */
                     /* Check to make sure the indices are valid (inside roi) */
@@ -1694,7 +1686,7 @@ bspline_score_h_mi (
 
     /* Now we have a ton of bins and each bin's 64 slots are full.
      * Let's sum each bin's 64 slots.  The result will be dc_dp. */
-    bspline_condense_grad (cond_x, cond_y, cond_z, bxf, ssd);
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z, bxf, ssd);
 
     free (cond_x);
     free (cond_y);
@@ -1708,9 +1700,6 @@ bspline_score_h_mi (
     if (parms->debug) {
         printf ("<< MSE %3.3f >>\n", mse_score);
     }
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 
@@ -1774,9 +1763,6 @@ bspline_score_g_mi (
     }
 #endif
 
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     memset (f_hist, 0, mi_hist->fixed.bins * sizeof(double));
     memset (m_hist, 0, mi_hist->moving.bins * sizeof(double));
     memset (j_hist, 0, mi_hist->fixed.bins * mi_hist->moving.bins * sizeof(double));
@@ -1841,7 +1827,7 @@ bspline_score_g_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist_score_omp (mi_hist, ssd->num_vox);
+    ssd->smetric[0] = mi_hist_score_omp (mi_hist, ssd->num_vox);
     num_vox_f = (float) ssd->num_vox;
 
     /* PASS 2 - Compute Gradient (Parallel across tiles) */
@@ -1885,7 +1871,8 @@ bspline_score_g_mi (
                     if (fijk[2] >= bxf->roi_offset[2] + bxf->roi_dim[2]) { continue; }
 
                     /* Compute space coordinates of fixed image voxel */
-                    GET_WORLD_COORDS (fxyz, fijk, fixed, bxf);
+                    POSITION_FROM_COORDS (fxyz, fijk, bxf->img_origin, 
+                        fixed->step);
 
                     /* Compute deformation vector (dxyz) for voxel */
                     bspline_interp_pix_c (dxyz, bxf, pidx, q);
@@ -1938,7 +1925,7 @@ bspline_score_g_mi (
 
     /* Now we have a ton of bins and each bin's 64 slots are full.
      * Let's sum each bin's 64 slots.  The result will be dc_dp. */
-    bspline_condense_grad (cond_x, cond_y, cond_z, bxf, ssd);
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z, bxf, ssd);
 
     free (cond_x);
     free (cond_y);
@@ -1954,9 +1941,6 @@ bspline_score_g_mi (
     if (parms->debug) {
         printf ("<< MSE %3.3f >>\n", mse_score);
     }
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 
@@ -2012,9 +1996,6 @@ bspline_score_f_mi (
     }
 #endif
 
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     memset (f_hist, 0, mi_hist->fixed.bins * sizeof(double));
     memset (m_hist, 0, mi_hist->moving.bins * sizeof(double));
     memset (j_hist, 0, mi_hist->fixed.bins * mi_hist->moving.bins * sizeof(double));
@@ -2153,7 +2134,7 @@ bspline_score_f_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist_score_omp (mi_hist, ssd->num_vox);
+    ssd->smetric[0] = mi_hist_score_omp (mi_hist, ssd->num_vox);
     num_vox_f = (float) ssd->num_vox;
 
     /* PASS 2 - Compute Gradient (Parallel across tiles) */
@@ -2240,7 +2221,7 @@ bspline_score_f_mi (
 
     /* Now we have a ton of bins and each bin's 64 slots are full.
      * Let's sum each bin's 64 slots.  The result with be dc_dp. */
-    bspline_condense_grad (cond_x, cond_y, cond_z,
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z,
         bxf, ssd);
 
     free (cond_x);
@@ -2254,9 +2235,6 @@ bspline_score_f_mi (
 #endif
 
     mse_score = mse_score / ssd->num_vox;
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 #endif
 
@@ -2311,9 +2289,6 @@ bspline_score_e_mi (
     }
 #endif
 
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     memset (f_hist, 0, mi_hist->fixed.bins * sizeof(double));
     memset (m_hist, 0, mi_hist->moving.bins * sizeof(double));
     memset (j_hist, 0, mi_hist->fixed.bins * mi_hist->moving.bins * sizeof(double));
@@ -2482,7 +2457,7 @@ bspline_score_e_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist_score_omp (mi_hist, ssd->num_vox);
+    ssd->smetric[0] = mi_hist_score_omp (mi_hist, ssd->num_vox);
     num_vox_f = (float) ssd->num_vox;
 
     /* PASS 2 - Compute Gradient (Parallel across tiles) */
@@ -2569,7 +2544,7 @@ bspline_score_e_mi (
 
     /* Now we have a ton of bins and each bin's 64 slots are full.
      * Let's sum each bin's 64 slots.  The result with be dc_dp. */
-    bspline_condense_grad (cond_x, cond_y, cond_z,
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z,
         bxf, ssd);
 
     free (cond_x);
@@ -2599,9 +2574,6 @@ bspline_score_e_mi (
 #endif
 
     mse_score = mse_score / ssd->num_vox;
-    
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 #endif
 
@@ -2668,9 +2640,6 @@ bspline_score_d_mi (
     }
 #endif
 
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     memset (f_hist, 0, mi_hist->fixed.bins * sizeof(double));
     memset (m_hist, 0, mi_hist->moving.bins * sizeof(double));
     memset (j_hist, 0, mi_hist->fixed.bins * mi_hist->moving.bins * sizeof(double));
@@ -2778,7 +2747,7 @@ bspline_score_d_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist_score_omp (mi_hist, ssd->num_vox);
+    ssd->smetric[0] = mi_hist_score_omp (mi_hist, ssd->num_vox);
     num_vox_f = (float) ssd->num_vox;
 
     /* PASS 2 - Compute Gradient (Parallel across tiles) */
@@ -2876,7 +2845,7 @@ bspline_score_d_mi (
 
     /* Now we have a ton of bins and each bin's 64 slots are full.
      * Let's sum each bin's 64 slots.  The result will be dc_dp. */
-    bspline_condense_grad (cond_x, cond_y, cond_z, bxf, ssd);
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z, bxf, ssd);
 
     free (cond_x);
     free (cond_y);
@@ -2892,9 +2861,6 @@ bspline_score_d_mi (
     if (parms->debug) {
         printf ("<< MSE %3.3f >>\n", mse_score);
     }
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 /* Mutual information version of implementation "C" */
@@ -2949,9 +2915,6 @@ bspline_score_c_mi (
     }
 #endif
 
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     memset (f_hist, 0, mi_hist->fixed.bins * sizeof(double));
     memset (m_hist, 0, mi_hist->moving.bins * sizeof(double));
     memset (j_hist, 0, mi_hist->fixed.bins * mi_hist->moving.bins * sizeof(double));
@@ -3056,7 +3019,7 @@ bspline_score_c_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist->compute_score (ssd->num_vox);
+    ssd->smetric[0] = mi_hist->compute_score (ssd->num_vox);
     num_vox_f = (float) ssd->num_vox;
 
     /* PASS 2 - Compute gradient */
@@ -3120,7 +3083,7 @@ bspline_score_c_mi (
                 );
 #endif
 
-                bspline_update_grad_b (&bst->ssd, bxf, pidx, qidx, dc_dv);
+                bst->ssd.update_smetric_grad_b (bxf, pidx, qidx, dc_dv);
 
             } /* LOOP_THRU_ROI_X */
         } /* LOOP_THRU_ROI_Y */
@@ -3133,9 +3096,6 @@ bspline_score_c_mi (
 #endif
 
     mse_score = mse_score / ssd->num_vox;
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 /* Mutual information version of implementation "k" */
@@ -3144,9 +3104,6 @@ bspline_score_k_mi (
     Bspline_optimize *bod
 )
 {
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     Bspline_parms *parms = bod->get_bspline_parms ();
     Bspline_state *bst = bod->get_bspline_state ();
     Bspline_score* ssd = &bst->ssd;
@@ -3193,7 +3150,7 @@ bspline_score_k_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist->compute_score (ssd->num_vox);
+    ssd->smetric[0] = mi_hist->compute_score (ssd->num_vox);
 
     /* Create/initialize bspline_loop_user (PASS 2) */
     Bspline_mi_k_pass_2 blu2 (bod);
@@ -3201,9 +3158,6 @@ bspline_score_k_mi (
 
     /* Run the loop */
     bspline_loop_voxel_serial (blu2, bod);
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 /* Mutual information version of implementation "l" */
@@ -3212,9 +3166,6 @@ bspline_score_l_mi (
     Bspline_optimize *bod
 )
 {
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     Bspline_parms *parms = bod->get_bspline_parms ();
     Bspline_state *bst = bod->get_bspline_state ();
     Bspline_score* ssd = &bst->ssd;
@@ -3261,7 +3212,7 @@ bspline_score_l_mi (
     }
 
     /* Compute score */
-    ssd->smetric = mi_hist->compute_score (ssd->num_vox);
+    ssd->smetric[bst->sm] = mi_hist->compute_score (ssd->num_vox);
 
     /* Create/initialize bspline_loop_user (PASS 2) */
     Bspline_mi_k_pass_2 blu2 (bod);
@@ -3269,9 +3220,6 @@ bspline_score_l_mi (
 
     /* Run the loop */
     bspline_loop_voxel_serial (blu2, bod);
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 void
@@ -3280,11 +3228,6 @@ bspline_score_mi (
 )
 {
     Bspline_parms *parms = bod->get_bspline_parms ();
-    Bspline_state *bst = bod->get_bspline_state ();
-    Bspline_xform *bxf = bod->get_bspline_xform ();
-
-    Reg_parms* reg_parms = parms->reg_parms;
-    Bspline_landmarks* blm = parms->blm;
 
     Volume* fixed_roi  = parms->fixed_roi;
     Volume* moving_roi = parms->moving_roi;
diff --git a/src/plastimatch/register/bspline_mi.txx b/src/plastimatch/register/bspline_mi.txx
index 148fabb..b0f4d58 100755
--- a/src/plastimatch/register/bspline_mi.txx
+++ b/src/plastimatch/register/bspline_mi.txx
@@ -94,7 +94,7 @@ bspline_mi_pvi_8_dc_dv_dcos (
         }
         idx_jbin = offset_fbin + idx_mbin;
         if (j_hist[idx_jbin] > 0.0001) {
-            dS_dP = logf((num_vox_f * j_hist[idx_jbin]) / (f_hist[idx_fbin] * m_hist[idx_mbin])) - ssd->smetric;
+            dS_dP = logf((num_vox_f * j_hist[idx_jbin]) / (f_hist[idx_fbin] * m_hist[idx_mbin])) - ssd->smetric[0];
             dc_dv[0] -= dw[3*idx_pv+0] * dS_dP;
             dc_dv[1] -= dw[3*idx_pv+1] * dS_dP;
             dc_dv[2] -= dw[3*idx_pv+2] * dS_dP;
@@ -215,7 +215,7 @@ public:
         );
 
         /* Update cost function gradient */
-        bspline_update_grad_b (ssd, bxf, pidx, qidx, dc_dv);
+        ssd->update_smetric_grad_b (bxf, pidx, qidx, dc_dv);
     }
 };
 
diff --git a/src/plastimatch/register/bspline_mse.cxx b/src/plastimatch/register/bspline_mse.cxx
index 99d9271..b976b2d 100644
--- a/src/plastimatch/register/bspline_mse.cxx
+++ b/src/plastimatch/register/bspline_mse.cxx
@@ -45,7 +45,7 @@ bspline_score_normalize (
 {
     Bspline_state *bst = bod->get_bspline_state ();
     Bspline_xform *bxf = bod->get_bspline_xform ();
-    Bspline_score* ssd = &bst->ssd;
+    Bspline_score *ssd = &bst->ssd;
 
     const int MIN_VOX = 1;
 
@@ -56,14 +56,14 @@ bspline_score_normalize (
        However, the best score is not currently stored in the state.  
     */
     if (ssd->num_vox < MIN_VOX) {
-        ssd->smetric = FLT_MAX;
+        ssd->smetric[bst->sm] = FLT_MAX;
         for (int i = 0; i < bxf->num_coeff; i++) {
-            ssd->grad[i] = 0;
+            ssd->smetric_grad[i] = 0;
         }
     } else {
-        ssd->smetric = raw_score / ssd->num_vox;
+        ssd->smetric[bst->sm] = raw_score / ssd->num_vox;
         for (int i = 0; i < bxf->num_coeff; i++) {
-            ssd->grad[i] = 2 * ssd->grad[i] / ssd->num_vox;
+            ssd->smetric_grad[i] = 2 * ssd->smetric_grad[i] / ssd->num_vox;
         }
     }
 }
@@ -109,10 +109,6 @@ bspline_score_i_mse (
 
     FILE* corr_fp = 0;
 
-    // Start timing the code
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     if (parms->debug) {
         std::string fn = string_format ("%s/%02d_corr_mse_%03d_%03d.csv",
             parms->debug_dir.c_str(), parms->debug_stage, bst->it, 
@@ -136,8 +132,8 @@ bspline_score_i_mse (
         plm_long ijk_tile[3];
         plm_long ijk_local[3];
 
-        float xyz_fixed[3];
-        plm_long ijk_fixed[3];
+        float fxyz[3];
+        plm_long fijk[3];
         plm_long idx_fixed;
 
         float dxyz[3];
@@ -171,29 +167,28 @@ bspline_score_i_mse (
                 LOOP_THRU_TILE_X (ijk_local, bxf) {
 
                     // Construct coordinates into fixed image volume
-                    GET_VOL_COORDS (ijk_fixed, ijk_tile, ijk_local, bxf);
+                    GET_VOL_COORDS (fijk, ijk_tile, ijk_local, bxf);
 
                     // Make sure we are inside the image volume
-                    if (ijk_fixed[0] >= bxf->roi_offset[0] + bxf->roi_dim[0])
+                    if (fijk[0] >= bxf->roi_offset[0] + bxf->roi_dim[0])
                         continue;
-                    if (ijk_fixed[1] >= bxf->roi_offset[1] + bxf->roi_dim[1])
+                    if (fijk[1] >= bxf->roi_offset[1] + bxf->roi_dim[1])
                         continue;
-                    if (ijk_fixed[2] >= bxf->roi_offset[2] + bxf->roi_dim[2])
+                    if (fijk[2] >= bxf->roi_offset[2] + bxf->roi_dim[2])
                         continue;
 
                     // Compute physical coordinates of fixed image voxel
-                    /* To remove DCOS support, switch to 
-                       GET_REAL_SPACE_COORDS (xyz_fixed, ijk_fixed, bxf); */
-                    GET_WORLD_COORDS (xyz_fixed, ijk_fixed, fixed, bxf);
+                    POSITION_FROM_COORDS (fxyz, fijk, bxf->img_origin, 
+                        fixed->step);
 
                     /* JAS 2012.03.26: Tends to break the optimizer (PGTOL)   */
                     /* Check to make sure the indices are valid (inside roi) */
                     if (fixed_roi) {
-                        if (!inside_roi (xyz_fixed, fixed_roi)) continue;
+                        if (!inside_roi (fxyz, fixed_roi)) continue;
                     }
 
                     // Construct the image volume index
-                    idx_fixed = volume_index (fixed->dim, ijk_fixed);
+                    idx_fixed = volume_index (fixed->dim, fijk);
 
                     // Calc. deformation vector (dxyz) for voxel
                     bspline_interp_pix_c (dxyz, bxf, idx_tile, ijk_local);
@@ -201,15 +196,15 @@ bspline_score_i_mse (
                     // Calc. moving image coordinate from the deformation 
                     // vector
                     rc = bspline_find_correspondence_dcos_roi (
-                        xyz_moving, ijk_moving, xyz_fixed, dxyz, moving,
+                        xyz_moving, ijk_moving, fxyz, dxyz, moving,
                         moving_roi);
 
                     if (parms->debug) {
                         fprintf (corr_fp, 
                             "%d %d %d %f %f %f\n",
-                            (unsigned int) ijk_fixed[0], 
-                            (unsigned int) ijk_fixed[1], 
-                            (unsigned int) ijk_fixed[2], 
+                            (unsigned int) fijk[0], 
+                            (unsigned int) fijk[1], 
+                            (unsigned int) fijk[2], 
                             ijk_moving[0], ijk_moving[1], ijk_moving[2]);
                     }
 
@@ -279,7 +274,7 @@ bspline_score_i_mse (
 
     /* Now we have a ton of bins and each bin's 64 slots are full.
      * Let's sum each bin's 64 slots.  The result with be dc_dp. */
-    bspline_condense_grad (cond_x, cond_y, cond_z, bxf, ssd);
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z, bxf, ssd);
 
     free (cond_x);
     free (cond_y);
@@ -291,10 +286,6 @@ bspline_score_i_mse (
     if (parms->debug) {
         fclose (corr_fp);
     }
-
-    /* Save for reporting */
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -340,10 +331,6 @@ bspline_score_h_mse (
 
     static int it = 0;
 
-    // Start timing the code
-    Plm_timer *timer = new Plm_timer;
-    timer->start ();
-
     // Zero out accumulators
     score_tile = 0;
     memset(cond_x, 0, cond_size);
@@ -367,8 +354,8 @@ bspline_score_h_mse (
         int ijk_tile[3];
         plm_long ijk_local[3];
 
-        float xyz_fixed[3];
-        plm_long ijk_fixed[3];
+        float fxyz[3];
+        plm_long fijk[3];
         plm_long idx_fixed;
 
         float dxyz[3];
@@ -402,24 +389,22 @@ bspline_score_h_mse (
                 LOOP_THRU_TILE_X (ijk_local, bxf) {
 
                     // Construct coordinates into fixed image volume
-                    GET_VOL_COORDS (ijk_fixed, ijk_tile, ijk_local, bxf);
+                    GET_VOL_COORDS (fijk, ijk_tile, ijk_local, bxf);
 
                     // Make sure we are inside the region of interest
-                    if (ijk_fixed[0] >= bxf->roi_offset[0] + bxf->roi_dim[0])
+                    if (fijk[0] >= bxf->roi_offset[0] + bxf->roi_dim[0])
                         continue;
-                    if (ijk_fixed[1] >= bxf->roi_offset[1] + bxf->roi_dim[1])
+                    if (fijk[1] >= bxf->roi_offset[1] + bxf->roi_dim[1])
                         continue;
-                    if (ijk_fixed[2] >= bxf->roi_offset[2] + bxf->roi_dim[2])
+                    if (fijk[2] >= bxf->roi_offset[2] + bxf->roi_dim[2])
                         continue;
 
                     // Compute physical coordinates of fixed image voxel
-                    /* To remove DCOS support, switch to 
-                       GET_REAL_SPACE_COORDS (xyz_fixed, ijk_fixed, bxf); */
-                    GET_WORLD_COORDS (xyz_fixed, ijk_fixed, 
-                        fixed, bxf);
+                    POSITION_FROM_COORDS (fxyz, fijk, bxf->img_origin, 
+                        fixed->step);
 
                     // Construct the image volume index
-                    idx_fixed = volume_index (fixed->dim, ijk_fixed);
+                    idx_fixed = volume_index (fixed->dim, fijk);
 
                     // Calc. deformation vector (dxyz) for voxel
                     bspline_interp_pix_c (dxyz, bxf, idx_tile, ijk_local);
@@ -428,7 +413,7 @@ bspline_score_h_mse (
                     /* To remove DCOS support, change function call to 
                        bspline_find_correspondence() */
                     rc = bspline_find_correspondence_dcos (
-                        xyz_moving, ijk_moving, xyz_fixed, dxyz, moving);
+                        xyz_moving, ijk_moving, fxyz, dxyz, moving);
 
                     // Return code is 0 if voxel is pushed outside of moving image
                     if (!rc) continue;
@@ -436,9 +421,9 @@ bspline_score_h_mse (
                     if (parms->debug) {
                         fprintf (corr_fp, 
                             "%d %d %d %f %f %f\n",
-                            (unsigned int) ijk_fixed[0], 
-                            (unsigned int) ijk_fixed[1], 
-                            (unsigned int) ijk_fixed[2], 
+                            (unsigned int) fijk[0], 
+                            (unsigned int) fijk[1], 
+                            (unsigned int) fijk[2], 
                             ijk_moving[0], ijk_moving[1], ijk_moving[2]);
                     }
 
@@ -503,7 +488,7 @@ bspline_score_h_mse (
      * The number of total bins is equal to the number of control
      * points in the control grid.
      */
-    bspline_condense_grad (cond_x, cond_y, cond_z, bxf, ssd);
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z, bxf, ssd);
 
     free (cond_x);
     free (cond_y);
@@ -515,9 +500,6 @@ bspline_score_h_mse (
     if (parms->debug) {
         fclose (corr_fp);
     }
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 
@@ -567,10 +549,6 @@ bspline_score_g_mse (
 
     FILE* corr_fp = 0;
 
-    // Start timing the code
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     if (parms->debug) {
         std::string fn = string_format ("%s/%02d_corr_mse_%03d_%03d.csv",
             parms->debug_dir.c_str(), parms->debug_stage, bst->it, 
@@ -594,8 +572,8 @@ bspline_score_g_mse (
         plm_long ijk_tile[3];
         plm_long ijk_local[3];
 
-        float xyz_fixed[3];
-        plm_long ijk_fixed[3];
+        float fxyz[3];
+        plm_long fijk[3];
         plm_long idx_fixed;
 
         float dxyz[3];
@@ -629,24 +607,22 @@ bspline_score_g_mse (
                 LOOP_THRU_TILE_X (ijk_local, bxf) {
 
                     // Construct coordinates into fixed image volume
-                    GET_VOL_COORDS (ijk_fixed, ijk_tile, ijk_local, bxf);
+                    GET_VOL_COORDS (fijk, ijk_tile, ijk_local, bxf);
 
                     // Make sure we are inside the image volume
-                    if (ijk_fixed[0] >= bxf->roi_offset[0] + bxf->roi_dim[0])
+                    if (fijk[0] >= bxf->roi_offset[0] + bxf->roi_dim[0])
                         continue;
-                    if (ijk_fixed[1] >= bxf->roi_offset[1] + bxf->roi_dim[1])
+                    if (fijk[1] >= bxf->roi_offset[1] + bxf->roi_dim[1])
                         continue;
-                    if (ijk_fixed[2] >= bxf->roi_offset[2] + bxf->roi_dim[2])
+                    if (fijk[2] >= bxf->roi_offset[2] + bxf->roi_dim[2])
                         continue;
 
                     // Compute physical coordinates of fixed image voxel
-                    /* To remove DCOS support, switch to 
-                       GET_REAL_SPACE_COORDS (xyz_fixed, ijk_fixed, bxf); */
-                    GET_WORLD_COORDS (xyz_fixed, ijk_fixed, 
-                        fixed, bxf);
+                    POSITION_FROM_COORDS (fxyz, fijk, bxf->img_origin, 
+                        fixed->step);
                     
                     // Construct the image volume index
-                    idx_fixed = volume_index (fixed->dim, ijk_fixed);
+                    idx_fixed = volume_index (fixed->dim, fijk);
 
                     // Calc. deformation vector (dxyz) for voxel
                     bspline_interp_pix_c (dxyz, bxf, idx_tile, ijk_local);
@@ -656,14 +632,14 @@ bspline_score_g_mse (
                     /* To remove DCOS support, change function call to 
                        bspline_find_correspondence() */
                     rc = bspline_find_correspondence_dcos (
-                        xyz_moving, ijk_moving, xyz_fixed, dxyz, moving);
+                        xyz_moving, ijk_moving, fxyz, dxyz, moving);
 
                     if (parms->debug) {
                         fprintf (corr_fp, 
                             "%d %d %d %f %f %f\n",
-                            (unsigned int) ijk_fixed[0], 
-                            (unsigned int) ijk_fixed[1], 
-                            (unsigned int) ijk_fixed[2], 
+                            (unsigned int) fijk[0], 
+                            (unsigned int) fijk[1], 
+                            (unsigned int) fijk[2], 
                             ijk_moving[0], ijk_moving[1], ijk_moving[2]);
                     }
 
@@ -734,7 +710,7 @@ bspline_score_g_mse (
 
     /* Now we have a ton of bins and each bin's 64 slots are full.
      * Let's sum each bin's 64 slots.  The result with be dc_dp. */
-    bspline_condense_grad (cond_x, cond_y, cond_z, bxf, ssd);
+    bspline_condense_smetric_grad (cond_x, cond_y, cond_z, bxf, ssd);
 
     free (cond_x);
     free (cond_y);
@@ -746,10 +722,6 @@ bspline_score_g_mse (
     if (parms->debug) {
         fclose (corr_fp);
     }
-
-    /* Save for reporting */
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 /* -----------------------------------------------------------------------
@@ -763,9 +735,6 @@ bspline_score_c_mse (
     Bspline_optimize *bod
 )
 {
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
     Bspline_parms *parms = bod->get_bspline_parms ();
     Bspline_state *bst = bod->get_bspline_state ();
     Bspline_xform *bxf = bod->get_bspline_xform ();
@@ -886,7 +855,7 @@ bspline_score_c_mse (
                 dc_dv[0] = diff * m_grad[3*mvr+0];  /* x component */
                 dc_dv[1] = diff * m_grad[3*mvr+1];  /* y component */
                 dc_dv[2] = diff * m_grad[3*mvr+2];  /* z component */
-                bspline_update_grad_b (&bst->ssd, bxf, pidx, qidx, dc_dv);
+                bst->ssd.update_smetric_grad_b (bxf, pidx, qidx, dc_dv);
         
                 if (parms->debug) {
                     fprintf (val_fp, 
@@ -919,9 +888,6 @@ bspline_score_c_mse (
 
     /* Normalize score for MSE */
     bspline_score_normalize (bod, score_acc);
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 /* -----------------------------------------------------------------------
@@ -937,15 +903,6 @@ bspline_score_k_mse (
     Bspline_optimize *bod
 )
 {
-    /* The timer should be moved back into bspline_loop, however 
-       it requires that start/end routines for bspline_loop_user 
-       have consistent interface for all users */
-       
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
-    Bspline_score *ssd = &bod->get_bspline_state()->ssd;
-
     /* Create/initialize bspline_loop_user */
     Bspline_mse_k blu (bod);
 
@@ -954,9 +911,6 @@ bspline_score_k_mse (
 
     /* Normalize score for MSE */
     bspline_score_normalize (bod, blu.score_acc);
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 void
@@ -964,15 +918,6 @@ bspline_score_l_mse (
     Bspline_optimize *bod
 )
 {
-    /* The timer should be moved back into bspline_loop, however 
-       it requires that start/end routines for bspline_loop_user 
-       have consistent interface for all users */
-       
-    Plm_timer* timer = new Plm_timer;
-    timer->start ();
-
-    Bspline_score *ssd = &bod->get_bspline_state()->ssd;
-
     /* Create/initialize bspline_loop_user */
     Bspline_mse_l blu (bod);
 
@@ -981,9 +926,6 @@ bspline_score_l_mse (
 
     /* Normalize score for MSE */
     bspline_score_normalize (bod, blu.score_acc);
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 void
@@ -1009,7 +951,7 @@ bspline_score_mse (
     Bspline_state *bst = bod->get_bspline_state ();
     Bspline_xform *bxf = bod->get_bspline_xform ();
 
-    Reg_parms* reg_parms = parms->reg_parms;
+    Regularization_parms* reg_parms = parms->reg_parms;
     Bspline_landmarks* blm = parms->blm;
 
     Volume* fixed_roi  = parms->fixed_roi;
@@ -1017,8 +959,8 @@ bspline_score_mse (
     bool have_roi = fixed_roi || moving_roi;
 
     /* CPU Implementations */
-    if (parms->threading == BTHR_CPU) {
-            
+    if (parms->threading == BTHR_CPU)
+    {
         if (have_roi) {
             switch (parms->implementation) {
             case 'c':
@@ -1068,8 +1010,8 @@ bspline_score_mse (
 
 #if (CUDA_FOUND)
     /* CUDA Implementations */
-    else if (parms->threading == BTHR_CUDA) {
-
+    else if (parms->threading == BTHR_CUDA)
+    {
         /* Be sure we loaded the CUDA plugin */
         LOAD_LIBRARY_SAFE (libplmregistercuda);
         LOAD_SYMBOL (CUDA_bspline_mse_j, libplmregistercuda);
@@ -1091,7 +1033,6 @@ bspline_score_mse (
 
         /* Unload plugin when done */
         UNLOAD_LIBRARY (libplmregistercuda);
-
     }
 #endif
 }
diff --git a/src/plastimatch/register/bspline_mse.txx b/src/plastimatch/register/bspline_mse.txx
index 4d7d669..1a01c13 100755
--- a/src/plastimatch/register/bspline_mse.txx
+++ b/src/plastimatch/register/bspline_mse.txx
@@ -72,7 +72,7 @@ public:
         dc_dv[2] = diff * m_grad[3*mvr+2];  /* z component */
 
         /* Update cost function gradient */
-        bspline_update_grad_b (ssd, bxf, pidx, qidx, dc_dv);
+        ssd->update_smetric_grad_b (bxf, pidx, qidx, dc_dv);
         ssd->num_vox++;
     }
 };
diff --git a/src/plastimatch/register/bspline_optimize_lbfgsb.cxx b/src/plastimatch/register/bspline_optimize_lbfgsb.cxx
index 79ecc2a..16d060b 100644
--- a/src/plastimatch/register/bspline_optimize_lbfgsb.cxx
+++ b/src/plastimatch/register/bspline_optimize_lbfgsb.cxx
@@ -237,7 +237,7 @@ bspline_optimize_lbfgsb (
             /* Copy from C to fortran (float -> double) */
             optimizer.f = ssd->score;
             for (int i = 0; i < bxf->num_coeff; i++) {
-                optimizer.g[i] = ssd->grad[i];
+                optimizer.g[i] = ssd->total_grad[i];
             }
 
             /* Check # feval */
diff --git a/src/plastimatch/register/bspline_optimize_liblbfgs.cxx b/src/plastimatch/register/bspline_optimize_liblbfgs.cxx
index f2655c0..4900a70 100644
--- a/src/plastimatch/register/bspline_optimize_liblbfgs.cxx
+++ b/src/plastimatch/register/bspline_optimize_liblbfgs.cxx
@@ -40,7 +40,7 @@ evaluate (
 
     /* Copy gradient out */
     for (i = 0; i < bxf->num_coeff; i++) {
-	g[i] = (lbfgsfloatval_t) bst->ssd.grad[i];
+	g[i] = (lbfgsfloatval_t) bst->ssd.total_grad[i];
     }
 
     /* Increment num function evals */
diff --git a/src/plastimatch/register/bspline_optimize_nlopt.cxx b/src/plastimatch/register/bspline_optimize_nlopt.cxx
index d0cffc6..194fdbf 100644
--- a/src/plastimatch/register/bspline_optimize_nlopt.cxx
+++ b/src/plastimatch/register/bspline_optimize_nlopt.cxx
@@ -42,7 +42,7 @@ bspline_optimize_nlopt_score (
 
     /* Copy gradient out */
     for (i = 0; i < bxf->num_coeff; i++) {
-	grad[i] = (double) bst->ssd.grad[i];
+	grad[i] = (double) bst->ssd.total_grad[i];
     }
 
     /* Return cost */
diff --git a/src/plastimatch/register/bspline_optimize_steepest.cxx b/src/plastimatch/register/bspline_optimize_steepest.cxx
index 5ad7c10..b553271 100644
--- a/src/plastimatch/register/bspline_optimize_steepest.cxx
+++ b/src/plastimatch/register/bspline_optimize_steepest.cxx
@@ -78,13 +78,13 @@ bspline_optimize_steepest_trace (
     /* Get search direction */
     ssd_grad_norm = 0;
     for (i = 0; i < bxf->num_coeff; i++) {
-        ssd_grad_norm += ssd->grad[i] * ssd->grad[i];
+        ssd_grad_norm += ssd->total_grad[i] * ssd->total_grad[i];
     }
     ssd_grad_norm = sqrt (ssd_grad_norm);
     htg = 0.0;
     for (i = 0; i < bxf->num_coeff; i++) {
-        h[i] = - ssd->grad[i] / ssd_grad_norm;
-        htg -= h[i] * ssd->grad[i];
+        h[i] = - ssd->total_grad[i] / ssd_grad_norm;
+        htg -= h[i] * ssd->total_grad[i];
     }
 
     /* Give a little feedback to the user */
@@ -150,7 +150,7 @@ bspline_optimize_steepest_trace (
         // line search.
         success ++;
         memcpy (x, bxf->coeff, bxf->num_coeff * sizeof(float));
-        memcpy (grad_backup, ssd->grad, bxf->num_coeff * sizeof(float));
+        memcpy (grad_backup, ssd->total_grad, bxf->num_coeff * sizeof(float));
         score_backup = ssd->score;
         sprintf (filename, "grad_%04i.csv", success);
         trace = fopen(filename, "w");
@@ -173,19 +173,19 @@ bspline_optimize_steepest_trace (
         fclose (trace);
 
         printf ("Finished Capturing Gradient.\n\n");
-        memcpy (ssd->grad, grad_backup, bxf->num_coeff * sizeof(float));
+        memcpy (ssd->total_grad, grad_backup, bxf->num_coeff * sizeof(float));
         ssd->score = score_backup;
 
         /* Start new line search */
         ssd_grad_norm = 0;
         for (i = 0; i < bxf->num_coeff; i++) {
-            ssd_grad_norm += ssd->grad[i] * ssd->grad[i];
+            ssd_grad_norm += ssd->total_grad[i] * ssd->total_grad[i];
         }
         ssd_grad_norm = sqrt (ssd_grad_norm);
         htg = 0.0;
         for (i = 0; i < bxf->num_coeff; i++) {
-            h[i] = - ssd->grad[i] / ssd_grad_norm;
-            htg -= h[i] * ssd->grad[i];
+            h[i] = - ssd->total_grad[i] / ssd_grad_norm;
+            htg -= h[i] * ssd->total_grad[i];
         }
         old_score = bst->ssd.score;
     }
@@ -257,13 +257,13 @@ bspline_optimize_steepest_trust (
     /* Get search direction */
     ssd_grad_norm = 0;
     for (i = 0; i < bxf->num_coeff; i++) {
-        ssd_grad_norm += ssd->grad[i] * ssd->grad[i];
+        ssd_grad_norm += ssd->total_grad[i] * ssd->total_grad[i];
     }
     ssd_grad_norm = sqrt (ssd_grad_norm);
     htg = 0.0;
     for (i = 0; i < bxf->num_coeff; i++) {
-        h[i] = - ssd->grad[i] / ssd_grad_norm;
-        htg -= h[i] * ssd->grad[i];
+        h[i] = - ssd->total_grad[i] / ssd_grad_norm;
+        htg -= h[i] * ssd->total_grad[i];
     }
 
     /* Give a little feedback to the user */
@@ -326,13 +326,13 @@ bspline_optimize_steepest_trust (
         memcpy (x, bxf->coeff, bxf->num_coeff * sizeof(float));
         ssd_grad_norm = 0;
         for (i = 0; i < bxf->num_coeff; i++) {
-            ssd_grad_norm += ssd->grad[i] * ssd->grad[i];
+            ssd_grad_norm += ssd->total_grad[i] * ssd->total_grad[i];
         }
         ssd_grad_norm = sqrt (ssd_grad_norm);
         htg = 0.0;
         for (i = 0; i < bxf->num_coeff; i++) {
-            h[i] = - ssd->grad[i] / ssd_grad_norm;
-            htg -= h[i] * ssd->grad[i];
+            h[i] = - ssd->total_grad[i] / ssd_grad_norm;
+            htg -= h[i] * ssd->total_grad[i];
         }
         old_score = bst->ssd.score;
     }
@@ -384,7 +384,7 @@ bspline_optimize_steepest_naive (
     /* Set alpha based on norm gradient */
     ssd_grad_norm = 0;
     for (i = 0; i < bxf->num_coeff; i++) {
-        ssd_grad_norm += fabs (ssd->grad[i]);
+        ssd_grad_norm += fabs (ssd->total_grad[i]);
     }
     a = 1.0f / ssd_grad_norm;
     gamma = a;
@@ -409,7 +409,7 @@ bspline_optimize_steepest_naive (
         /* Update b-spline coefficients from gradient */
         //gamma = a / pow(it + A, alpha);
         for (i = 0; i < bxf->num_coeff; i++) {
-            bxf->coeff[i] = bxf->coeff[i] + gamma * ssd->grad[i];
+            bxf->coeff[i] = bxf->coeff[i] + gamma * ssd->total_grad[i];
         }
 
         /* Get score and gradient */
diff --git a/src/plastimatch/register/bspline_parms.cxx b/src/plastimatch/register/bspline_parms.cxx
index 0283f6e..487b5ce 100644
--- a/src/plastimatch/register/bspline_parms.cxx
+++ b/src/plastimatch/register/bspline_parms.cxx
@@ -12,6 +12,7 @@ Bspline_parms::Bspline_parms ()
     this->threading = BTHR_CPU;
     this->optimization = BOPT_LBFGSB;
     this->metric_type.push_back (REGISTRATION_METRIC_MSE);
+    this->metric_lambda.push_back (1.0);
     this->implementation = '\0';
     this->min_its = 0;
     this->max_its = 10;
@@ -34,14 +35,15 @@ Bspline_parms::Bspline_parms ()
     this->lbfgsb_factr = 1.0e+7;
     this->lbfgsb_pgtol = 1.0e-5;
 
-    this->fixed = NULL;
-    this->moving = NULL;
-    this->fixed_grad = NULL;
-    this->moving_grad = NULL;
-    this->fixed_roi = NULL;
-    this->moving_roi = NULL;
+    this->fixed = 0;
+    this->moving = 0;
+    this->fixed_grad = 0;
+    this->moving_grad = 0;
+    this->fixed_roi = 0;
+    this->moving_roi = 0;
+    this->fixed_stiffness = 0;
 
-    this->reg_parms = new Reg_parms;
+    this->reg_parms = new Regularization_parms;
 
     this->blm = new Bspline_landmarks;
     this->rbf_radius = 0;
diff --git a/src/plastimatch/register/bspline_parms.h b/src/plastimatch/register/bspline_parms.h
index 652fdec..6e27fad 100644
--- a/src/plastimatch/register/bspline_parms.h
+++ b/src/plastimatch/register/bspline_parms.h
@@ -27,7 +27,7 @@ enum BsplineThreading {
 };
 
 class Bspline_landmarks;
-class Reg_parms;
+class Regularization_parms;
 
 class PLMREGISTER_API Bspline_parms
 {
@@ -75,7 +75,7 @@ public:
     Volume* fixed_stiffness;
 
     /* Regularization */
-    Reg_parms* reg_parms;        /* Regularization Parameters */
+    Regularization_parms* reg_parms;        /* Regularization Parameters */
 
     /* Landmarks */
     Bspline_landmarks* blm;      /* Landmarks parameters */
diff --git a/src/plastimatch/register/bspline_regularize.cxx b/src/plastimatch/register/bspline_regularize.cxx
index 22cbc89..fe15ef0 100644
--- a/src/plastimatch/register/bspline_regularize.cxx
+++ b/src/plastimatch/register/bspline_regularize.cxx
@@ -11,9 +11,9 @@ Bspline_regularize::Bspline_regularize ()
 {
     /* all methods */
     this->reg_parms = 0;
+    this->bxf = 0;
     this->fixed = 0;
     this->moving = 0;
-    this->bxf = 0;
     this->fixed_stiffness = 0;
 
     /* semi-analytic method */
@@ -66,7 +66,7 @@ Bspline_regularize::~Bspline_regularize ()
 
 void
 Bspline_regularize::initialize (
-    Reg_parms *reg_parms,
+    Regularization_parms *reg_parms,
     Bspline_xform* bxf
 )
 {
@@ -96,7 +96,7 @@ Bspline_regularize::initialize (
 void
 Bspline_regularize::compute_score (
     Bspline_score *bspline_score,    /* Gets updated */
-    const Reg_parms* reg_parms,
+    const Regularization_parms* reg_parms,
     const Bspline_xform* bxf
 )
 {
diff --git a/src/plastimatch/register/bspline_regularize.h b/src/plastimatch/register/bspline_regularize.h
index febcf99..2d07ea3 100644
--- a/src/plastimatch/register/bspline_regularize.h
+++ b/src/plastimatch/register/bspline_regularize.h
@@ -7,17 +7,16 @@
 #include "plmregister_config.h"
 #include "volume.h"
 
-class Bspline_regularize_private;
 class Bspline_score;
 class Bspline_xform;
 
-class Reg_parms
+class Regularization_parms
 {
 public:
     char implementation;    /* Implementation: a, b, c, etc */
     float lambda;           /* Smoothness weighting factor  */
 public:
-    Reg_parms () {
+    Regularization_parms () {
         this->implementation = '\0';
         this->lambda = 0.0f;
     }
@@ -26,13 +25,12 @@ public:
 class PLMREGISTER_API Bspline_regularize {
 public:
     SMART_POINTER_SUPPORT (Bspline_regularize);
-    Bspline_regularize_private *d_ptr;
 public:
     Bspline_regularize ();
     ~Bspline_regularize ();
 public:
     /* all methods */
-    Reg_parms *reg_parms;
+    Regularization_parms *reg_parms;
     Bspline_xform *bxf;
 
     Volume* fixed;
@@ -59,12 +57,12 @@ public:
     double* cond;
 public:
     void initialize (
-        Reg_parms* reg_parms,
+        Regularization_parms* reg_parms,
         Bspline_xform* bxf
     );
     void compute_score (
         Bspline_score* bsp_score,    /* Gets updated */
-        const Reg_parms* reg_parms,
+        const Regularization_parms* reg_parms,
         const Bspline_xform* bxf
     );
 
@@ -73,7 +71,7 @@ protected:
         const Bspline_xform* bxf);
     void compute_score_numeric (
         Bspline_score *bscore, 
-        const Reg_parms *parms, 
+        const Regularization_parms *parms, 
         const Bspline_regularize *rst,
         const Bspline_xform* bxf);
 
@@ -81,12 +79,12 @@ protected:
         const Bspline_xform* bxf);
     void compute_score_analytic (
         Bspline_score *bspline_score, 
-        const Reg_parms* reg_parms,
+        const Regularization_parms* reg_parms,
         const Bspline_regularize* rst,
         const Bspline_xform* bxf);
     void compute_score_analytic_omp (
         Bspline_score *bspline_score, 
-        const Reg_parms* reg_parms,
+        const Regularization_parms* reg_parms,
         const Bspline_regularize* rst,
         const Bspline_xform* bxf);
 
@@ -113,7 +111,7 @@ protected:
         int derive2);
     void compute_score_semi_analytic (
         Bspline_score *bscore, 
-        const Reg_parms *parms, 
+        const Regularization_parms *parms, 
         const Bspline_regularize *rst,
         const Bspline_xform* bxf);
 };
diff --git a/src/plastimatch/register/bspline_regularize_analytic.cxx b/src/plastimatch/register/bspline_regularize_analytic.cxx
index 120c162..6eff0d1 100644
--- a/src/plastimatch/register/bspline_regularize_analytic.cxx
+++ b/src/plastimatch/register/bspline_regularize_analytic.cxx
@@ -115,11 +115,11 @@ reg_update_grad (
 {
     int kidx, sidx;
 
-    for (kidx=0; kidx < (bxf->cdims[0] * bxf->cdims[1] * bxf->cdims[2]); kidx++) {
+    for (kidx=0; kidx < bxf->num_knots; kidx++) {
         for (sidx=0; sidx<64; sidx++) {
-            ssd->grad[3*kidx+0] += cond[3*(64*kidx+sidx)+0];
-            ssd->grad[3*kidx+1] += cond[3*(64*kidx+sidx)+1];
-            ssd->grad[3*kidx+2] += cond[3*(64*kidx+sidx)+2];
+            ssd->total_grad[3*kidx+0] += cond[3*(64*kidx+sidx)+0];
+            ssd->total_grad[3*kidx+1] += cond[3*(64*kidx+sidx)+1];
+            ssd->total_grad[3*kidx+2] += cond[3*(64*kidx+sidx)+2];
         }
     }
 }
@@ -333,7 +333,7 @@ get_Vmatrix (double* V, double* X, double* Y, double* Z)
 double
 region_smoothness_omp (
     double* sets,
-    const Reg_parms* reg_parms,    
+    const Regularization_parms* reg_parms,    
     const Bspline_xform* bxf,
     double* V, 
     plm_long* knots
@@ -371,7 +371,7 @@ region_smoothness_omp (
 void
 region_smoothness (
     Bspline_score *bspline_score, 
-    const Reg_parms* reg_parms,    
+    const Regularization_parms* reg_parms,    
     const Bspline_xform* bxf,
     double* V, 
     plm_long* knots)
@@ -396,9 +396,9 @@ region_smoothness (
         /* ------------------------------------------------ */
 
         /* dS/dp = 2Vp operation */
-        bspline_score->grad[3*knots[j]+0] += 2 * reg_parms->lambda * X[j];
-        bspline_score->grad[3*knots[j]+1] += 2 * reg_parms->lambda * Y[j];
-        bspline_score->grad[3*knots[j]+2] += 2 * reg_parms->lambda * Z[j];
+        bspline_score->total_grad[3*knots[j]+0] += 2 * reg_parms->lambda * X[j];
+        bspline_score->total_grad[3*knots[j]+1] += 2 * reg_parms->lambda * Y[j];
+        bspline_score->total_grad[3*knots[j]+2] += 2 * reg_parms->lambda * Z[j];
     }
 
     bspline_score->rmetric += S;
@@ -497,7 +497,7 @@ Bspline_regularize::analytic_init (
 void
 Bspline_regularize::compute_score_analytic_omp (
     Bspline_score *bspline_score, 
-    const Reg_parms* reg_parms,
+    const Regularization_parms* reg_parms,
     const Bspline_regularize* rst,
     const Bspline_xform* bxf)
 {
@@ -547,7 +547,7 @@ Bspline_regularize::compute_score_analytic_omp (
 void
 Bspline_regularize::compute_score_analytic (
     Bspline_score *bspline_score, 
-    const Reg_parms* reg_parms,
+    const Regularization_parms* reg_parms,
     const Bspline_regularize* rst,
     const Bspline_xform* bxf)
 {
diff --git a/src/plastimatch/register/bspline_regularize_numeric.cxx b/src/plastimatch/register/bspline_regularize_numeric.cxx
index 406a393..7d1b886 100644
--- a/src/plastimatch/register/bspline_regularize_numeric.cxx
+++ b/src/plastimatch/register/bspline_regularize_numeric.cxx
@@ -25,7 +25,7 @@
 static void
 compute_score_numeric_internal (
     Bspline_score *bscore, 
-    const Reg_parms *parms, 
+    const Regularization_parms *parms, 
     const Bspline_regularize *rst,
     const Bspline_xform* bxf,
     const Volume* vol
@@ -264,65 +264,63 @@ compute_score_numeric_internal (
 		int pidx, qidx;
 		pidx = get_region_index  (i  , j  , k  , bxf);
 		qidx = get_region_offset (i  , j  , k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv);
 
 		pidx = get_region_index  (i-1, j  , k  , bxf);
 		qidx = get_region_offset (i-1, j  , k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_in);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_in);
 		pidx = get_region_index  (i+1, j  , k  , bxf);
 		qidx = get_region_offset (i+1, j  , k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_ip);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_ip);
 		pidx = get_region_index  (i  , j-1, k  , bxf);
 		qidx = get_region_offset (i  , j-1, k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_jn);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_jn);
 		pidx = get_region_index  (i  , j+1, k  , bxf);
 		qidx = get_region_offset (i  , j+1, k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_jp);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_jp);
 		pidx = get_region_index  (i  , j  , k-1, bxf);
 		qidx = get_region_offset (i  , j  , k-1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_kn);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_kn);
 		pidx = get_region_index  (i  , j  , k+1, bxf);
 		qidx = get_region_offset (i  , j  , k+1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_kp);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_kp);
 
 		pidx = get_region_index  (i-1, j-1, k  , bxf);
 		qidx = get_region_offset (i-1, j-1, k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_injn);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_injn);
 		pidx = get_region_index  (i-1, j+1, k  , bxf);
 		qidx = get_region_offset (i-1, j+1, k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_injp);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_injp);
 		pidx = get_region_index  (i+1, j-1, k  , bxf);
 		qidx = get_region_offset (i+1, j-1, k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_ipjn);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_ipjn);
 		pidx = get_region_index  (i+1, j+1, k  , bxf);
 		qidx = get_region_offset (i+1, j+1, k  , bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_ipjp);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_ipjp);
 		pidx = get_region_index  (i-1, j  , k-1, bxf);
 		qidx = get_region_offset (i-1, j  , k-1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_inkn);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_inkn);
 		pidx = get_region_index  (i-1, j  , k+1, bxf);
 		qidx = get_region_offset (i-1, j  , k+1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_inkp);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_inkp);
 		pidx = get_region_index  (i+1, j  , k-1, bxf);
 		qidx = get_region_offset (i+1, j  , k-1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_ipkn);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_ipkn);
 		pidx = get_region_index  (i+1, j  , k+1, bxf);
 		qidx = get_region_offset (i+1, j  , k+1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_ipkp);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_ipkp);
 		pidx = get_region_index  (i  , j-1, k-1, bxf);
 		qidx = get_region_offset (i  , j-1, k-1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_jnkn);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_jnkn);
 		pidx = get_region_index  (i  , j-1, k+1, bxf);
 		qidx = get_region_offset (i  , j-1, k+1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_jnkp);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_jnkp);
 		pidx = get_region_index  (i  , j+1, k-1, bxf);
 		qidx = get_region_offset (i  , j+1, k-1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_jpkn);
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_jpkn);
 		pidx = get_region_index  (i  , j+1, k+1, bxf);
 		qidx = get_region_offset (i  , j+1, k+1, bxf);
-		bspline_update_grad_b (bscore, bxf, pidx, qidx, dc_dv_jpkp);
-#if defined (commentout)
-#endif
+		bscore->update_total_grad_b (bxf, pidx, qidx, dc_dv_jpkp);
             }
         }
     }
@@ -344,7 +342,7 @@ compute_score_numeric_internal (
 void
 Bspline_regularize::compute_score_numeric (
     Bspline_score *bscore, 
-    const Reg_parms *parms, 
+    const Regularization_parms *parms, 
     const Bspline_regularize *rst,
     const Bspline_xform* bxf)
 {
diff --git a/src/plastimatch/register/bspline_regularize_semi_analytic.cxx b/src/plastimatch/register/bspline_regularize_semi_analytic.cxx
index 8f00f3b..4ac9baa 100644
--- a/src/plastimatch/register/bspline_regularize_semi_analytic.cxx
+++ b/src/plastimatch/register/bspline_regularize_semi_analytic.cxx
@@ -340,9 +340,9 @@ Bspline_regularize::hessian_update_grad (
 		    + (p[1] + j) * bxf->cdims[0]
 		    + (p[0] + i);
 		cidx = cidx * 3;
-		bscore->grad[cidx+0] += dc_dv[0] * q_lut[m];
-		bscore->grad[cidx+1] += dc_dv[1] * q_lut[m];
-		bscore->grad[cidx+2] += dc_dv[2] * q_lut[m];
+		bscore->total_grad[cidx+0] += dc_dv[0] * q_lut[m];
+		bscore->total_grad[cidx+1] += dc_dv[1] * q_lut[m];
+		bscore->total_grad[cidx+2] += dc_dv[2] * q_lut[m];
 		m ++;
 	    }
 	}
@@ -370,9 +370,9 @@ bspline_regularize_hessian_update_grad_b (
 		    + (p[1] + j) * bxf->cdims[0]
 		    + (p[0] + i);
 		cidx = cidx * 3;
-		bscore->grad[cidx+0] += dc_dv[0] * q_lut[m];
-		bscore->grad[cidx+1] += dc_dv[1] * q_lut[m];
-		bscore->grad[cidx+2] += dc_dv[2] * q_lut[m];
+		bscore->total_grad[cidx+0] += dc_dv[0] * q_lut[m];
+		bscore->total_grad[cidx+1] += dc_dv[1] * q_lut[m];
+		bscore->total_grad[cidx+2] += dc_dv[2] * q_lut[m];
 		m ++;
 	    }
 	}
@@ -413,7 +413,7 @@ update_score_and_grad (
 void
 Bspline_regularize::compute_score_semi_analytic (
     Bspline_score *bscore, 
-    const Reg_parms *parms, 
+    const Regularization_parms *parms, 
     const Bspline_regularize *rst,
     const Bspline_xform* bxf
 )
@@ -500,7 +500,6 @@ Bspline_regularize::compute_score_semi_analytic (
 	//raw_score = grad_score / num_vox;
 	grad_score *= (parms->lambda / num_vox);
 	//printf ("        GRAD_COST %.4f   RAW_GRAD %.4f   [%.3f secs]\n", grad_score, raw_score, interval);
-	//bscore->score += grad_score;
 	bscore->rmetric += grad_score;
     }
     //printf ("SCORE=%.4f\n", bscore->score);
diff --git a/src/plastimatch/register/bspline_score.cxx b/src/plastimatch/register/bspline_score.cxx
index 98c7a1a..2c18a8d 100644
--- a/src/plastimatch/register/bspline_score.cxx
+++ b/src/plastimatch/register/bspline_score.cxx
@@ -2,36 +2,44 @@
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
 #include "plmregister_config.h"
-#include <string.h>
 
 #include "bspline_score.h"
+#include "bspline_xform.h"
 
 Bspline_score::Bspline_score ()
 {
     this->score = 0;
     this->lmetric = 0;
     this->rmetric = 0;
-    this->smetric = 0;
-    this->num_vox = 0;
 
+    this->num_vox = 0;
     this->num_coeff = 0;
-    this->grad = 0;
+    this->smetric_grad = 0;
+    this->total_grad = 0;
 
-    this->time_smetric = 0;
     this->time_rmetric = 0;
 }
 
 Bspline_score::~Bspline_score ()
 {
-    delete[] grad;
+    delete[] smetric_grad;
+    delete[] total_grad;
 }
 
 void
 Bspline_score::set_num_coeff (plm_long num_coeff)
 {
     this->num_coeff = num_coeff;
-    delete[] this->grad;
-    this->grad = new float[num_coeff];
+    delete[] this->smetric_grad;
+    delete[] this->total_grad;
+    this->smetric_grad = new float[num_coeff];
+    this->total_grad = new float[num_coeff];
+}
+
+void
+Bspline_score::reset_smetric_grad ()
+{
+    memset (this->smetric_grad, 0, this->num_coeff * sizeof(float));
 }
 
 void
@@ -40,9 +48,115 @@ Bspline_score::reset_score ()
     this->score = 0;
     this->lmetric = 0;
     this->rmetric = 0;
-    this->smetric = 0;
+    this->smetric.clear();
     this->num_vox = 0;
-    memset (this->grad, 0, this->num_coeff * sizeof(float));
-    this->time_smetric = 0;
+    memset (this->smetric_grad, 0, this->num_coeff * sizeof(float));
+    memset (this->total_grad, 0, this->num_coeff * sizeof(float));
+    this->time_smetric.clear();
     this->time_rmetric = 0;
 }
+
+void
+Bspline_score::accumulate_grad (float lambda)
+{
+    for (plm_long i = 0; i < this->num_coeff; i++) {
+        this->total_grad[i] += lambda * this->smetric_grad[i];
+    }
+    this->reset_smetric_grad ();
+}
+
+void
+Bspline_score::update_smetric_grad (
+    const Bspline_xform* bxf, 
+    const plm_long p[3],
+    plm_long qidx,
+    const float dc_dv[3])
+{
+    this->update_grad (this->smetric_grad, bxf, p, qidx, dc_dv);
+}
+
+void
+Bspline_score::update_total_grad (
+    const Bspline_xform* bxf, 
+    const plm_long p[3],
+    plm_long qidx,
+    const float dc_dv[3])
+{
+    this->update_grad (this->total_grad, bxf, p, qidx, dc_dv);
+}
+
+void
+Bspline_score::update_smetric_grad_b (
+    const Bspline_xform* bxf, 
+    plm_long pidx, 
+    plm_long qidx, 
+    const float dc_dv[3])
+{
+    this->update_grad_b (this->smetric_grad, bxf, pidx, qidx, dc_dv);
+}
+
+void
+Bspline_score::update_total_grad_b (
+    const Bspline_xform* bxf, 
+    plm_long pidx, 
+    plm_long qidx, 
+    const float dc_dv[3])
+{
+    this->update_grad_b (this->total_grad, bxf, pidx, qidx, dc_dv);
+}
+
+void
+Bspline_score::update_grad (
+    float *grad,
+    const Bspline_xform* bxf, 
+    const plm_long p[3],
+    plm_long qidx,
+    const float dc_dv[3])
+{
+    plm_long i, j, k, m;
+    plm_long cidx;
+    float* q_lut = &bxf->q_lut[qidx*64];
+
+    m = 0;
+    for (k = 0; k < 4; k++) {
+        for (j = 0; j < 4; j++) {
+            for (i = 0; i < 4; i++) {
+                cidx = (p[2] + k) * bxf->cdims[1] * bxf->cdims[0]
+                    + (p[1] + j) * bxf->cdims[0]
+                    + (p[0] + i);
+                cidx = cidx * 3;
+                grad[cidx+0] += dc_dv[0] * q_lut[m];
+                grad[cidx+1] += dc_dv[1] * q_lut[m];
+                grad[cidx+2] += dc_dv[2] * q_lut[m];
+                m ++;
+            }
+        }
+    }
+}
+
+void
+Bspline_score::update_grad_b (
+    float *grad,
+    const Bspline_xform* bxf, 
+    plm_long pidx, 
+    plm_long qidx, 
+    const float dc_dv[3])
+{
+    plm_long i, j, k, m;
+    plm_long cidx;
+    float* q_lut = &bxf->q_lut[qidx*64];
+    plm_long* c_lut = &bxf->c_lut[pidx*64];
+
+    m = 0;
+    for (k = 0; k < 4; k++) {
+        for (j = 0; j < 4; j++) {
+            for (i = 0; i < 4; i++) {
+                cidx = 3 * c_lut[m];
+                grad[cidx+0] += dc_dv[0] * q_lut[m];
+                grad[cidx+1] += dc_dv[1] * q_lut[m];
+                grad[cidx+2] += dc_dv[2] * q_lut[m];
+                m ++;
+            }
+        }
+    }
+}
diff --git a/src/plastimatch/register/bspline_score.h b/src/plastimatch/register/bspline_score.h
index d79e955..b8c8a49 100644
--- a/src/plastimatch/register/bspline_score.h
+++ b/src/plastimatch/register/bspline_score.h
@@ -5,28 +5,69 @@
 #define _bspline_score_h_
 
 #include "plmregister_config.h"
+#include <vector>
 #include "plm_int.h"
 
+class Bspline_xform;
+
 class PLMREGISTER_API Bspline_score
 {
 public:
     Bspline_score ();
     ~Bspline_score ();
 public:
-    float score;         /* Total Score (sent to optimizer) */
-    float lmetric;       /* Landmark metric */
-    float rmetric;       /* Regularization metric */
-    float smetric;       /* Similarity metric */
-    plm_long num_vox;    /* Number of voxel with correspondence */
+    float score;           /* Total Score (sent to optimizer) */
+    float lmetric;         /* Landmark metric */
+    float rmetric;         /* Regularization metric */
+    std::vector<float> smetric;  /* Similarity metric */
 
-    plm_long num_coeff;  /* Size of gradient vector = num coefficents */
-    float* grad;         /* Gradient score wrt control coeff */
+    plm_long num_vox;      /* Number of voxel with correspondence */
+    plm_long num_coeff;    /* Size of gradient vector = num coefficents */
+    float* smetric_grad;   /* Gradient of score for current smetric */
+    float* total_grad;     /* Total cost function gradient wrt coefficients */
 
-    double time_smetric;   /* Time to compute similarity metric */
-    double time_rmetric;   /* Time to compute regularization metric */
+    /* Time to compute similarity metric */
+    std::vector<double> time_smetric;
+    /* Time to compute regularization metric */
+    double time_rmetric;
 public:
     void set_num_coeff (plm_long num_coeff);
+    void reset_smetric_grad ();
     void reset_score ();
+    void accumulate_grad (float lambda);
+    void update_smetric_grad (
+        const Bspline_xform* bxf, 
+        const plm_long p[3],
+        plm_long qidx,
+        const float dc_dv[3]);
+    void update_total_grad (
+        const Bspline_xform* bxf, 
+        const plm_long p[3],
+        plm_long qidx,
+        const float dc_dv[3]);
+    void update_smetric_grad_b (
+        const Bspline_xform* bxf, 
+        plm_long pidx, 
+        plm_long qidx, 
+        const float dc_dv[3]);
+    void update_total_grad_b (
+        const Bspline_xform* bxf, 
+        plm_long pidx, 
+        plm_long qidx, 
+        const float dc_dv[3]);
+protected:
+    void update_grad (
+        float *grad,
+        const Bspline_xform* bxf, 
+        const plm_long p[3],
+        plm_long qidx,
+        const float dc_dv[3]);
+    void update_grad_b (
+        float *grad,
+        const Bspline_xform *bxf, 
+        plm_long pidx, 
+        plm_long qidx, 
+        const float dc_dv[3]);
 };
 
 #endif
diff --git a/src/plastimatch/register/bspline_stage.cxx b/src/plastimatch/register/bspline_stage.cxx
index 1547748..94a3684 100644
--- a/src/plastimatch/register/bspline_stage.cxx
+++ b/src/plastimatch/register/bspline_stage.cxx
@@ -299,6 +299,11 @@ Bspline_stage::initialize ()
     }
     logfile_printf ("Algorithm flavor = %c\n", bsp_parms->implementation);
 
+    if (stage->threading_type == THREADING_CUDA) {
+        bsp_parms->gpuid = stage->gpuid;
+        logfile_printf ("GPU ID = %d\n", bsp_parms->gpuid);
+    }
+    
     /* Regularization */
     bsp_parms->reg_parms->lambda = stage->regularization_lambda;
     switch (stage->regularization_type) {
diff --git a/src/plastimatch/register/bspline_state.cxx b/src/plastimatch/register/bspline_state.cxx
index 83a6526..7f1fb80 100644
--- a/src/plastimatch/register/bspline_state.cxx
+++ b/src/plastimatch/register/bspline_state.cxx
@@ -14,6 +14,7 @@
 #include "bspline.h"
 #if (CUDA_FOUND)
 #include "bspline_cuda.h"
+#include "cuda_util.h"
 #endif
 #include "bspline_interpolate.h"
 #include "bspline_landmarks.h"
@@ -80,13 +81,14 @@ Bspline_state::initialize (
     Bspline_xform *bxf,
     Bspline_parms *parms)
 {
-    Reg_parms* reg_parms = parms->reg_parms;
+    Regularization_parms* reg_parms = parms->reg_parms;
     Bspline_regularize* rst = &this->rst;
     Bspline_landmarks* blm = parms->blm;
 
     d_ptr->bxf = bxf;
     d_ptr->parms = parms;
 
+    this->sm = 0;
     this->it = 0;
     this->feval = 0;
     this->dev_ptrs = 0;
@@ -148,14 +150,20 @@ bspline_cuda_state_create (
 )
 {
 #if (CUDA_FOUND)
+    /* Set the gpuid */
+    LOAD_LIBRARY_SAFE (libplmcuda);
+    LOAD_SYMBOL (CUDA_selectgpu, libplmcuda);
+    CUDA_selectgpu (parms->gpuid);
+    UNLOAD_LIBRARY (libplmcuda);
+    
     Volume *fixed = parms->fixed;
     Volume *moving = parms->moving;
     Volume *moving_grad = parms->moving_grad;
 
     Dev_Pointers_Bspline* dev_ptrs 
         = (Dev_Pointers_Bspline*) malloc (sizeof (Dev_Pointers_Bspline));
-
     bst->dev_ptrs = dev_ptrs;
+    
     if ((parms->threading == BTHR_CUDA) && (parms->metric_type[0] == REGISTRATION_METRIC_MSE)) {
         /* Be sure we loaded the CUDA plugin */
         LOAD_LIBRARY_SAFE (libplmregistercuda);
@@ -235,7 +243,7 @@ bspline_state_create (
 )
 {
     Bspline_state *bst = (Bspline_state*) malloc (sizeof (Bspline_state));
-    Reg_parms* reg_parms = parms->reg_parms;
+    Regularization_parms* reg_parms = parms->reg_parms;
     Bspline_regularize* rst = &bst->rst;
     Bspline_landmarks* blm = parms->blm;
 
diff --git a/src/plastimatch/register/bspline_state.h b/src/plastimatch/register/bspline_state.h
index 13f1728..18645d7 100644
--- a/src/plastimatch/register/bspline_state.h
+++ b/src/plastimatch/register/bspline_state.h
@@ -25,11 +25,12 @@ public:
     ~Bspline_state ();
     void initialize (Bspline_xform *bxf, Bspline_parms *parms);
 public:
-    int it;              /* Number of iterations */
-    int feval;           /* Number of function evaluations */
-    Bspline_score ssd;   /* Score and Gradient  */
-    void* dev_ptrs;      /* GPU Device Pointers */
-    Bspline_regularize rst;   /* Analytic regularization */
+    int sm;                         /* Current smetric */
+    int it;                         /* Current iterations */
+    int feval;                      /* Number of function evaluations */
+    Bspline_score ssd;              /* Score and Gradient  */
+    void* dev_ptrs;                 /* GPU Device Pointers */
+    Bspline_regularize rst;         /* Analytic regularization */
     Bspline_mi_hist_set *mi_hist;   /* MI histograms */
 public:
     Bspline_score* get_bspline_score () {
diff --git a/src/plastimatch/register/cuda/CMakeLists.txt b/src/plastimatch/register/cuda/CMakeLists.txt
index b756080..7f3a415 100644
--- a/src/plastimatch/register/cuda/CMakeLists.txt
+++ b/src/plastimatch/register/cuda/CMakeLists.txt
@@ -114,6 +114,7 @@ if (CUDA_FOUND)
       "${PLMREGISTERCUDA_LIBRARY_SRC}"
       "${PLMREGISTERCUDA_LIBRARY_DEPENDENCIES}"
       ""
+      "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
       "")
   endif ()
 endif ()
diff --git a/src/plastimatch/register/cuda/bspline_cuda.cu b/src/plastimatch/register/cuda/bspline_cuda.cu
index 6ae75f8..2174513 100644
--- a/src/plastimatch/register/cuda/bspline_cuda.cu
+++ b/src/plastimatch/register/cuda/bspline_cuda.cu
@@ -1146,7 +1146,6 @@ CUDA_bspline_mi_hist_jnt (
 
 }
 
-
 void
 CUDA_bspline_mi_grad (
     Bspline_state *bst,
@@ -1154,6 +1153,7 @@ CUDA_bspline_mi_grad (
     Volume* fixed,
     Volume* moving,
     float num_vox_f,
+    float score,
     Dev_Pointers_Bspline *dev_ptrs
 )
 {
@@ -1162,8 +1162,7 @@ CUDA_bspline_mi_grad (
     build_gbd (&gbd, bxf, fixed, moving);
 
     Bspline_score* ssd = &bst->ssd;
-    float* host_grad = ssd->grad;
-    float score = ssd->smetric;
+    float* host_grad = ssd->smetric_grad;
 
     if ((mi_hist->fixed.bins > GPU_MAX_BINS) ||
         (mi_hist->moving.bins > GPU_MAX_BINS)) {
diff --git a/src/plastimatch/register/cuda/bspline_cuda.cxx b/src/plastimatch/register/cuda/bspline_cuda.cxx
index 213ebf6..3a98a40 100644
--- a/src/plastimatch/register/cuda/bspline_cuda.cxx
+++ b/src/plastimatch/register/cuda/bspline_cuda.cxx
@@ -27,7 +27,6 @@
 #include "interpolate_macros.h"
 #include "logfile.h"
 #include "plm_math.h"
-#include "plm_timer.h"
 #include "volume.h"
 #include "volume_macros.h"
 
@@ -71,42 +70,6 @@ clamp_linear_interpolate (
 }
 
 void
-report_score (
-    char *alg, 
-    Bspline_xform *bxf, 
-    Bspline_state *bst, 
-    int num_vox, 
-    double timing)
-{
-    int i;
-    float ssd_grad_norm, ssd_grad_mean;
-
-    /* Normalize gradient */
-    ssd_grad_norm = 0;
-    ssd_grad_mean = 0;
-    for (i = 0; i < bxf->num_coeff; i++) {
-	ssd_grad_mean += bst->ssd.grad[i];
-	ssd_grad_norm += fabs (bst->ssd.grad[i]);
-    }
-
-    // JAS 04.19.2010
-    // MI scores are between 0 and 1
-    // The extra decimal point resolution helps in seeing
-    // if the optimizer is performing adequately.
-    if (!strcmp (alg, "MI")) {
-	logfile_printf (
-	    "%s[%2d,%3d] %1.8f NV %6d GM %9.3f GN %9.3f [%9.3f secs]\n", 
-	    alg, bst->it, bst->feval, bst->ssd.score, num_vox, ssd_grad_mean, 
-	    ssd_grad_norm, timing);
-    } else {
-	logfile_printf (
-	    "%s[%2d,%3d] %9.3f NV %6d GM %9.3f GN %9.3f [%9.3f secs]\n", 
-	    alg, bst->it, bst->feval, bst->ssd.score, num_vox, ssd_grad_mean, 
-	    ssd_grad_norm, timing);
-    }
-}
-
-void
 bspline_interp_pix_b (
     float out[3], 
     Bspline_xform* bxf, 
@@ -657,7 +620,6 @@ CUDA_bspline_mi_a (
 
     // --- DECLARE LOCAL VARIABLES ------------------------------
     Bspline_score* ssd; // Holds the SSD "Score" information
-    Plm_timer* timer = new Plm_timer;
     Bspline_mi_hist_set* mi_hist = bst->mi_hist;
     double* f_hist = mi_hist->f_hist;
     double* m_hist = mi_hist->m_hist;
@@ -708,18 +670,13 @@ CUDA_bspline_mi_a (
     CUDA_bspline_zero_grad  (dev_ptrs);
     // ----------------------------------------------------------
 
-    timer->start ();
-
     // --- GENERATE HISTOGRMS -----------------------------------
-//  plm_timer_start (&timer0);
     if ((mi_hist->fixed.bins > GPU_MAX_BINS) ||
         (mi_hist->moving.bins > GPU_MAX_BINS)) {
 
         ssd->num_vox = CPU_MI_Hist (mi_hist, bxf, fixed, moving);
-//        printf (" * hists: %9.3f s\t [CPU]\n", plm_timer_report(&timer0));
     } else {
         ssd->num_vox = CUDA_bspline_mi_hist (dev_ptrs, mi_hist, fixed, moving, bxf);
-//        printf (" * hists: %9.3f s\t [GPU]\n", plm_timer_report(&timer0));
     }
     // ----------------------------------------------------------
 
@@ -740,39 +697,31 @@ CUDA_bspline_mi_a (
 
 
     // --- COMPUTE SCORE ----------------------------------------
-//  plm_timer_start (&timer0);
 #if defined (MI_SCORE_CPU)
-    ssd->smetric = CPU_MI_Score(mi_hist, ssd->num_vox);
-//  printf (" * score: %9.3f s\t [CPU]\n", plm_timer_report(&timer0));
+    ssd->smetric[0] = CPU_MI_Score(mi_hist, ssd->num_vox);
 #else
     // Doing this on the GPU may be silly.
     // The CPU generally completes this computation extremely quickly
-//  printf (" * score: %9.3f s\t [GPU]\n", plm_timer_report(&timer0));
 #endif
     // ----------------------------------------------------------
 
     // --- COMPUTE GRADIENT -------------------------------------
-//  plm_timer_start (&timer0);
 #if defined (MI_GRAD_CPU)
     CPU_MI_Grad(mi_hist, bst, bxf, fixed, moving, (float)ssd->num_vox);
-//  printf (" *  grad: %9.3f s\t [CPU]\n", plm_timer_report(&timer0));
 #else
+    float score = ssd->smetric[0];
     CUDA_bspline_mi_grad (
         bst,
         bxf,
         fixed,
         moving,
         (float)ssd->num_vox,
+        score,
         dev_ptrs
     );
-//  printf (" *  grad: %9.3f s\t [GPU]\n", plm_timer_report(&timer0));
 #endif
     // ----------------------------------------------------------
 
-
-    ssd->time_smetric = timer->report ();
-    delete timer;
-
     if (parms->debug) {
         fclose (fp);
     }
@@ -797,13 +746,12 @@ CUDA_bspline_mse_j (
     Bspline_score* ssd;     // Holds the SSD "Score" information
     float ssd_grad_norm;    // Holds the SSD Gradient's Norm
     float ssd_grad_mean;    // Holds the SSD Gradient's Mean
-    Plm_timer* timer = new Plm_timer;
 
     static int it=0;        // Holds Iteration Number
     char debug_fn[1024];    // Debug message buffer
     FILE* fp = NULL;        // File Pointer to Debug File
     // ----------------------------------------------------------
-
+    
     // --- INITIALIZE LOCAL VARIABLES ---------------------------
     ssd = &bst->ssd;
     
@@ -813,8 +761,6 @@ CUDA_bspline_mse_j (
     }
     // ----------------------------------------------------------
 
-    timer->start ();
-    
     // --- INITIALIZE GPU MEMORY --------------------------------
     CUDA_bspline_push_coeff (dev_ptrs, bxf);
     CUDA_bspline_zero_score (dev_ptrs);
@@ -843,8 +789,8 @@ CUDA_bspline_mse_j (
         fixed,
         bxf->vox_per_rgn,
         fixed->dim,
-        &(ssd->smetric),
-        bst->ssd.grad,
+        &(ssd->smetric[0]),
+        bst->ssd.smetric_grad,
         &ssd_grad_mean,
         &ssd_grad_norm,
         dev_ptrs,
@@ -854,10 +800,6 @@ CUDA_bspline_mse_j (
     if (parms->debug) {
         fclose (fp);
     }
-
-    // --- USER FEEDBACK ----------------------------------------
-    ssd->time_smetric = timer->report ();
-    delete timer;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/plastimatch/register/cuda/bspline_cuda.h b/src/plastimatch/register/cuda/bspline_cuda.h
index 8b7ecb1..d8e73e7 100644
--- a/src/plastimatch/register/cuda/bspline_cuda.h
+++ b/src/plastimatch/register/cuda/bspline_cuda.h
@@ -318,6 +318,7 @@ extern "C" {
         Volume* fixed,
         Volume* moving,
         float num_vox_f,
+        float score,
         Dev_Pointers_Bspline *dev_ptrs
     );
 
diff --git a/src/plastimatch/register/itk_optimizer.cxx b/src/plastimatch/register/itk_optimizer.cxx
index 27928d8..7f18bc9 100644
--- a/src/plastimatch/register/itk_optimizer.cxx
+++ b/src/plastimatch/register/itk_optimizer.cxx
@@ -494,7 +494,7 @@ set_optimization_scales_versor (
 	rotation_scale = 1.0;
 	translation_scale = 1.0;
     } else {
-        rotation_scale = 1.0;
+        rotation_scale = 1.0 / (double) stage->rotation_scale_factor;
         translation_scale = 1.0 / (double) stage->translation_scale_factor;
     }
 
@@ -516,7 +516,7 @@ set_optimization_scales_quaternion (
     double rotation_scale, translation_scale;
     itk::Array<double> optimizerScales(7);
 
-    rotation_scale = 1.0;
+    rotation_scale = 1.0 / (double) stage->rotation_scale_factor;
     translation_scale = 1.0 / (double) stage->translation_scale_factor;
 
     /* GCS FIX: Changing the scale fudge_factor is one way to avoid 
@@ -564,6 +564,27 @@ set_optimization_scales_affine (RegistrationType::Pointer registration,
 }
 
 void
+set_optimization_scales_similarity (RegistrationType::Pointer registration,
+                Stage_parms* stage)
+{
+    itk::Array<double> optimizerScales(7);
+
+    const double rotation_scale = 1.0 / (double) stage->rotation_scale_factor;
+    const double translation_scale = 1.0/ (double) stage->translation_scale_factor;
+    const double scaling_scale = 1.0/ (double) stage->scaling_scale_factor;
+
+    optimizerScales[0] =  rotation_scale;
+    optimizerScales[1] =  rotation_scale;
+    optimizerScales[2] =  rotation_scale;
+    optimizerScales[3] =  translation_scale;
+    optimizerScales[4] =  translation_scale;
+    optimizerScales[5] =  translation_scale;
+    optimizerScales[6] =  scaling_scale;
+
+    registration->GetOptimizer()->SetScales(optimizerScales);
+}
+
+void
 Itk_registration_private::set_optimization ()
 {
     if (stage->xform_type == STAGE_TRANSFORM_QUATERNION)
@@ -572,7 +593,8 @@ Itk_registration_private::set_optimization ()
     }
     else if (stage->optim_type == OPTIMIZATION_VERSOR
 	&& (stage->xform_type == STAGE_TRANSFORM_TRANSLATION
-	    || stage->xform_type == STAGE_TRANSFORM_AFFINE))
+        || stage->xform_type == STAGE_TRANSFORM_AFFINE
+        || stage->xform_type == STAGE_TRANSFORM_SIMILARITY))
     {
 	stage->optim_type = OPTIMIZATION_RSG;
     }
@@ -588,11 +610,11 @@ Itk_registration_private::set_optimization ()
 	set_optimization_amoeba(registration,stage);
 	break;
     case OPTIMIZATION_ONEPLUSONE:
-        set_optimization_oneplusone(registration,stage);
-        break;
+    set_optimization_oneplusone(registration,stage);
+    break;
     case OPTIMIZATION_FRPR:
-        set_optimization_frpr(registration,stage);
-        break;
+    set_optimization_frpr(registration,stage);
+    break;
     case OPTIMIZATION_RSG:
 	set_optimization_rsg(registration,stage);
 	break;
@@ -625,6 +647,9 @@ Itk_registration_private::set_optimization ()
     case STAGE_TRANSFORM_AFFINE:
 	set_optimization_scales_affine (registration, stage);
 	break;
+    case STAGE_TRANSFORM_SIMILARITY:
+    set_optimization_scales_similarity (registration, stage);
+    break;
     case STAGE_TRANSFORM_BSPLINE:
 	/* LBFGS/LBFGSB only. No optimizer scales. */
 	break;
diff --git a/src/plastimatch/register/itk_registration.cxx b/src/plastimatch/register/itk_registration.cxx
index d4c66f2..18ae8d4 100644
--- a/src/plastimatch/register/itk_registration.cxx
+++ b/src/plastimatch/register/itk_registration.cxx
@@ -179,6 +179,10 @@ Itk_registration_private::set_best_xform ()
         xf_best->set_aff (
             registration->GetTransform()->GetParameters());
         break;
+    case STAGE_TRANSFORM_SIMILARITY:
+        xf_best->set_similarity (
+            registration->GetTransform()->GetParameters());
+        break;
     case STAGE_TRANSFORM_BSPLINE: {
         /* GCS FIX: The B-spline method still gives the last xform, 
            not the best xform  */
@@ -570,6 +574,19 @@ set_transform_affine (
     registration->SetTransform (xf_out->get_aff());
 }
 
+void
+set_transform_similarity (
+    RegistrationType::Pointer registration,
+    Xform *xf_out,
+    const Xform *xf_in,
+    Stage_parms* stage)
+{
+    Plm_image_header pih;
+    pih.set_from_itk_image (registration->GetFixedImage());
+    xform_to_similarity (xf_out, xf_in, &pih);
+    registration->SetTransform (xf_out->get_similarity());
+}
+
 static void
 set_transform_bspline (
     RegistrationType::Pointer registration,
@@ -601,6 +618,9 @@ Itk_registration_private::set_transform ()
     case STAGE_TRANSFORM_QUATERNION:
         set_transform_quaternion (registration, xf_out, xf_in, stage);
         break;
+    case STAGE_TRANSFORM_SIMILARITY:
+        set_transform_similarity (registration, xf_out, xf_in, stage);
+        break;
     case STAGE_TRANSFORM_AFFINE:
         set_transform_affine (registration, xf_out, xf_in, stage);
         break;
diff --git a/src/plastimatch/register/registration.cxx b/src/plastimatch/register/registration.cxx
index f2eab7a..0bf6744 100644
--- a/src/plastimatch/register/registration.cxx
+++ b/src/plastimatch/register/registration.cxx
@@ -97,6 +97,18 @@ Registration::set_moving_image (Plm_image::Pointer& moving)
     d_ptr->rdata->moving_image = moving;
 }
 
+void
+Registration::set_fixed_roi (Plm_image::Pointer& fixed_roi)
+{
+    d_ptr->rdata->fixed_roi = fixed_roi;
+}
+
+void
+Registration::set_moving_roi (Plm_image::Pointer& moving_roi)
+{
+    d_ptr->rdata->moving_roi = moving_roi;
+}
+
 Registration_data::Pointer
 Registration::get_registration_data ()
 {
@@ -242,7 +254,8 @@ save_output (
     float default_value, 
     const std::string& img_out_fn,
     const std::string& vf_out_fn,
-    const std::string& warped_landmarks_fn
+    const std::string& warped_landmarks_fn,
+    const std::string& valid_roi_out_fn
 )
 {
     /* Handle null xf, make it zero translation */
@@ -264,7 +277,9 @@ save_output (
         }
     }
 
-    if (img_out_fn[0] || vf_out_fn[0] || warped_landmarks_fn[0]) {
+    if (img_out_fn[0] || vf_out_fn[0] || warped_landmarks_fn[0]
+        || valid_roi_out_fn[0])
+    {
         DeformationFieldType::Pointer vf;
         DeformationFieldType::Pointer *vfp;
         Plm_image::Pointer im_warped;
@@ -307,6 +322,15 @@ save_output (
             logfile_printf ("Saving vf...\n");
             itk_image_save (vf, vf_out_fn);
         }
+        if (valid_roi_out_fn[0]) {
+            logfile_printf ("Warping valid ROI...\n");
+            Plm_image::Pointer valid_roi
+                = Plm_image::clone (regd->moving_image);
+#if defined (commentout)
+            plm_warp (im_warped, vfp, xf_out, &pih, regd->moving_image, 
+                default_value, 0, 1);
+#endif
+        }
     }
 }
 
@@ -352,6 +376,7 @@ Registration::do_registration_stage (
     case STAGE_TRANSFORM_NONE:
     case STAGE_TRANSFORM_VERSOR:
     case STAGE_TRANSFORM_QUATERNION:
+    case STAGE_TRANSFORM_SIMILARITY:
     case STAGE_TRANSFORM_AFFINE:
         xf_out = do_itk_registration_stage (regd.get(), xf_in, stage);
         break;
@@ -384,7 +409,7 @@ set_auto_resample (float subsample_rate[], Plm_image *pli)
     Plm_image_header pih (pli);
 
     for (int d = 0; d < 3; d++) {
-        subsample_rate[d] = (float) ((pih.Size(d)+99) / 100);
+        subsample_rate[d] = (float) ((pih.dim(d)+99) / 100);
     }
 }
 
@@ -517,7 +542,8 @@ Registration::run_main_thread ()
                 stage->xf_out_fn, stage->xf_out_itk, 
                 stage->img_out_fmt, stage->img_out_type, 
                 stage->default_value, stage->img_out_fn, 
-                stage->vf_out_fn, shared->warped_landmarks_fn);
+                stage->vf_out_fn, shared->warped_landmarks_fn, 
+                shared->valid_roi_out_fn);
 
             /* Tell the parent thread that we finished a stage, 
                so it can wake up if needed. */
@@ -598,7 +624,8 @@ Registration::save_global_outputs ()
     save_output (regd.get(), d_ptr->xf_out, regp->xf_out_fn, regp->xf_out_itk, 
         regp->img_out_fmt, regp->img_out_type, 
         regp->default_value, regp->img_out_fn, 
-        regp->vf_out_fn, shared->warped_landmarks_fn.c_str());
+        regp->vf_out_fn, shared->warped_landmarks_fn,
+        shared->valid_roi_out_fn);
 }
 
 Xform::Pointer
diff --git a/src/plastimatch/register/registration.h b/src/plastimatch/register/registration.h
index ef9b65d..aca6409 100644
--- a/src/plastimatch/register/registration.h
+++ b/src/plastimatch/register/registration.h
@@ -23,6 +23,8 @@ public:
     int set_command_string (const std::string& command_string);
     void set_fixed_image (Plm_image::Pointer& fixed);
     void set_moving_image (Plm_image::Pointer& moving);
+    void set_fixed_roi (Plm_image::Pointer& fixed_roi);
+    void set_moving_roi (Plm_image::Pointer& moving_roi);
 
     Registration_data::Pointer get_registration_data ();
     Registration_parms::Pointer get_registration_parms ();
diff --git a/src/plastimatch/register/registration_metric_type.cxx b/src/plastimatch/register/registration_metric_type.cxx
new file mode 100644
index 0000000..432da22
--- /dev/null
+++ b/src/plastimatch/register/registration_metric_type.cxx
@@ -0,0 +1,28 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plmbase_config.h"
+#include <stdio.h>
+#include <string.h>
+#include "registration_metric_type.h"
+
+const char* 
+registration_metric_type_string (Registration_metric_type type)
+{
+    switch (type) {
+    case REGISTRATION_METRIC_NONE:
+        return "none";
+    case REGISTRATION_METRIC_GM:
+        return "GM";
+    case REGISTRATION_METRIC_MI_MATTES:
+        return "MI";
+    case REGISTRATION_METRIC_MI_VW:
+        return "MIVW";
+    case REGISTRATION_METRIC_MSE:
+        return "MSE";
+    case REGISTRATION_METRIC_NMI:
+        return "NMI";
+    default:
+        return "(unkn)";
+    }
+}
diff --git a/src/plastimatch/register/registration_metric_type.h b/src/plastimatch/register/registration_metric_type.h
index 617b344..927b7eb 100755
--- a/src/plastimatch/register/registration_metric_type.h
+++ b/src/plastimatch/register/registration_metric_type.h
@@ -13,4 +13,6 @@ enum Registration_metric_type {
     REGISTRATION_METRIC_NMI
 };
 
+const char* registration_metric_type_string (Registration_metric_type);
+
 #endif
diff --git a/src/plastimatch/register/registration_parms.cxx b/src/plastimatch/register/registration_parms.cxx
index d4d3c39..96463ca 100644
--- a/src/plastimatch/register/registration_parms.cxx
+++ b/src/plastimatch/register/registration_parms.cxx
@@ -339,6 +339,10 @@ Registration_parms::set_key_value (
             goto key_not_allowed_in_section_process;
         }
     }
+    else if (key == "valid_roi_out") {
+        if (section_process) goto key_not_allowed_in_section_process;
+        shared->valid_roi_out_fn = val;
+    }
     else if (key == "fixed_landmarks") {
         if (section_process) goto key_not_allowed_in_section_process;
         shared->fixed_landmarks_fn = val;
@@ -396,6 +400,9 @@ Registration_parms::set_key_value (
         else if (val == "affine") {
             stage->xform_type = STAGE_TRANSFORM_AFFINE;
         }
+        else if (val == "similarity") {
+            stage->xform_type = STAGE_TRANSFORM_SIMILARITY;
+        }
         else if (val == "bspline") {
             stage->xform_type = STAGE_TRANSFORM_BSPLINE;
         }
@@ -516,6 +523,12 @@ Registration_parms::set_key_value (
             goto error_exit;
         }
     }
+    else if (key == "gpuid") {
+        if (!section_stage) goto key_only_allowed_in_section_stage;
+        if (sscanf (val.c_str(), "%d", &stage->gpuid) != 1) {
+            goto error_exit;
+        }
+    }
     else if (key == "metric" || key == "smetric") {
         if (!section_stage) goto key_only_allowed_in_section_stage;
         std::vector<std::string> metric_vec = string_split (val, ',');
@@ -534,9 +547,15 @@ Registration_parms::set_key_value (
                 stage->metric_type.push_back (REGISTRATION_METRIC_MSE);
             }
             else if (metric_vec[i] == "mi" || metric_vec[i] == "MI") {
+#if PLM_CONFIG_LEGACY_MI_METRIC
                 stage->metric_type.push_back (REGISTRATION_METRIC_MI_VW);
+#else
+                stage->metric_type.push_back (REGISTRATION_METRIC_MI_MATTES);
+#endif
             }
-            else if (metric_vec[i] == "mi_vw") {
+            else if (metric_vec[i] == "mi_vw"
+                    || metric_vec[i] == "viola-wells")
+            {
                 stage->metric_type.push_back (REGISTRATION_METRIC_MI_VW);
             }
             else if (metric_vec[i] == "nmi" || metric_vec[i] == "NMI") {
@@ -656,6 +675,18 @@ Registration_parms::set_key_value (
             goto error_exit;
         }
     }
+    else if (key == "rotation_scale_factor") {
+        if (!section_stage) goto key_only_allowed_in_section_stage;
+        if (sscanf (val.c_str(), "%d", &stage->rotation_scale_factor) != 1) {
+            goto error_exit;
+        }
+    }
+    else if (key == "scaling_scale_factor") {
+        if (!section_stage) goto key_only_allowed_in_section_stage;
+        if (sscanf (val.c_str(), "%f", &stage->scaling_scale_factor) != 1) {
+            goto error_exit;
+        }
+    }
     else if (key == "convergence_tol") {
         if (!section_stage) goto key_only_allowed_in_section_stage;
         if (sscanf (val.c_str(), "%g", &stage->convergence_tol) != 1) {
@@ -840,6 +871,15 @@ Registration_parms::set_key_value (
             goto error_exit;
         }
     }
+    else if (key == "gridsearch_min_steps") {
+        if (!section_stage) goto key_only_allowed_in_section_stage;
+        if (sscanf (val.c_str(), "%d %d %d", 
+                &(stage->gridsearch_min_steps[0]), 
+                &(stage->gridsearch_min_steps[1]), 
+                &(stage->gridsearch_min_steps[2])) != 3) {
+            goto error_exit;
+        }
+    }
     else if (key == "gridsearch_strategy") {
         if (!section_stage) goto key_only_allowed_in_section_stage;
         if (val == "global") {
diff --git a/src/plastimatch/register/registration_resample.cxx b/src/plastimatch/register/registration_resample.cxx
index 1e33699..aa734a5 100755
--- a/src/plastimatch/register/registration_resample.cxx
+++ b/src/plastimatch/register/registration_resample.cxx
@@ -29,6 +29,12 @@ registration_resample_volume (
     switch (stage->resample_type) {
     case RESAMPLE_AUTO:
     case RESAMPLE_VOXEL_RATE:
+        if (resample_rate[0] == 1.0f
+            && resample_rate[1] == 1.0f
+            && resample_rate[2] == 1.0f)
+        {
+            return vol->clone ();
+        }
         if (shared->legacy_subsampling) {
             return volume_subsample_vox_legacy (vol, resample_rate);
         } else {
diff --git a/src/plastimatch/register/shared_parms.h b/src/plastimatch/register/shared_parms.h
index 32f1550..b6a2d31 100755
--- a/src/plastimatch/register/shared_parms.h
+++ b/src/plastimatch/register/shared_parms.h
@@ -19,6 +19,7 @@ public:
     bool moving_roi_enable;
     std::string fixed_roi_fn;
     std::string moving_roi_fn;
+    std::string valid_roi_out_fn;
 
     /* Stiffness map */
     bool fixed_stiffness_enable;
diff --git a/src/plastimatch/register/stage_parms.cxx b/src/plastimatch/register/stage_parms.cxx
index 68e9617..357876c 100644
--- a/src/plastimatch/register/stage_parms.cxx
+++ b/src/plastimatch/register/stage_parms.cxx
@@ -48,6 +48,8 @@ Stage_parms::Stage_parms ()
     optim_subtype = OPTIMIZATION_SUB_FSF;
     alg_flavor = 0;
     threading_type = THREADING_CPU_OPENMP;
+    gpuid = 0;
+    /* Similarity metric */
     metric_type.push_back (REGISTRATION_METRIC_MSE);
     metric_lambda.push_back (1.0);
     regularization_type = REGULARIZATION_BSPLINE_ANALYTIC;
@@ -77,6 +79,8 @@ Stage_parms::Stage_parms ()
     min_step = 0.001;
     rsg_grad_tol = 0.0001;
     translation_scale_factor = 1000.0;
+    rotation_scale_factor = 1;
+    scaling_scale_factor = 10;
     /*OnePlusOne evolutionary optimizer*/
     opo_initial_search_rad=1.01;
     opo_epsilon=1e-7;
@@ -131,6 +135,9 @@ Stage_parms::Stage_parms ()
     gridsearch_step_size[0] = 10;
     gridsearch_step_size[1] = 10;
     gridsearch_step_size[2] = 10;
+    gridsearch_min_steps[0] = 0;
+    gridsearch_min_steps[1] = 0;
+    gridsearch_min_steps[2] = 0;
     /* Landmarks */
     landmark_stiffness = 1.0;
     landmark_flavor = 'a';
@@ -162,6 +169,8 @@ Stage_parms::Stage_parms (const Stage_parms& s)
     optim_subtype = s.optim_subtype;
     alg_flavor = s.alg_flavor;
     threading_type = s.threading_type;
+    gpuid = s.gpuid;
+    /* Similarity metric */
     metric_type = s.metric_type;
     metric_lambda = s.metric_lambda;
     regularization_type = s.regularization_type;
@@ -190,6 +199,8 @@ Stage_parms::Stage_parms (const Stage_parms& s)
     min_step = s.min_step;
     rsg_grad_tol = s.rsg_grad_tol;
     translation_scale_factor = s.translation_scale_factor;
+    rotation_scale_factor = s.rotation_scale_factor;
+    scaling_scale_factor = s.scaling_scale_factor;
 
     /*OnePlusOne optmizer*/
     opo_epsilon=s.opo_epsilon;
@@ -246,6 +257,9 @@ Stage_parms::Stage_parms (const Stage_parms& s)
     gridsearch_step_size[0] = s.gridsearch_step_size[0];
     gridsearch_step_size[1] = s.gridsearch_step_size[1];
     gridsearch_step_size[2] = s.gridsearch_step_size[2];
+    gridsearch_min_steps[0] = s.gridsearch_min_steps[0];
+    gridsearch_min_steps[1] = s.gridsearch_min_steps[1];
+    gridsearch_min_steps[2] = s.gridsearch_min_steps[2];
     /* Landmarks */
     landmark_stiffness = s.landmark_stiffness;
     landmark_flavor = s.landmark_flavor;
diff --git a/src/plastimatch/register/stage_parms.h b/src/plastimatch/register/stage_parms.h
index 4504034..a74d6b1 100644
--- a/src/plastimatch/register/stage_parms.h
+++ b/src/plastimatch/register/stage_parms.h
@@ -26,6 +26,7 @@ enum Stage_transform_type {
     STAGE_TRANSFORM_VERSOR,
     STAGE_TRANSFORM_QUATERNION,
     STAGE_TRANSFORM_AFFINE,
+    STAGE_TRANSFORM_SIMILARITY,
     STAGE_TRANSFORM_BSPLINE,
     STAGE_TRANSFORM_VECTOR_FIELD
 };
@@ -125,6 +126,7 @@ public:
     int optim_subtype;       /* used for demons types (diffeomorphic, etc.) */
     char alg_flavor;
     Threading threading_type;
+    int gpuid;               /* Sets GPU to use for multi-gpu machines */
     /* Similarity metric */
     std::vector<Registration_metric_type> metric_type;
     std::vector<float> metric_lambda;
@@ -153,6 +155,8 @@ public:
     float min_step;
     float rsg_grad_tol;
     float translation_scale_factor;
+    int rotation_scale_factor;
+    float scaling_scale_factor;
     /*OnePlusOne evvolutionary optimizer*/
     float opo_epsilon;
     float opo_initial_search_rad;
@@ -196,6 +200,7 @@ public:
     float gridsearch_min_overlap[3];
     Gridsearch_step_size_type gridsearch_step_size_type;
     float gridsearch_step_size[3];
+    int gridsearch_min_steps[3];
     /* Landmarks */
     float landmark_stiffness; //strength of attraction between landmarks
     char landmark_flavor;
diff --git a/src/plastimatch/register/translation_grid_search.cxx b/src/plastimatch/register/translation_grid_search.cxx
index c0ee2fe..536411c 100644
--- a/src/plastimatch/register/translation_grid_search.cxx
+++ b/src/plastimatch/register/translation_grid_search.cxx
@@ -112,6 +112,9 @@ translation_grid_search (
         for (int d = 0; d < 3; d++) {
             float search_range = search_max[d] - search_min[d];
             num_steps[d] = ROUND_INT (search_range / nominal_step) + 1;
+            if (num_steps[d] < stage->gridsearch_min_steps[d]) {
+                num_steps[d] = stage->gridsearch_min_steps[d];
+            }
             if (num_steps[d] > 1) {
                 search_step[d] = search_range / (num_steps[d] - 1);
             }
diff --git a/src/plastimatch/segment/CMakeLists.txt b/src/plastimatch/segment/CMakeLists.txt
index dae15be..c880bea 100644
--- a/src/plastimatch/segment/CMakeLists.txt
+++ b/src/plastimatch/segment/CMakeLists.txt
@@ -58,4 +58,5 @@ plm_add_library (
   "${PLMSEGMENT_LIBRARY_SRC}" 
   "${PLMSEGMENT_LIBRARY_DEPENDENCIES}"
   "${PLMSEGMENT_LIBRARY_LDFLAGS}"
+  "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
   "")
diff --git a/src/plastimatch/segment/autolabel.cxx b/src/plastimatch/segment/autolabel.cxx
index 2d3cb1f..260595c 100644
--- a/src/plastimatch/segment/autolabel.cxx
+++ b/src/plastimatch/segment/autolabel.cxx
@@ -60,10 +60,10 @@ autolabel_la1 (Autolabel_parms *parms)
     float best_score = FLT_MAX;
     float best_slice = 0.f;
     UNUSED_VARIABLE (best_slice);
-    for (int i = 0; i < pih.Size(2); i++) {
+    for (int i = 0; i < pih.dim(2); i++) {
 
         /* Create slice thumbnail and dlib sample */
-        float loc = pih.m_origin[2] + i * pih.m_spacing[2];
+        float loc = pih.origin(2) + i * pih.spacing(2);
         Dlib_trainer::Dense_sample_type d = thumb.make_sample (loc);
 
         /* Predict the value */
@@ -109,10 +109,10 @@ autolabel_tsv1 (Autolabel_parms *parms)
 
     /* Loop through slices, and predict location for each slice */
     Plm_image_header pih (thumb.pli);
-    for (int i = 0; i < pih.Size(2); i++) {
+    for (int i = 0; i < pih.dim(2); i++) {
 
         /* Create slice thumbnail and dlib sample */
-        float loc = pih.m_origin[2] + i * pih.m_spacing[2];
+        float loc = pih.origin(2) + i * pih.spacing(2);
         Dlib_trainer::Dense_sample_type d = thumb.make_sample (loc);
 
         /* Predict the value */
@@ -157,10 +157,10 @@ autolabel_tsv2 (Autolabel_parms *parms)
 
     /* Loop through slices, and predict location for each slice */
     Plm_image_header pih (thumb.pli);
-    for (int i = 0; i < pih.Size(2); i++) {
+    for (int i = 0; i < pih.dim(2); i++) {
 
         /* Create slice thumbnail and dlib sample */
-        float loc = pih.m_origin[2] + i * pih.m_spacing[2];
+        float loc = pih.origin(2) + i * pih.spacing(2);
         Dlib_trainer::Dense_sample_type d = thumb.make_sample (loc);
 
         /* Predict the value */
diff --git a/src/plastimatch/segment/mabs.cxx b/src/plastimatch/segment/mabs.cxx
index fc56a11..666c47d 100755
--- a/src/plastimatch/segment/mabs.cxx
+++ b/src/plastimatch/segment/mabs.cxx
@@ -45,6 +45,9 @@ class Mabs_private {
 public:
     /* These are the input parameters */
     const Mabs_parms *parms;
+ 
+    /* Store the executed command */
+    std::string executed_command;
     
     /* traindir_base is the output directory when we are 
        doing a training task (i.e. not labeling), and is of the form:
@@ -73,8 +76,11 @@ public:
     /* output_dir is ??? */
     std::string output_dir;
 
-    /* input_roi_fn is the binary structure file name used to align the center of gravity*/
-    std::string input_roi_fn;
+    /* Binary structure used to align the center of gravity */
+    Plm_image::Pointer input_roi_for_cog_prealignment;
+
+    /* String containing the roi file name passed by command line */
+    std::string prealign_roi_cmd_name;
     
     /* process_dir_list is a list of the input directories which 
        need to be processed, one directory per case
@@ -587,6 +593,49 @@ Mabs::run_registration_loop ()
             moving_image->set_itk (
                 rtds.get_image()->itk_float());
             reg.set_moving_image (moving_image);
+            
+            /* PAOLO ZAFFINO: align centers of gravity */
+            if (d_ptr->input_roi_for_cog_prealignment != NULL) {
+                
+                /* Add STAGE only if a segment command is executed. Is it needed or we can define it into the configuration file? */
+                std::string command_string_plus_cog = "[STAGE]\nxform=align_center_of_gravity\n";
+                command_string_plus_cog.append(command_string);
+                int rc_cog = reg.set_command_string (command_string_plus_cog);
+                if (rc != PLM_SUCCESS) {
+                    lprintf ("Skipping centers of gravity prealignment addition to command file \"%s\" \n", command_file.c_str());
+                    continue;
+                }
+             
+                /* Set fixed ROI */
+                reg.set_fixed_roi(d_ptr->input_roi_for_cog_prealignment); 
+               
+                /* Set moving ROI*/ 
+                std::string target_roi_name;
+                if (d_ptr->executed_command == "segment") {
+                    target_roi_name = strip_extension(basename(d_ptr->prealign_roi_cmd_name));
+                }
+                else if (d_ptr->executed_command == "prealign") {
+                    target_roi_name = d_ptr->parms->prealign_roi_cfg_name;
+                }
+
+                size_t target_roi_index = -1;
+                for (size_t i = 0; i < rtss->get_num_structures(); i++) {
+                    std::string struct_name = rtss->get_structure_name (i);
+
+                    if (struct_name == target_roi_name) {
+                        target_roi_index = i;
+                        break;
+                    }
+                }
+                if (target_roi_index != -1) {
+                    Plm_image::Pointer moving_roi = Plm_image::New ();
+                    moving_roi->set_itk (rtss->get_structure_image (target_roi_index));
+                    reg.set_moving_roi(moving_roi);
+                }
+                else if (target_roi_index == -1) {
+                    lprintf("No moving ROI set!\n");
+                }
+            }
 
             /* Run the registration */
             lprintf ("DO_REGISTRATION_PURE\n");
@@ -852,7 +901,7 @@ Mabs::atlas_selection ()
     atlas_selector->subject_id = d_ptr->segment_input_fn.c_str();
     atlas_selector->atlas_dir = d_ptr->parms->atlas_dir;
     atlas_selector->number_of_atlases = (int) d_ptr->process_dir_list.size();
-        
+    
     if (d_ptr->parms->roi_mask_fn != "") { /* Set the mask if defined */
         Plm_image::Pointer mask_plm = plm_image_load (d_ptr->parms->roi_mask_fn, PLM_IMG_TYPE_ITK_UCHAR);
             
@@ -873,8 +922,84 @@ Mabs::atlas_selection ()
         
     /* New selection is required, execute it */
     if (compute_new_ranking) {
+        
         atlas_selector->subject = plm_image_load_native(atlas_selector->subject_id);
         atlas_selector->atlas_dir_list = d_ptr->process_dir_list;
+
+        // if set, before the selection, prealign the images (using COG or whatever else)
+        if (d_ptr->parms->prealign_mode == "custom") {
+            
+            Registration reg; 
+            Registration_parms::Pointer regp = reg.get_registration_parms ();
+            Registration_data::Pointer regd = reg.get_registration_data ();
+            
+            /* Parse the registration command string */
+            std::string command_string = slurp_file (d_ptr->parms->prealign_registration_config);
+            int rc = reg.set_command_string (command_string);
+            if (rc != PLM_SUCCESS) {
+                lprintf ("Skipping command file \"%s\" "
+                        "due to parse error.\n", d_ptr->parms->prealign_registration_config.c_str());
+            }
+            
+            /* Set input images */
+            std::string fixed_image_fn;
+            fixed_image_fn = string_format ("%s/%s/img.nrrd",
+                    d_ptr->convert_dir.c_str(),
+                    d_ptr->parms->prealign_reference.c_str());
+            Plm_image::Pointer fixed_image = Plm_image::New (fixed_image_fn);
+            reg.set_fixed_image (fixed_image);
+            
+            Plm_image::Pointer moving_image = Plm_image::New ();
+            moving_image->set_itk (atlas_selector->subject->itk_float());
+            reg.set_moving_image (moving_image);
+            
+            /* Align centers of gravity */
+            if (d_ptr->prealign_roi_cmd_name != "") {
+
+                /* Add STAGE for COG */
+                std::string command_string_plus_cog = "[STAGE]\nxform=align_center_of_gravity\n";
+                command_string_plus_cog.append(command_string);
+                int rc_cog = reg.set_command_string (command_string_plus_cog);
+                if (rc != PLM_SUCCESS) {
+                    lprintf ("Skipping centers of gravity prealignment addition to command file \"%s\" \n", d_ptr->parms->prealign_registration_config.c_str());
+                }
+            
+                /* Set moving ROI */
+                d_ptr->input_roi_for_cog_prealignment = Plm_image::New (d_ptr->prealign_roi_cmd_name);    
+                reg.set_moving_roi(d_ptr->input_roi_for_cog_prealignment);
+            
+                /* Set fixed ROI*/
+                std::string fixed_roi_fn;
+                fixed_roi_fn = string_format ("%s/%s/structures/%s.nrrd",
+                    d_ptr->convert_dir.c_str(),
+                    d_ptr->parms->prealign_reference.c_str(), 
+                    d_ptr->parms->prealign_roi_cfg_name.c_str());
+
+                Plm_image::Pointer fixed_roi = Plm_image::New (fixed_roi_fn);
+                reg.set_fixed_roi(fixed_roi);
+            }
+        
+            /* Run the registration */
+            lprintf ("DO_REGISTRATION_PURE\n");
+            lprintf ("regp->num_stages = %d\n", regp->num_stages);
+            timer.start();
+            Xform::Pointer xf_out = reg.do_registration_pure ();
+            d_ptr->time_reg += timer.report();
+        
+            /* Warp the image */
+            lprintf ("Prealign input image...\n");
+            Plm_image_header fixed_pih (regd->fixed_image);
+            Plm_image::Pointer warped_image = Plm_image::New();
+            timer.start();
+            plm_warp (warped_image, 0, xf_out, &fixed_pih,
+                regd->moving_image,
+                regp->default_value, 0, 1);
+            d_ptr->time_warp_img += timer.report();
+
+            atlas_selector->subject = warped_image; 
+
+        }
+        
         atlas_selector->run_selection();
     }
 
@@ -1161,10 +1286,10 @@ Mabs::atlas_prealign ()
 
     /* Parse directory with registration files */
     if (d_ptr->parms->prealign_mode == "disabled") {
-        print_and_exit ("Prealignment not enabled in parameters file!");
+        print_and_exit ("Prealignment not enabled in parameters file!\n");
     }
     else if (d_ptr->parms->prealign_mode == "default") {
-        print_and_exit ("No default prealignment implemented yet!");
+        print_and_exit ("No default prealignment implemented yet!\n");
     }
     else if (d_ptr->parms->prealign_mode == "custom") {
         this->parse_registration_dir (d_ptr->parms->prealign_registration_config);
@@ -1174,7 +1299,7 @@ Mabs::atlas_prealign ()
     this->load_process_dir_list (d_ptr->convert_dir);
     if (d_ptr->process_dir_list.size() < 2) {
         print_and_exit ("Error.  Prealignment requires at least two "
-            "images in the convert directory.");
+            "images in the convert directory.\n");
     }
 
     /* Identify directory of reference image */
@@ -1187,7 +1312,7 @@ Mabs::atlas_prealign ()
             d_ptr->parms->prealign_reference.c_str());
         if (!is_directory (reference_dir)) {
             print_and_exit ("Error.  Prealignment reference directory (%s) "
-                " was not found.", reference_dir.c_str());
+                " was not found.\n", reference_dir.c_str());
         }
     } else {
         reference_dir = d_ptr->process_dir_list.front();
@@ -1217,6 +1342,27 @@ Mabs::atlas_prealign ()
         ref_rtds->resample (d_ptr->prealign_spacing);
     }
     Plm_image::Pointer reference_image = ref_rtds->get_image ();
+    
+    /* PAOLO ZAFFINO
+     * set fixed ROI if defined into the prealign section */ 
+    if (d_ptr->parms->prealign_roi_cfg_name != "") {
+        Segmentation::Pointer fixed_rtss = ref_rtds->get_rtss();
+        size_t target_roi_index = -1;
+        for (size_t i = 0; i < fixed_rtss->get_num_structures(); i++) {
+            std::string struct_name = fixed_rtss->get_structure_name (i);
+            if (struct_name == d_ptr->parms->prealign_roi_cfg_name) {
+                target_roi_index = i;
+                break;
+            }
+        }
+        if (target_roi_index != -1) {
+            d_ptr->input_roi_for_cog_prealignment = Plm_image::New (fixed_rtss->get_structure_image (target_roi_index));
+        }
+        else if (target_roi_index == -1) {
+            lprintf("No fixed ROI set!\n");
+        }
+    }
+
     reference_image->save_image (reference_prealign_img_fn);
     ref_rtds->save_prefix (reference_prealign_structures_dir, "nrrd");
 
@@ -1314,6 +1460,14 @@ Mabs::parse_registration_dir (const std::string& registration_config)
     }
 }
 
+
+void 
+Mabs::set_executed_command (const std::string& executed_command)
+{
+    d_ptr->executed_command = executed_command;
+}
+
+
 FloatImageType::Pointer
 Mabs::compute_dmap (
     UCharImageType::Pointer& structure_image,
@@ -1359,6 +1513,123 @@ Mabs::compute_dmap (
     return dmap_image;
 }
 
+
+void
+Mabs::no_voting (
+    const std::string& atlas_id,
+    const std::string& label_output_dir
+)
+{
+    Plm_timer timer;
+   
+    /* Set up files & directories for this job */
+    std::string atlas_input_path;
+    atlas_input_path = string_format ("%s/%s",
+        d_ptr->preprocessed_dir.c_str(), atlas_id.c_str());
+    lprintf ("atlas_input_path: %s\n",
+        atlas_input_path.c_str());
+    std::string atlas_output_path;
+    atlas_output_path = string_format ("%s/%s",
+        d_ptr->output_dir.c_str(), atlas_id.c_str());
+    lprintf ("atlas_output_path: %s\n",
+        atlas_output_path.c_str());
+    std::string curr_output_dir;
+    curr_output_dir = string_format ("%s/%s",
+        atlas_output_path.c_str(),
+        d_ptr->registration_id.c_str());
+    lprintf ("curr_output_dir: %s\n", curr_output_dir.c_str());
+
+    /* Load xform */
+    timer.start();
+    std::string xf_fn = string_format ("%s/%s",
+        curr_output_dir.c_str(),
+        "xf.txt");
+    lprintf ("Loading xform: %s\n", xf_fn.c_str());
+    Xform::Pointer xf = xform_load (xf_fn);
+    d_ptr->time_io += timer.report();
+
+    /* Loop through structures for this atlas image */
+    std::set<std::string>::const_iterator it;
+    for (it = d_ptr->parms->structure_set.begin ();
+         it != d_ptr->parms->structure_set.end (); it++)
+    {
+        const std::string& mapped_name = *it;
+        lprintf ("Segmenting structure: %s\n", mapped_name.c_str());
+
+        /* Load original structure */
+        timer.start();
+        std::string atlas_struct_fn;
+        atlas_struct_fn = string_format ("%s/structures/%s.nrrd", 
+                atlas_input_path.c_str(), mapped_name.c_str());
+        Plm_image::Pointer atlas_struct = 
+                plm_image_load_native (atlas_struct_fn);
+        d_ptr->time_io += timer.report();
+        if (!atlas_struct) {
+                lprintf ("Atlas %s doesn't have structure %s\n",
+                    atlas_id.c_str(), mapped_name.c_str());
+                continue;
+        }
+
+        /* Warp structure */
+        timer.start();
+        Plm_image::Pointer warped_structure = Plm_image::New();
+        Plm_image_header fixed_pih (d_ptr->ref_rtds->get_image());
+        lprintf ("Warping atlas structure.\n");
+        plm_warp (warped_structure, 0, xf, 
+                &fixed_pih, 
+                atlas_struct,
+                0, 0, 0);
+        d_ptr->time_warp_str += timer.report();
+        
+        /* Save warped structure */        
+        std::string final_segmentation_img_fn = string_format (
+            "%s/%s_novoting.nrrd", label_output_dir.c_str(), 
+            mapped_name.c_str());
+
+        itk_image_save (warped_structure->itk_uchar(),
+            final_segmentation_img_fn.c_str());
+        
+        /* If required compute statistics */
+        std::string atl_name = basename (d_ptr->output_dir);
+        std::string ref_stru_fn = string_format ("%s/%s/structures/%s.nrrd",
+            d_ptr->preprocessed_dir.c_str(), atl_name.c_str(),
+            mapped_name.c_str());
+
+        Plm_image::Pointer ref_stru = 
+            plm_image_load_native (ref_stru_fn);
+
+        if (!ref_stru) {
+            /* User is not running train, so no statistics */
+            continue;
+        }
+       
+        
+        /* Compute Dice, etc. */
+        std::string stats_string = d_ptr->stats.compute_statistics (
+            "segmentation", /* Not used yet */
+            ref_stru->itk_uchar(),
+            warped_structure->itk_uchar());
+        std::string seg_log_string = string_format (
+            "target=%s,reg=%s,struct=%s,"
+            "%s\n",
+            d_ptr->ref_id.c_str(),
+            d_ptr->registration_id.c_str(),
+            mapped_name.c_str(),
+            stats_string.c_str());
+        lprintf ("%s", seg_log_string.c_str());
+
+        /* Update seg_dice file */
+        std::string seg_dice_log_fn = string_format (
+            "%s/seg_dice.csv",
+            d_ptr->mabs_train_dir.c_str());
+        FILE *fp = fopen (seg_dice_log_fn.c_str(), "a");
+        fprintf (fp, "%s", seg_log_string.c_str());
+        fclose (fp);
+    
+    }
+        
+}
+
 void
 Mabs::gaussian_segmentation_vote (
     const std::string& atlas_id,
@@ -1671,16 +1942,21 @@ Mabs::staple_segmentation_label (
              = d_ptr->staple_map.begin(); 
          staple_it != d_ptr->staple_map.end(); staple_it++)
     {
+       
         const std::string& mapped_name = staple_it->first;
         std::string atl_name = basename (d_ptr->output_dir);
 
+        /* Find fusion weights for this structure */
+        const Mabs_seg_weights* msw = seg_weights.find (mapped_name);
+
         std::string ref_stru_fn = string_format ("%s/%s/structures/%s.nrrd",
             d_ptr->preprocessed_dir.c_str(), atl_name.c_str(),
             mapped_name.c_str());
 
         std::string final_segmentation_img_fn = string_format (
-            "%s/%s_staple.nrrd", label_output_dir.c_str(), 
-            mapped_name.c_str());
+            "%s/%s_staple_%.9f.nrrd", label_output_dir.c_str(), 
+            mapped_name.c_str(),
+            msw->confidence_weight);
 
         printf("Structure %s \n", final_segmentation_img_fn.c_str());
         staple_it->second->run();
@@ -1696,9 +1972,6 @@ Mabs::staple_segmentation_label (
             continue;
         }
        
-        /* Find fusion weights for this structure */
-        const Mabs_seg_weights* msw = seg_weights.find (mapped_name);
-        
         /* Compute Dice, etc. */
         std::string stats_string = d_ptr->stats.compute_statistics (
             "segmentation", /* Not used yet */
@@ -1730,6 +2003,19 @@ Mabs::staple_segmentation_label (
 void
 Mabs::run_segmentation (const Mabs_seg_weights_list& seg_weights)
 {
+    
+    /* Just one atlas and no voting option selected */
+    if (d_ptr->parms->fusion_criteria == "none" && d_ptr->parms->atlases_from_ranking == 1) {
+
+        std::string atlas_id = basename (*d_ptr->atlas_list.begin());
+        std::string label_output_dir =
+            string_format ("%s/segmentations", d_ptr->output_dir.c_str());
+
+        no_voting(atlas_id, label_output_dir);
+
+        return;
+    }
+
     /* Clear out internal structures */
     d_ptr->clear_vote_map ();
     d_ptr->clear_staple_map ();
@@ -1783,6 +2069,18 @@ Mabs::run_segmentation (const Mabs_seg_weights_list& seg_weights)
 void
 Mabs::run_segmentation_train (const Mabs_seg_weights& msw)
 {
+    /* Just one atlas and no voting option selected */
+    if (d_ptr->parms->fusion_criteria == "none" && d_ptr->parms->atlases_from_ranking == 1) {
+
+        std::string atlas_id = basename (*d_ptr->atlas_list.begin());
+        std::string label_output_dir =
+            string_format ("%s/segmentations", d_ptr->output_dir.c_str());
+
+        no_voting(atlas_id, label_output_dir);
+
+        return;
+    }
+
     /* Clear out internal structures */
     d_ptr->clear_vote_map ();
     d_ptr->clear_staple_map ();
@@ -2021,9 +2319,9 @@ Mabs::set_segment_output_dicom (const std::string& output_dicom_dir)
 }
 
 void 
-Mabs::set_segment_input_roi (const std::string& input_roi_fn)
+Mabs::set_prealign_roi_cmd_name (const std::string& input_roi_for_cog_prealignment_fn)
 {
-    d_ptr->input_roi_fn = input_roi_fn;
+    d_ptr->prealign_roi_cmd_name = input_roi_for_cog_prealignment_fn;
 }
 
 void
@@ -2057,7 +2355,7 @@ Mabs::train_internal ()
 
     /* Parse atlas directory */
     this->load_process_dir_list (d_ptr->preprocessed_dir);
- 
+
     /* If set, run train atlas selection */
     if (d_ptr->parms->enable_atlas_selection)
     {
@@ -2215,6 +2513,12 @@ Mabs::segment ()
 
     /* Run the registrations */
     d_ptr->write_warped_images = true;
+    
+    /* PAOLO ZAFFINO: if an input ROI has been passed, prealign the centers of gravity before registration */
+    if (d_ptr->prealign_roi_cmd_name != "" ) {
+        d_ptr->input_roi_for_cog_prealignment = Plm_image::New (d_ptr->prealign_roi_cmd_name);
+    }
+    
     this->run_registration_loop ();
 
     /* Run the segmentation */
diff --git a/src/plastimatch/segment/mabs.h b/src/plastimatch/segment/mabs.h
index c32d927..6d831cf 100755
--- a/src/plastimatch/segment/mabs.h
+++ b/src/plastimatch/segment/mabs.h
@@ -36,6 +36,11 @@ protected:
     void run_segmentation (const Mabs_seg_weights_list& seg_weights);
     void run_segmentation_train (const Mabs_seg_weights& seg_weights);
     void run_segmentation_train_loop ();
+    
+    void no_voting (
+        const std::string& atlas_id,
+        const std::string& label_output_dir);
+
     void gaussian_segmentation_vote (
         const std::string& atlas_id,
         const Mabs_seg_weights_list& seg_weights
@@ -57,11 +62,13 @@ protected:
 public:
     void set_parms (const Mabs_parms *parms);
     void parse_registration_dir (const std::string& registration_config);
+    
+    void set_executed_command (const std::string& executed_command);
 
     void set_segment_input (const std::string& input_fn);
     void set_segment_output (const std::string& output_dir);
     void set_segment_output_dicom (const std::string& output_dicom_dir);
-    void set_segment_input_roi (const std::string& input_roi_fn);
+    void set_prealign_roi_cmd_name (const std::string& input_roi_fn);
 
     void atlas_selection ();
     void train_atlas_selection ();
diff --git a/src/plastimatch/segment/mabs_atlas_selection.cxx b/src/plastimatch/segment/mabs_atlas_selection.cxx
index c607fd4..a35765c 100644
--- a/src/plastimatch/segment/mabs_atlas_selection.cxx
+++ b/src/plastimatch/segment/mabs_atlas_selection.cxx
@@ -358,7 +358,7 @@ Mabs_atlas_selection::compute_nmi (
         this->min_hist_atl_value_defined)
     {
         NmiMetricType::MeasurementVectorType lower_bounds;
-        #ifdef ITK4 
+        #if ITK_VERSION_MAJOR >= 4 
         lower_bounds.SetSize(2);
         #endif
         lower_bounds.SetElement(0, this->min_hist_sub_value);
@@ -370,7 +370,7 @@ Mabs_atlas_selection::compute_nmi (
         this->max_hist_atl_value_defined)
     {
         NmiMetricType::MeasurementVectorType upper_bounds;
-        #ifdef ITK4 
+        #if ITK_VERSION_MAJOR >= 4
         upper_bounds.SetSize(2);
         #endif
         upper_bounds.SetElement(0, this->max_hist_sub_value);
@@ -381,7 +381,7 @@ Mabs_atlas_selection::compute_nmi (
     /* Metric settings */
     unsigned int numberOfHistogramBins = this->hist_bins;
     NmiMetricType::HistogramType::SizeType histogramSize;
-    #ifdef ITK4
+    #if ITK_VERSION_MAJOR >= 4
     histogramSize.SetSize(2);
     #endif
     histogramSize.Fill(numberOfHistogramBins);
diff --git a/src/plastimatch/segment/mabs_parms.cxx b/src/plastimatch/segment/mabs_parms.cxx
index 4c9def3..b3dbbfc 100644
--- a/src/plastimatch/segment/mabs_parms.cxx
+++ b/src/plastimatch/segment/mabs_parms.cxx
@@ -125,6 +125,9 @@ Mabs_parms_parser::set_key_value (
         else if (key == "registration_config") {
             mp->prealign_registration_config = val;
         }
+        else if (key == "prealign_struct") {
+            mp->prealign_roi_cfg_name = val;
+        }
         else {
             goto error_exit;
         }
@@ -232,11 +235,13 @@ Mabs_parms_parser::set_key_value (
             else if (val == "staple" || val == "STAPLE" || val == "Staple") {
                 mp->fusion_criteria = "staple";
             }
-
             else if (val == "gaussian,staple" || val == "GAUSSIAN,STAPLE" || val == "Gaussian,Staple" ||
-                val == "staple,gaussian" || val == "STAPLE,GAUSSIAN" || val == "Staple,Gaussian") {
+                     val == "staple,gaussian" || val == "STAPLE,GAUSSIAN" || val == "Staple,Gaussian") {
                 mp->fusion_criteria = "gaussian_and_staple";
             }
+            else if (val == "none" || val == "NONE" || val == "None") {
+                mp->fusion_criteria = "none";
+            }
         }
         else if (key == "distance_map_algorithm") {
             mp->distance_map_algorithm = val;
@@ -372,6 +377,7 @@ Mabs_parms::Mabs_parms ()
     this->prealign_reference = "";
     this->prealign_spacing = "";
     this->prealign_registration_config = "";
+    this->prealign_roi_cfg_name = "";
 
     /* [ATLAS-SELECTION] */
     this->enable_atlas_selection = false;
diff --git a/src/plastimatch/segment/mabs_parms.h b/src/plastimatch/segment/mabs_parms.h
index f6a09c5..8c0ccd9 100644
--- a/src/plastimatch/segment/mabs_parms.h
+++ b/src/plastimatch/segment/mabs_parms.h
@@ -30,6 +30,7 @@ public:
     std::string prealign_reference;
     std::string prealign_spacing;
     std::string prealign_registration_config;
+    std::string prealign_roi_cfg_name;
     
     /* [ATLASES-SELECTION] */
     bool enable_atlas_selection;
diff --git a/src/plastimatch/segment/plmsegment_config.h.in b/src/plastimatch/segment/plmsegment_config.h.in
index 4464437..8e95548 100644
--- a/src/plastimatch/segment/plmsegment_config.h.in
+++ b/src/plastimatch/segment/plmsegment_config.h.in
@@ -19,8 +19,4 @@
 # define PLMSEGMENT_API 
 #endif
 
-#if (ITK_VERSION_MAJOR == 4)
-#define ITK4
-#endif
-
 #endif
diff --git a/src/plastimatch/segment/segment_body.cxx b/src/plastimatch/segment/segment_body.cxx
index b726a94..81011f3 100644
--- a/src/plastimatch/segment/segment_body.cxx
+++ b/src/plastimatch/segment/segment_body.cxx
@@ -25,6 +25,40 @@ const short T1 = -300;
 const short T2 = -500;
 const short T3 = -1000;
 
+/* Reduce resolution to at most 5 mm voxels if "m_fast" option selected */
+FloatImageType::Pointer
+Segment_body::reduce_image_dim (FloatImageType::Pointer i1)
+{
+    if (m_fast) {
+        Plm_image_header pih;
+        bool need_resample = false;
+        pih.set_from_plm_image (this->img_in);
+        RegionType itk_region = pih.GetRegion();
+        SizeType itk_dim = itk_region.GetSize();
+        OriginType itk_origin = pih.GetOrigin();
+        SpacingType itk_spacing = pih.GetSpacing();
+        DirectionType itk_direction = pih.GetDirection();
+        for (int d = 0; d < 3; d++) {
+            if (itk_spacing[d] < 5.0) {
+                itk_dim[d] = (int) floor(itk_dim[d] * itk_spacing[d] / 5.0);
+                itk_origin[d] += (5.0 - itk_spacing[d]) / 2;
+                itk_spacing[d] = 5.0;
+                need_resample = true;
+            }
+        }
+        if (need_resample) {
+            printf ("Resampling image\n");
+            itk_region.SetSize (itk_dim);
+            pih.set (itk_region, itk_origin, itk_spacing, itk_direction);
+            i1 = resample_image (i1, &pih, -1000, 1);
+            if (m_debug) {
+                itk_image_save (i1, "0_resample.nrrd");
+            }
+        }
+    }
+    return i1;
+}
+
 /* Return bottom image row (indexed starting at 0) containing a patient pixel */
 int
 Segment_body::find_patient_bottom (FloatImageType::Pointer i1)
@@ -290,30 +324,7 @@ Segment_body::do_segmentation ()
     FloatImageType::Pointer i1 = this->img_in->itk_float();
 
     /* Reduce resolution to at most 5 mm voxels */
-    if (m_fast) {
-        Plm_image_header pih;
-        bool need_resample = false;
-        ImageRegionType::SizeType itk_size;
-        pih.set_from_plm_image (this->img_in);
-        for (int d = 0; d < 3; d++) {
-            if (pih.m_spacing[d] < 5.0) {
-                itk_size[d] = (int) floor(pih.Size(d) * pih.m_spacing[d] / 5.0);
-                pih.m_origin[d] += (5.0 - pih.m_spacing[d]) / 2;
-                pih.m_spacing[d] = 5.0;
-                need_resample = true;
-            } else {
-                itk_size[d] = pih.Size(d);
-            }
-        }
-        if (need_resample) {
-            printf ("Resampling image\n");
-            pih.m_region.SetSize (itk_size);
-            i1 = resample_image (i1, &pih, -1000, 1);
-            if (m_debug) {
-                itk_image_save (i1, "0_resample.nrrd");
-            }
-        }
-    }
+    i1 = reduce_image_dim (i1);
 
     /* Allocate output image (i2) */
     UCharImageType::Pointer i2 = UCharImageType::New ();
@@ -394,109 +405,85 @@ Segment_body::do_segmentation ()
 void
 Segment_body::do_segmentation_air_cavity()//for air cavity filling in CT/CBCT images
 {
-	/* Convert input to float */
-	FloatImageType::Pointer i1 = this->img_in->itk_float();
-
-	/* Reduce resolution to at most 5 mm voxels */
-	if (m_fast) {
-		Plm_image_header pih;
-		bool need_resample = false;
-		ImageRegionType::SizeType itk_size;
-		pih.set_from_plm_image(this->img_in);
-		for (int d = 0; d < 3; d++) {
-			if (pih.m_spacing[d] < 5.0) {
-				itk_size[d] = (int)floor(pih.Size(d) * pih.m_spacing[d] / 5.0);
-				pih.m_origin[d] += (5.0 - pih.m_spacing[d]) / 2;
-				pih.m_spacing[d] = 5.0;
-				need_resample = true;
-			}
-			else {
-				itk_size[d] = pih.Size(d);
-			}
-		}
-		if (need_resample) {
-			printf("Resampling image\n");
-			pih.m_region.SetSize(itk_size);
-			i1 = resample_image(i1, &pih, -1000, 1);
-			if (m_debug) {
-				itk_image_save(i1, "0_resample.nrrd");
-			}
-		}
-	}
-
-	/* Allocate output image (i2) */
-	UCharImageType::Pointer i2 = UCharImageType::New();
-
-	/* Find patient */
-	int patient_bottom;
-	if (this->m_bot_given) {
-		patient_bottom = this->m_bot;
-	}
-	else {
-		printf("find_patient_bottom\n");
-		patient_bottom = find_patient_bottom(i1);
-	}
-
-	/* Threshold image */
-	printf("threshold\n");
-	i2 = this->threshold_patient(i1);
-
-	/* Zero out the couch */
-	//YKTEMP
-	/*  printf ("remove_couch\n");
+    /* Convert input to float */
+    FloatImageType::Pointer i1 = this->img_in->itk_float();
+
+    /* Reduce resolution to at most 5 mm voxels */
+    i1 = reduce_image_dim (i1);
+
+    /* Allocate output image (i2) */
+    UCharImageType::Pointer i2 = UCharImageType::New();
+
+    /* Find patient */
+    int patient_bottom;
+    if (this->m_bot_given) {
+        patient_bottom = this->m_bot;
+    }
+    else {
+        printf("find_patient_bottom\n");
+        patient_bottom = find_patient_bottom(i1);
+    }
+
+    /* Threshold image */
+    printf("threshold\n");
+    i2 = this->threshold_patient(i1);
+
+    /* Zero out the couch */
+    //YKTEMP
+    /*  printf ("remove_couch\n");
 	remove_couch (i2, patient_bottom);
 	if (m_debug) {
 	itk_image_save (i2, "1_remove_couch.nrrd");
 	}*/
 
-	/* Erode and dilate */ //YKTEMP
-	printf("erode_and_dilate\n");
-	i2 = erode_and_dilate(i2);
+    /* Erode and dilate */ //YKTEMP
+    printf("erode_and_dilate\n");
+    i2 = erode_and_dilate(i2);
 
-	/* Compute connected components */ //YKTEMP
-	//printf ("get_largest_connected_component\n");
-	//i2 = get_largest_connected_component (i2);
+    /* Compute connected components */ //YKTEMP
+    //printf ("get_largest_connected_component\n");
+    //i2 = get_largest_connected_component (i2);
 
-	/* Invert the image *///YKTEMP
-	/*printf ("invert\n");
-	invert_image (i2);*/
-	if (m_debug) {
-		itk_image_save(i2, "2_largest_cc.nrrd");
-	}
+    /* Invert the image *///YKTEMP
+    /*printf ("invert\n");
+      invert_image (i2);*/
+    if (m_debug) {
+        itk_image_save(i2, "2_largest_cc.nrrd");
+    }
 
-	/* Fill holes: Redo connected components on the (formerly) black parts *///YKTEMP
-	//printf ("get_largest_connected_component\n");
-	//i2 = get_largest_connected_component (i2);
+    /* Fill holes: Redo connected components on the (formerly) black parts *///YKTEMP
+    //printf ("get_largest_connected_component\n");
+    //i2 = get_largest_connected_component (i2);
 
-	if (m_debug) {
-		itk_image_save(i2, "3_re_invert.nrrd");
-	}
-
-	/* Fill holes */
-	/* PAOLO ZAFFINO 9-4-13
-	* Often the mask has some big holes inside (for example the structures that contain air).
-	* Try to fill that holes.
-	* This step can be very slow but anyway it seems to fix the issue. */
-	if (m_fill_holes) {
-		printf("fill holes \n");
-		printf("fill parameters: \n");
-		printf("radius1 = %d, radius2 = %d, radius3 = %d \n",
-			m_fill_parms[0], m_fill_parms[1], m_fill_parms[2]);
-		printf("iterations1 = %d, iterations2 = %d, iterations3 = %d \n",
-			m_fill_parms[3], m_fill_parms[4], m_fill_parms[5]);
-		i2 = fill_holes(i2, m_fill_parms[0], m_fill_parms[3]);
-		i2 = fill_holes(i2, m_fill_parms[1], m_fill_parms[4]);
-		i2 = fill_holes(i2, m_fill_parms[2], m_fill_parms[5]);
-		if (m_debug) {
-			itk_image_save(i2, "4_filled.nrrd");
-		}
-	}
-
-	/* Invert the image */
-	printf("invert\n");
-	invert_image(i2);
-
-	/* Return image to caller */
-	printf("return\n");
-	this->img_out->set_itk(i2);
+    if (m_debug) {
+        itk_image_save(i2, "3_re_invert.nrrd");
+    }
+
+    /* Fill holes */
+    /* PAOLO ZAFFINO 9-4-13
+     * Often the mask has some big holes inside (for example the structures that contain air).
+     * Try to fill that holes.
+     * This step can be very slow but anyway it seems to fix the issue. */
+    if (m_fill_holes) {
+        printf("fill holes \n");
+        printf("fill parameters: \n");
+        printf("radius1 = %d, radius2 = %d, radius3 = %d \n",
+            m_fill_parms[0], m_fill_parms[1], m_fill_parms[2]);
+        printf("iterations1 = %d, iterations2 = %d, iterations3 = %d \n",
+            m_fill_parms[3], m_fill_parms[4], m_fill_parms[5]);
+        i2 = fill_holes(i2, m_fill_parms[0], m_fill_parms[3]);
+        i2 = fill_holes(i2, m_fill_parms[1], m_fill_parms[4]);
+        i2 = fill_holes(i2, m_fill_parms[2], m_fill_parms[5]);
+        if (m_debug) {
+            itk_image_save(i2, "4_filled.nrrd");
+        }
+    }
+
+    /* Invert the image */
+    printf("invert\n");
+    invert_image(i2);
+
+    /* Return image to caller */
+    printf("return\n");
+    this->img_out->set_itk(i2);
 }
diff --git a/src/plastimatch/segment/segment_body.h b/src/plastimatch/segment/segment_body.h
index af094c1..912229d 100644
--- a/src/plastimatch/segment/segment_body.h
+++ b/src/plastimatch/segment/segment_body.h
@@ -42,10 +42,12 @@ class PLMSEGMENT_API Segment_body {
         m_fill_parms[5]= 50;
     }
     void do_segmentation ();
-	void do_segmentation_air_cavity();
+    void do_segmentation_air_cavity();
+    FloatImageType::Pointer reduce_image_dim (FloatImageType::Pointer i1);
     UCharImageType::Pointer threshold_patient (FloatImageType::Pointer i1);
     int find_patient_bottom (FloatImageType::Pointer i1);
-    UCharImageType::Pointer fill_holes (UCharImageType::Pointer mask, int radius, int max_its);
+    UCharImageType::Pointer fill_holes (UCharImageType::Pointer mask, 
+        int radius, int max_its);
 };
 
 #endif
diff --git a/src/plastimatch/standalone/CMakeLists.txt b/src/plastimatch/standalone/CMakeLists.txt
index 5ba54bd..121ee87 100644
--- a/src/plastimatch/standalone/CMakeLists.txt
+++ b/src/plastimatch/standalone/CMakeLists.txt
@@ -4,6 +4,7 @@
 project (src_plastimatch_standalone)
 
 include_directories (BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
+include_directories (BEFORE ${CMAKE_CURRENT_BINARY_DIR})
 include_directories (AFTER ${INIH_INCLUDE_DIR})
 
 set (XVI_ARCHIVE_LIBRARIES 
@@ -53,9 +54,6 @@ set (DRR_SRC
   drr_main.cxx 
   drr_opts.cxx
   )
-set (EXTRACT_CONTOUR_SRC
-  extract_contour.cxx
-  )
 set (FDK_SRC
   fdk_main.cxx 
   fdk_opts.cxx
@@ -147,9 +145,67 @@ QT4_WRAP_CPP(nki2mha_converter_SRC
 	nki2mha_converter.h)
 QT4_WRAP_UI(nki2mha_converter_SRC
 	nki2mha_converter.ui)
-
 QT4_ADD_RESOURCES (nki2mha_converter_SRC
 	nki2mha_converter.qrc)
+
+	
+set(GAMMA_GUI_SRC
+	gamma_gui_main.cpp	
+	gamma_gui.cpp
+	gamma_gui.h
+	qyklabel.h
+	qyklabel.cpp
+	DlgGammaView.h
+	DlgGammaView.cxx
+	YK16GrayImage.cxx
+	YK16GrayImage.h	
+	qt_util.h
+	qt_util.cxx
+	qcustomplot.h
+	qcustomplot.cpp
+	yk_config.h)
+
+QT4_WRAP_CPP(GAMMA_GUI_MOC
+	DlgGammaView.h
+	qyklabel.h
+	qcustomplot.h
+	gamma_gui.h)
+	
+QT4_WRAP_UI(GAMMA_GUI_UI
+	gamma_gui.ui
+	DlgGammaView.ui)
+
+set(GAMMA_GUI_SRC
+	${GAMMA_GUI_SRC}
+	${GAMMA_GUI_MOC}
+	${GAMMA_GUI_UI}
+	)
+
+set(REGISTER_GUI_SRC
+	register_gui_main.cpp
+	register_gui.cpp 
+	register_gui.h
+	qyklabel.h
+	qyklabel.cpp
+	YK16GrayImage.cxx
+	YK16GrayImage.h
+	qt_util.h
+	qt_util.cxx
+	qcustomplot.h
+	qcustomplot.cpp
+	yk_config.h
+	YKThreadRegi.h
+	YKThreadRegi.cpp
+	)
+
+QT4_WRAP_CPP(REGISTER_GUI_SRC	
+	qyklabel.h
+	qcustomplot.h
+	register_gui.h
+	YKThreadRegi.h)
+QT4_WRAP_UI(REGISTER_GUI_SRC
+	register_gui.ui
+	)
 endif()
 
 ##-----------------------------------------------------------------------------
@@ -158,26 +214,23 @@ endif()
 if (ITK_FOUND)
   plm_add_executable (compute_distance "${COMPUTE_DISTANCE_SRC}" 
     "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
-    ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
+    ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
   plm_add_executable (dicom_info "${DICOM_INFO_SRC}"
     "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
-    ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
-  plm_add_executable (extract_contour "${EXTRACT_CONTOUR_SRC}"
-    "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
     ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
   plm_add_executable (landmark_diff "${LANDMARK_DIFF_SRC}" 
     "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
     ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
   plm_add_executable (merge_vfs  "${MERGE_VFS_SRC}"
     "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
-    ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
+    ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
   plm_add_executable (shuffle_mha "${SHUFFLE_MHA_SRC}"
     "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
-    ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
+    ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
   if (PLM_BUILD_VISCOUS)
     plm_add_executable (viscous "${VISCOUS_SRC}"
       "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
-      ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
+      ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
   endif ()
   plm_add_executable (vf_invert "${VF_INVERT_SRC}"
       "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
@@ -220,16 +273,18 @@ endif ()
 #    ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
 endif ()
 
-
 if (QT4_FOUND)
-	set (nki2mha_converter_LIBRARIES ${QT_LIBRARIES} ${PLASTIMATCH_LIBS})
-	ADD_EXECUTABLE(nki2mha_converter ${nki2mha_converter_SRC})
-	INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
-	TARGET_LINK_LIBRARIES(nki2mha_converter ${nki2mha_converter_LIBRARIES})
-	#plm_add_executable (nki2mha_converter "${nki2mha_converter_SRC}" 
-	 #"${PLASTIMATCH_LIBS} ${QT_LIBRARIES} ${QT_LIBRARIES}" "${PLASTIMATCH_LDFLAGS}" 
-	 # ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
-
+    plm_add_executable (nki2mha_converter "${nki2mha_converter_SRC}" 
+	"${PLASTIMATCH_LIBS};${QT_LIBRARIES}" "${PLASTIMATCH_LDFLAGS}" 
+	${BUILD_ALWAYS} ${INSTALL_IF_NOT_DEBIAN})
+    if (DCMTK_FOUND)
+	plm_add_executable (gamma_gui "${GAMMA_GUI_SRC}" 
+	    "${PLASTIMATCH_LIBS};${QT_LIBRARIES}" "${PLASTIMATCH_LDFLAGS}" 
+	    ${BUILD_ALWAYS} ${INSTALL_IF_NOT_DEBIAN})
+    endif ()
+    plm_add_executable (register_gui "${REGISTER_GUI_SRC}" 
+	"${PLASTIMATCH_LIBS};${QT_LIBRARIES}" "${PLASTIMATCH_LDFLAGS}" 
+	${BUILD_ALWAYS} ${INSTALL_IF_NOT_DEBIAN})
 endif()
 
 plm_add_executable (cmd_prompt_launcher "${CMD_PROMPT_LAUNCHER_SRC}" 
@@ -248,7 +303,7 @@ plm_add_executable (demons "${DEMONS_SRC}"
   ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
 plm_add_executable (dlib_train "${DLIB_TRAIN_SRC}" 
   "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
-  ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
+  ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
 plm_add_executable (drr "${DRR_SRC}" 
   "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
   ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_ALWAYS})
@@ -260,13 +315,13 @@ plm_add_executable (hnd_to_pfm "${HND_TO_PFM_SRC}"
   ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
 plm_add_executable (mha_to_raw "${MHA_TO_RAW_SRC}"
   "" ""
-  ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
+  ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
 plm_add_executable (proton_dose "${PROTON_DOSE_SRC}" 
   "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
   ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
 plm_add_executable (raw_to_mha "${RAW_TO_MHA_SRC}"
   "" "" 
-  ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_IF_NOT_DEBIAN})
+  ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
 plm_add_executable (bragg_curve "${BRAGG_CURVE_SRC}" 
   "${PLASTIMATCH_LIBS}" "${PLASTIMATCH_LDFLAGS}" 
   ${BUILD_IF_NOT_SLICER_EXT} ${INSTALL_NEVER})
diff --git a/src/plastimatch/standalone/DlgGammaView.cxx b/src/plastimatch/standalone/DlgGammaView.cxx
new file mode 100644
index 0000000..4683727
--- /dev/null
+++ b/src/plastimatch/standalone/DlgGammaView.cxx
@@ -0,0 +1,24 @@
+#include "DlgGammaView.h"
+#include "gamma_gui.h"
+
+#define FIXME_BACKGROUND_MAX (-1200)
+
+using namespace std;
+
+DlgGammaView::DlgGammaView(): QDialog ()
+{
+    /* Sets up the GUI */
+    ui.setupUi (this);
+}
+
+DlgGammaView::DlgGammaView(QWidget *parent): QDialog (parent)
+{
+    ui.setupUi (this);
+    m_pParent = (gamma_gui*)(parent);  
+
+}
+
+DlgGammaView::~DlgGammaView()
+{    
+ 
+}
\ No newline at end of file
diff --git a/src/plastimatch/standalone/DlgGammaView.h b/src/plastimatch/standalone/DlgGammaView.h
new file mode 100644
index 0000000..4b78d5d
--- /dev/null
+++ b/src/plastimatch/standalone/DlgGammaView.h
@@ -0,0 +1,33 @@
+#pragma once
+#include <QDialog>
+#include "ui_DlgGammaView.h"
+class gamma_gui;
+
+class DlgGammaView : public QDialog,
+    public Ui::DlgGammaViewClass
+{
+    Q_OBJECT
+    ;
+
+public slots:   
+
+
+public:
+    DlgGammaView();    
+    DlgGammaView(QWidget *parent);
+    ~DlgGammaView(); 
+
+
+
+
+
+
+
+public: 
+    gamma_gui* m_pParent; //to pull 3D images       
+
+
+private:
+    Ui::DlgGammaViewClass ui;
+	
+};
diff --git a/src/plastimatch/standalone/DlgGammaView.ui b/src/plastimatch/standalone/DlgGammaView.ui
new file mode 100644
index 0000000..7f2d2ce
--- /dev/null
+++ b/src/plastimatch/standalone/DlgGammaView.ui
@@ -0,0 +1,1980 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DlgGammaViewClass</class>
+ <widget class="QDialog" name="DlgGammaViewClass">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1120</width>
+    <height>823</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <widget class="qyklabel" name="labelOverlapWnd1">
+   <property name="enabled">
+    <bool>true</bool>
+   </property>
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>40</y>
+     <width>512</width>
+     <height>512</height>
+    </rect>
+   </property>
+   <property name="mouseTracking">
+    <bool>true</bool>
+   </property>
+   <property name="autoFillBackground">
+    <bool>false</bool>
+   </property>
+   <property name="frameShape">
+    <enum>QFrame::Box</enum>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+  </widget>
+  <widget class="qyklabel" name="labelOverlapWnd2">
+   <property name="enabled">
+    <bool>true</bool>
+   </property>
+   <property name="geometry">
+    <rect>
+     <x>550</x>
+     <y>40</y>
+     <width>256</width>
+     <height>256</height>
+    </rect>
+   </property>
+   <property name="mouseTracking">
+    <bool>true</bool>
+   </property>
+   <property name="autoFillBackground">
+    <bool>false</bool>
+   </property>
+   <property name="frameShape">
+    <enum>QFrame::Box</enum>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+  </widget>
+  <widget class="qyklabel" name="labelOverlapWnd3">
+   <property name="enabled">
+    <bool>true</bool>
+   </property>
+   <property name="geometry">
+    <rect>
+     <x>550</x>
+     <y>340</y>
+     <width>256</width>
+     <height>256</height>
+    </rect>
+   </property>
+   <property name="mouseTracking">
+    <bool>true</bool>
+   </property>
+   <property name="autoFillBackground">
+    <bool>false</bool>
+   </property>
+   <property name="frameShape">
+    <enum>QFrame::Box</enum>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+  </widget>
+  <widget class="QGroupBox" name="groupBox">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>650</y>
+     <width>381</width>
+     <height>131</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string/>
+   </property>
+   <widget class="QComboBox" name="comboBoxImgFixed">
+    <property name="geometry">
+     <rect>
+      <x>210</x>
+      <y>10</y>
+      <width>161</width>
+      <height>22</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_11">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>0</y>
+      <width>131</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <pointsize>14</pointsize>
+     </font>
+    </property>
+    <property name="text">
+     <string>Fixed Image</string>
+    </property>
+   </widget>
+   <widget class="QSlider" name="sliderFixedW">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>40</y>
+      <width>221</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>10000</number>
+    </property>
+    <property name="singleStep">
+     <number>10</number>
+    </property>
+    <property name="pageStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>2000</number>
+    </property>
+    <property name="orientation">
+     <enum>Qt::Horizontal</enum>
+    </property>
+   </widget>
+   <widget class="QSpinBox" name="spinBoxFixedW">
+    <property name="geometry">
+     <rect>
+      <x>250</x>
+      <y>40</y>
+      <width>51</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>10000</number>
+    </property>
+    <property name="singleStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>2000</number>
+    </property>
+   </widget>
+   <widget class="QSlider" name="sliderFixedL">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>60</y>
+      <width>231</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>65535</number>
+    </property>
+    <property name="singleStep">
+     <number>10</number>
+    </property>
+    <property name="pageStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>1024</number>
+    </property>
+    <property name="orientation">
+     <enum>Qt::Horizontal</enum>
+    </property>
+   </widget>
+   <widget class="QSpinBox" name="spinBoxFixedL">
+    <property name="geometry">
+     <rect>
+      <x>250</x>
+      <y>60</y>
+      <width>51</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>65535</number>
+    </property>
+    <property name="singleStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>1024</number>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditOriginFixed">
+    <property name="geometry">
+     <rect>
+      <x>130</x>
+      <y>90</y>
+      <width>211</width>
+      <height>20</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_17">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>90</y>
+      <width>111</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Origin (DICOM, mm)</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_29">
+    <property name="geometry">
+     <rect>
+      <x>310</x>
+      <y>40</y>
+      <width>21</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>W</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_30">
+    <property name="geometry">
+     <rect>
+      <x>310</x>
+      <y>60</y>
+      <width>21</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>L</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonPassFixed">
+    <property name="geometry">
+     <rect>
+      <x>130</x>
+      <y>10</y>
+      <width>61</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Pass</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_2">
+   <property name="geometry">
+    <rect>
+     <x>430</x>
+     <y>650</y>
+     <width>381</width>
+     <height>131</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string/>
+   </property>
+   <widget class="QComboBox" name="comboBoxImgMoving">
+    <property name="geometry">
+     <rect>
+      <x>220</x>
+      <y>10</y>
+      <width>151</width>
+      <height>22</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_13">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>0</y>
+      <width>131</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <pointsize>14</pointsize>
+     </font>
+    </property>
+    <property name="text">
+     <string>Moving Image</string>
+    </property>
+   </widget>
+   <widget class="QSlider" name="sliderMovingW">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>40</y>
+      <width>221</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>10000</number>
+    </property>
+    <property name="singleStep">
+     <number>10</number>
+    </property>
+    <property name="pageStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>2000</number>
+    </property>
+    <property name="orientation">
+     <enum>Qt::Horizontal</enum>
+    </property>
+   </widget>
+   <widget class="QSpinBox" name="spinBoxMovingW">
+    <property name="geometry">
+     <rect>
+      <x>250</x>
+      <y>40</y>
+      <width>51</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>10000</number>
+    </property>
+    <property name="singleStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>2000</number>
+    </property>
+   </widget>
+   <widget class="QSlider" name="sliderMovingL">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>60</y>
+      <width>231</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>65535</number>
+    </property>
+    <property name="singleStep">
+     <number>10</number>
+    </property>
+    <property name="pageStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>1024</number>
+    </property>
+    <property name="orientation">
+     <enum>Qt::Horizontal</enum>
+    </property>
+   </widget>
+   <widget class="QSpinBox" name="spinBoxMovingL">
+    <property name="geometry">
+     <rect>
+      <x>250</x>
+      <y>60</y>
+      <width>51</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>65535</number>
+    </property>
+    <property name="singleStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>1024</number>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditOriginMoving">
+    <property name="geometry">
+     <rect>
+      <x>130</x>
+      <y>90</y>
+      <width>201</width>
+      <height>20</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_18">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>90</y>
+      <width>111</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Origin (DICOM, mm)</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_31">
+    <property name="geometry">
+     <rect>
+      <x>310</x>
+      <y>40</y>
+      <width>21</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>W</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_32">
+    <property name="geometry">
+     <rect>
+      <x>310</x>
+      <y>60</y>
+      <width>21</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>L</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonPassMoving">
+    <property name="geometry">
+     <rect>
+      <x>150</x>
+      <y>10</y>
+      <width>61</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Pass</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QSlider" name="sliderPosDisp1">
+   <property name="geometry">
+    <rect>
+     <x>160</x>
+     <y>10</y>
+     <width>361</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="maximum">
+    <number>65535</number>
+   </property>
+   <property name="singleStep">
+    <number>1</number>
+   </property>
+   <property name="pageStep">
+    <number>10</number>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QSlider" name="sliderPosDisp3">
+   <property name="geometry">
+    <rect>
+     <x>640</x>
+     <y>310</y>
+     <width>161</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="maximum">
+    <number>65535</number>
+   </property>
+   <property name="singleStep">
+    <number>1</number>
+   </property>
+   <property name="pageStep">
+    <number>10</number>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QSlider" name="sliderPosDisp2">
+   <property name="geometry">
+    <rect>
+     <x>640</x>
+     <y>10</y>
+     <width>161</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="maximum">
+    <number>65535</number>
+   </property>
+   <property name="singleStep">
+    <number>1</number>
+   </property>
+   <property name="pageStep">
+    <number>10</number>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_3">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>560</y>
+     <width>511</width>
+     <height>81</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string/>
+   </property>
+   <widget class="QLineEdit" name="lineEditCurPosX">
+    <property name="geometry">
+     <rect>
+      <x>50</x>
+      <y>60</y>
+      <width>61</width>
+      <height>21</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditCurPosY">
+    <property name="geometry">
+     <rect>
+      <x>160</x>
+      <y>60</y>
+      <width>61</width>
+      <height>21</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditCurPosZ">
+    <property name="geometry">
+     <rect>
+      <x>280</x>
+      <y>60</y>
+      <width>61</width>
+      <height>21</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonCurPosGo">
+    <property name="geometry">
+     <rect>
+      <x>350</x>
+      <y>60</y>
+      <width>51</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>GO</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_20">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>60</y>
+      <width>41</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>X (mm)</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_21">
+    <property name="geometry">
+     <rect>
+      <x>120</x>
+      <y>60</y>
+      <width>41</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Y (mm)</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_22">
+    <property name="geometry">
+     <rect>
+      <x>230</x>
+      <y>60</y>
+      <width>41</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Z (mm)</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_12">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>0</y>
+      <width>181</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <pointsize>10</pointsize>
+     </font>
+    </property>
+    <property name="text">
+     <string>Current Position (DICOM)</string>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxDrawCrosshair">
+    <property name="geometry">
+     <rect>
+      <x>210</x>
+      <y>10</y>
+      <width>70</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Crosshair</string>
+    </property>
+    <property name="checked">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxDrawSplit">
+    <property name="geometry">
+     <rect>
+      <x>210</x>
+      <y>30</y>
+      <width>70</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>SplitView</string>
+    </property>
+    <property name="checked">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxZoom">
+    <property name="geometry">
+     <rect>
+      <x>350</x>
+      <y>30</y>
+      <width>51</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Zoom</string>
+    </property>
+    <property name="checked">
+     <bool>false</bool>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxPan">
+    <property name="geometry">
+     <rect>
+      <x>350</x>
+      <y>10</y>
+      <width>51</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Pan</string>
+    </property>
+    <property name="checked">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonRestoreSingle">
+    <property name="geometry">
+     <rect>
+      <x>420</x>
+      <y>10</y>
+      <width>81</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Restore single</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonRestoreAll">
+    <property name="geometry">
+     <rect>
+      <x>420</x>
+      <y>50</y>
+      <width>81</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Restore all</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QLabel" name="labelDisp1">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>10</y>
+     <width>101</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <pointsize>12</pointsize>
+    </font>
+   </property>
+   <property name="text">
+    <string>Axial</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="labelDisp2">
+   <property name="geometry">
+    <rect>
+     <x>540</x>
+     <y>10</y>
+     <width>91</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <pointsize>12</pointsize>
+    </font>
+   </property>
+   <property name="text">
+    <string>Frontal</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="labelDisp3">
+   <property name="geometry">
+    <rect>
+     <x>550</x>
+     <y>310</y>
+     <width>81</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <pointsize>12</pointsize>
+    </font>
+   </property>
+   <property name="text">
+    <string>Sagittal</string>
+   </property>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_4">
+   <property name="geometry">
+    <rect>
+     <x>810</x>
+     <y>130</y>
+     <width>261</width>
+     <height>121</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Manual registration</string>
+   </property>
+   <widget class="QLabel" name="label_19">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>40</y>
+      <width>71</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Moving origin</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditOriginChanged">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>60</y>
+      <width>131</width>
+      <height>20</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_23">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>90</y>
+      <width>121</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Moving resolution (mm)</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditMovingResol">
+    <property name="geometry">
+     <rect>
+      <x>140</x>
+      <y>90</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>1.0</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonRestoreOriginal">
+    <property name="geometry">
+     <rect>
+      <x>90</x>
+      <y>20</y>
+      <width>51</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Restore</string>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxKeyMoving">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>20</y>
+      <width>81</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Keyboard</string>
+    </property>
+    <property name="checked">
+     <bool>false</bool>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditGradOption">
+    <property name="geometry">
+     <rect>
+      <x>160</x>
+      <y>60</y>
+      <width>91</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>0.7,0.7,0.7</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_5">
+   <property name="geometry">
+    <rect>
+     <x>810</x>
+     <y>390</y>
+     <width>221</width>
+     <height>91</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Plastimatch- rigid body</string>
+   </property>
+   <widget class="QPushButton" name="pushButtonDoRigid">
+    <property name="geometry">
+     <rect>
+      <x>120</x>
+      <y>20</y>
+      <width>91</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Do registration</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_41">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>60</y>
+      <width>101</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Crop skin  bf. DIR</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditCBCTSkinCropBfDIR">
+    <property name="geometry">
+     <rect>
+      <x>130</x>
+      <y>60</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>8.0</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_42">
+    <property name="geometry">
+     <rect>
+      <x>180</x>
+      <y>60</y>
+      <width>21</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>mm</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_6">
+   <property name="geometry">
+    <rect>
+     <x>810</x>
+     <y>490</y>
+     <width>221</width>
+     <height>261</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Plastimatch-deformable</string>
+   </property>
+   <widget class="QLabel" name="label_25">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>20</y>
+      <width>81</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Optimization</string>
+    </property>
+   </widget>
+   <widget class="QComboBox" name="comboBoxDeformOption">
+    <property name="geometry">
+     <rect>
+      <x>100</x>
+      <y>20</y>
+      <width>91</width>
+      <height>22</height>
+     </rect>
+    </property>
+    <property name="currentIndex">
+     <number>-1</number>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonDoDeformable">
+    <property name="geometry">
+     <rect>
+      <x>90</x>
+      <y>200</y>
+      <width>91</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Do registration</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_26">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>50</y>
+      <width>101</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Arguments Step1</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditArgument1">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>70</y>
+      <width>171</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>2,2,1,30,0.00001,0.005,30</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_27">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>100</y>
+      <width>101</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Arguments Step2</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditArgument2">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>120</y>
+      <width>171</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_28">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>150</y>
+      <width>101</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Arguments Step3</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditArgument3">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>170</y>
+      <width>171</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonMacro">
+    <property name="geometry">
+     <rect>
+      <x>90</x>
+      <y>230</y>
+      <width>91</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Macro</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QPushButton" name="pushButtonChangeView">
+   <property name="geometry">
+    <rect>
+     <x>550</x>
+     <y>600</y>
+     <width>91</width>
+     <height>41</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Chage View</string>
+   </property>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_7">
+   <property name="geometry">
+    <rect>
+     <x>810</x>
+     <y>10</y>
+     <width>221</width>
+     <height>111</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Prepare CT</string>
+   </property>
+   <widget class="QCheckBox" name="checkBoxCropBkgroundCT">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>30</y>
+      <width>101</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Crop bkground</string>
+    </property>
+    <property name="checked">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxFillBubbleCT">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>50</y>
+      <width>71</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Fill bubble</string>
+    </property>
+    <property name="checked">
+     <bool>false</bool>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonProcessCT">
+    <property name="geometry">
+     <rect>
+      <x>120</x>
+      <y>80</y>
+      <width>91</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Process</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditBkDetectCT">
+    <property name="geometry">
+     <rect>
+      <x>110</x>
+      <y>50</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>-600</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_33">
+    <property name="geometry">
+     <rect>
+      <x>110</x>
+      <y>10</y>
+      <width>41</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Detect</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_34">
+    <property name="geometry">
+     <rect>
+      <x>170</x>
+      <y>10</y>
+      <width>41</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Fill</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditBkFillCT">
+    <property name="geometry">
+     <rect>
+      <x>160</x>
+      <y>30</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>-1024</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditBubFillCT">
+    <property name="geometry">
+     <rect>
+      <x>160</x>
+      <y>50</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>0</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_37">
+    <property name="geometry">
+     <rect>
+      <x>120</x>
+      <y>30</y>
+      <width>31</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>skin</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_8">
+   <property name="geometry">
+    <rect>
+     <x>810</x>
+     <y>260</y>
+     <width>221</width>
+     <height>111</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Prepare CBCT</string>
+   </property>
+   <widget class="QCheckBox" name="checkBoxCropBkgroundCBCT">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>30</y>
+      <width>101</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Crop bkground</string>
+    </property>
+    <property name="checked">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxFillBubbleCBCT">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>50</y>
+      <width>71</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Fill bubble</string>
+    </property>
+    <property name="checked">
+     <bool>false</bool>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditBubFillCBCT">
+    <property name="geometry">
+     <rect>
+      <x>160</x>
+      <y>50</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>700</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_35">
+    <property name="geometry">
+     <rect>
+      <x>110</x>
+      <y>10</y>
+      <width>41</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Detect</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditBubDetectCBCT">
+    <property name="geometry">
+     <rect>
+      <x>110</x>
+      <y>50</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>500</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_36">
+    <property name="geometry">
+     <rect>
+      <x>170</x>
+      <y>10</y>
+      <width>21</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Fill</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditBkFillCBCT">
+    <property name="geometry">
+     <rect>
+      <x>160</x>
+      <y>30</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>0</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_38">
+    <property name="geometry">
+     <rect>
+      <x>120</x>
+      <y>30</y>
+      <width>31</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>skin</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_39">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>80</y>
+      <width>101</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Crop skin  bf. regid</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditCBCTSkinCropBfRegid">
+    <property name="geometry">
+     <rect>
+      <x>130</x>
+      <y>80</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>10.0</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_40">
+    <property name="geometry">
+     <rect>
+      <x>180</x>
+      <y>80</y>
+      <width>21</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>mm</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QPushButton" name="pushButtonGradSearch">
+   <property name="geometry">
+    <rect>
+     <x>980</x>
+     <y>150</y>
+     <width>61</width>
+     <height>23</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Gradient</string>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>qyklabel</class>
+   <extends>QLabel</extends>
+   <header>qyklabel.h</header>
+   <slots>
+    <slot>SetDrawPointToggle(bool)</slot>
+   </slots>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>pushButtonCurPosGo</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_CrntPosGo()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>412</x>
+     <y>634</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>555</x>
+     <y>633</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderPosDisp1</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DrawImageWhenSliceChange()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>165</x>
+     <y>22</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>118</x>
+     <y>17</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderPosDisp2</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DrawImageWhenSliceChange()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>735</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>817</x>
+     <y>22</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderPosDisp3</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DrawImageWhenSliceChange()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>695</x>
+     <y>317</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>808</x>
+     <y>305</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderFixedW</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DrawImageInFixedSlice()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>46</x>
+     <y>697</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>16</x>
+     <y>697</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderFixedL</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DrawImageInFixedSlice()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>43</x>
+     <y>723</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>14</x>
+     <y>731</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderMovingW</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DrawImageInFixedSlice()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>458</x>
+     <y>696</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>423</x>
+     <y>701</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderMovingL</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DrawImageInFixedSlice()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>456</x>
+     <y>721</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>420</x>
+     <y>733</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderFixedW</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>spinBoxFixedW</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>254</x>
+     <y>698</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>284</x>
+     <y>697</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>spinBoxFixedW</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>sliderFixedW</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>300</x>
+     <y>693</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>240</x>
+     <y>694</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderFixedL</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>spinBoxFixedL</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>228</x>
+     <y>716</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>297</x>
+     <y>716</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>spinBoxFixedL</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>sliderFixedL</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>296</x>
+     <y>727</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>256</x>
+     <y>728</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderMovingW</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>spinBoxMovingW</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>655</x>
+     <y>701</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>689</x>
+     <y>699</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderMovingL</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>spinBoxMovingL</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>667</x>
+     <y>722</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>683</x>
+     <y>722</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>spinBoxMovingL</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>sliderMovingL</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>710</x>
+     <y>727</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>647</x>
+     <y>728</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>spinBoxMovingW</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>sliderMovingW</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>686</x>
+     <y>697</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>659</x>
+     <y>697</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonChangeView</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_ChangeView()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>597</x>
+     <y>608</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>638</x>
+     <y>612</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>checkBoxDrawCrosshair</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DrawImageWhenSliceChange()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>281</x>
+     <y>584</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>256</x>
+     <y>563</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonRestoreSingle</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_RestoreImageSingle()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>529</x>
+     <y>585</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>541</x>
+     <y>552</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonRestoreAll</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_RestoreImageAll()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>508</x>
+     <y>632</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>537</x>
+     <y>648</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonDoRigid</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DoRegistrationRigid()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>935</x>
+     <y>268</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>828</x>
+     <y>303</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>checkBoxKeyMoving</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_BringFocusToEnableArrow(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>900</x>
+     <y>150</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1012</x>
+     <y>20</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>checkBoxKeyMoving</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_KeyMoving(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>900</x>
+     <y>150</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1019</x>
+     <y>69</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>comboBoxImgFixed</sender>
+   <signal>currentIndexChanged(QString)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_FixedImageSelected(QString)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>381</x>
+     <y>663</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>387</x>
+     <y>792</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>comboBoxImgMoving</sender>
+   <signal>currentIndexChanged(QString)</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_MovingImageSelected(QString)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>780</x>
+     <y>674</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>822</x>
+     <y>721</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonRestoreOriginal</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_RestoreMovingImg()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>1003</x>
+     <y>170</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>968</x>
+     <y>0</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonDoDeformable</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DoRegistrationDeform()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>948</x>
+     <y>690</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>951</x>
+     <y>584</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonProcessCT</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_PreProcessCT()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>952</x>
+     <y>93</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1008</x>
+     <y>119</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonPassFixed</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_PassFixedImgForAnalysis()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>190</x>
+     <y>672</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>18</x>
+     <y>640</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonPassMoving</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_PassMovingImgForAnalysis()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>613</x>
+     <y>670</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>667</x>
+     <y>625</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonMacro</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_Macro()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>946</x>
+     <y>734</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>948</x>
+     <y>757</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonGradSearch</sender>
+   <signal>released()</signal>
+   <receiver>DlgGammaViewClass</receiver>
+   <slot>SLT_DoRegistrationGradient()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>940</x>
+     <y>159</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1039</x>
+     <y>171</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+ <slots>
+  <slot>SLT_CrntPosGo()</slot>
+  <slot>SLT_DrawImageWhenSliceChange()</slot>
+  <slot>SLT_DrawImageInFixedSlice()</slot>
+  <slot>SLT_ChangeView()</slot>
+  <slot>SLT_RestoreImageSingle()</slot>
+  <slot>SLT_RestoreImageAll()</slot>
+  <slot>SLT_DoRegistrationRigid()</slot>
+  <slot>SLT_BringFocusToEnableArrow(bool)</slot>
+  <slot>SLT_KeyMoving(bool)</slot>
+  <slot>SLT_FixedImageSelected(QString)</slot>
+  <slot>SLT_MovingImageSelected(QString)</slot>
+  <slot>SLT_RestoreMovingImg()</slot>
+  <slot>SLT_DoRegistrationDeform()</slot>
+  <slot>SLT_PreProcessCT()</slot>
+  <slot>SLT_PreProcessCBCT()</slot>
+  <slot>SLT_PassFixedImgForAnalysis()</slot>
+  <slot>SLT_PassMovingImgForAnalysis()</slot>
+  <slot>SLT_Macro()</slot>
+  <slot>SLT_ExchangeRawRef()</slot>
+  <slot>SLT_DoRegistrationGradient()</slot>
+ </slots>
+</ui>
diff --git a/src/plastimatch/standalone/Launch_cmd_prompt.bat b/src/plastimatch/standalone/Launch_cmd_prompt.bat
new file mode 100644
index 0000000..d03057f
--- /dev/null
+++ b/src/plastimatch/standalone/Launch_cmd_prompt.bat
@@ -0,0 +1,24 @@
+set target_file_a=C:\Windows\system32\cmd.exe
+ at echo off
+set SCRIPT="%TEMP%\%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%.vbs"
+echo Set oWS = WScript.CreateObject("WScript.Shell") >> %SCRIPT%
+echo sLinkFile = "%ALLUSERSPROFILE%\Desktop\plastimatch32_cmd.lnk" >> %SCRIPT%
+echo Set oLink = oWS.CreateShortcut(sLinkFile) >> %SCRIPT%
+echo oLink.TargetPath =  "%target_file_a%" >> %SCRIPT%
+echo oLink.WorkingDirectory = "%~dp0" >> %SCRIPT%
+echo oLink.Save >> %SCRIPT%
+cscript /nologo %SCRIPT%
+del %SCRIPT%
+
+set SCRIPT2="%TEMP%\%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%.vbs"
+echo Set oWS = WScript.CreateObject("WScript.Shell") >> %SCRIPT2%
+echo sLinkFile = "%~dp0\plastimatch32_cmd.lnk" >> %SCRIPT2%
+echo Set oLink = oWS.CreateShortcut(sLinkFile) >> %SCRIPT2%
+echo oLink.TargetPath =  "%target_file_a%" >> %SCRIPT2%
+echo oLink.WorkingDirectory = "%~dp0" >> %SCRIPT2%
+echo oLink.Save >> %SCRIPT2%
+cscript /nologo %SCRIPT2%
+del %SCRIPT2%
+
+start "" http://plastimatch.org/contents.html
+%SystemRoot%\explorer.exe "%~dp0"
diff --git a/src/plastimatch/standalone/YK16GrayImage.cxx b/src/plastimatch/standalone/YK16GrayImage.cxx
new file mode 100644
index 0000000..042028c
--- /dev/null
+++ b/src/plastimatch/standalone/YK16GrayImage.cxx
@@ -0,0 +1,2761 @@
+#include "YK16GrayImage.h"
+#include <QPixmap>
+#include <fstream>
+#include <iostream>
+#include <QLabel>
+#include <math.h>
+#include "itkImageRegionIterator.h"
+#include "itkMedianImageFilter.h"
+#include "itkIdentityTransform.h"
+#include "itkResampleImageFilter.h"
+#include "itkAffineTransform.h"
+#include "itkNearestNeighborInterpolateImageFunction.h"
+#include "itkFlipImageFilter.h"
+#include <QPainter>
+
+#include "qt_util.h"
+
+
+using namespace std;
+
+YK16GrayImage::YK16GrayImage(void)
+{
+	m_iWidth = 0;
+	m_iHeight = 0;
+
+	m_pData = NULL;
+	m_pPixmap = NULL;// for display
+	m_pElektaHisHeader = NULL;
+
+	//m_pQImage = NULL;	
+
+	//m_pPainter = NULL;
+
+	m_fPixelMean = 0.0;
+	m_fPixelSD= 0.0;
+	m_fPixelMin = 0.0;
+	m_fPixelMax= 0.0;
+
+	m_fPixelMean_ROI = 0.0;
+	m_fPixelSD_ROI = 0.0;
+	m_fPixelMin_ROI = 0.0;
+	m_fPixelMax_ROI = 0.0;	
+	m_bDrawROI = false;
+	m_bShowInvert = false;
+
+	m_fSpacingX = 0.0;
+	m_fSpacingY = 0.0;
+        m_fOriginX = 0.0;
+        m_fOriginY = 0.0;
+
+	m_ptProfileProbe.setX(0); //Mouse Clicked Position --> Data
+	m_ptProfileProbe.setY(0);	
+
+	m_bDrawProfileX = false;
+	m_bDrawProfileY = false;
+
+
+	m_ptFOVCenter.setX(0);
+	m_ptFOVCenter.setY(0);
+	m_iFOVRadius = 0;//data pixel
+	m_bDrawFOVCircle = false;
+
+	m_iTableTopPos = 0;
+	m_bDrawTableLine = false;
+
+
+	m_bDrawCrosshair = false;
+
+	m_fZoom = 1.0;
+	m_iOffsetX = 0;
+	m_iOffsetY = 0;
+
+	m_strTimeStamp = "00000000"; //9 digits HHMMSSFFF
+
+	m_bDrawMarkers = false;
+	m_bDrawMarkersRef = false;
+	m_iNumOfDispMarker = -1;
+	m_iNumOfTrackMarker = -1;
+
+	m_track_priorErr = 0.0;
+	m_track_motionErr = 0.0;
+	//m_track_motionWtX = 0.0;
+	//m_track_motionWtY = 0.0;
+
+	m_fMVGantryAngle = 0.0;
+	m_fPanelOffsetX = 0.0; //mm
+	m_fPanelOffsetY = 0.0;//mm
+	m_bKVOn = true;//default value: should be on otherwise doesn't work in simulation mode
+	m_bMVOn = false;
+	m_iXVI_ElapseMS = 0;
+
+	m_iProcessingElapsed = 0;
+	m_track_CC_penalty = 0.0;
+
+	m_bDraw8Bit = false;
+
+        m_fIntensityMag = 1.0;//intensity magnification factor default = 1.0;
+        m_fIntensityOffset = 0.0;//intensity Offset factor default = 0.0;
+
+        m_bDrawOverlayText = false;
+
+        //m_bDrawContours = false;
+        m_fNormValue = -1.0;
+}
+
+YK16GrayImage::YK16GrayImage(int width, int height)
+{
+	m_pData = NULL;
+	m_pPixmap = NULL;// for display
+	m_pElektaHisHeader = NULL;
+
+	m_fPixelMean = 0.0;
+	m_fPixelSD = 0.0;
+	m_fPixelMin = 0.0;
+	m_fPixelMax = 0.0;
+
+	m_fPixelMean_ROI = 0.0;
+	m_fPixelSD_ROI = 0.0;
+	m_fPixelMin_ROI = 0.0;
+	m_fPixelMax_ROI = 0.0;
+	m_bDrawROI = false;
+	m_bShowInvert = false;
+
+	m_fSpacingX = 0.0;
+	m_fSpacingY = 0.0;
+        m_fOriginX = 0.0;
+        m_fOriginY = 0.0;
+
+
+	m_ptProfileProbe.setX(0); //Mouse Clicked Position --> Data
+	m_ptProfileProbe.setY(0);
+
+	m_bDrawProfileX = false;
+	m_bDrawProfileY = false;
+
+
+	m_ptFOVCenter.setX(0);
+	m_ptFOVCenter.setY(0);
+	m_iFOVRadius = 0;//data pixel
+	m_bDrawFOVCircle = false;
+
+	m_iTableTopPos = 0;
+	m_bDrawTableLine = false;
+
+
+	m_bDrawCrosshair = false;
+
+	m_fZoom = 1.0;
+	m_iOffsetX = 0;
+	m_iOffsetY = 0;
+
+	m_strTimeStamp = "00000000"; //9 digits HHMMSSFFF
+
+	m_iWidth = width;
+	m_iHeight = height;	
+	CreateImage(width, height, 0);		
+
+	m_bDrawMarkers = false;
+	m_bDrawMarkersRef = false;
+	m_iNumOfDispMarker = -1;
+	m_iNumOfTrackMarker = -1;
+
+	m_track_priorErr = 0.0;
+	m_track_motionErr = 0.0;
+	//m_track_motionWtX = 0.0;
+	//m_track_motionWtY = 0.0;
+	m_track_CC_penalty = 0.0;
+
+	m_fMVGantryAngle = 0.0;
+	m_fPanelOffsetX = 0.0; //mm
+	m_fPanelOffsetY = 0.0;//mm
+        m_bKVOn = true;
+	m_bMVOn = false;
+	m_iXVI_ElapseMS = 0;
+
+	m_iProcessingElapsed = 0;
+	m_track_CC_penalty = 0.0;
+
+	m_bDraw8Bit = false;
+        //m_bDrawContours = false;
+
+        m_fIntensityMag = 1.0;//intensity magnification factor default = 1.0;
+        m_fIntensityOffset = 0.0;//intensity Offset factor default = 0.0;
+
+        m_bDrawOverlayText = false;
+        m_fNormValue = -1.0;
+}
+
+YK16GrayImage::~YK16GrayImage(void)
+{
+	ReleaseBuffer();
+}
+
+
+bool YK16GrayImage::ReleaseBuffer()
+{
+	if (m_pData != NULL)
+	{
+		delete [] m_pData;
+		m_pData = NULL;
+	}
+	if (m_pPixmap != NULL)
+	{
+		delete m_pPixmap; //delete  m_pPixmap??
+                m_pPixmap = NULL;
+	}
+
+	m_iWidth = 0;
+	m_iHeight = 0;
+
+	if (m_pElektaHisHeader!= NULL)
+	{
+		delete [] m_pElektaHisHeader;
+		m_pElektaHisHeader= NULL;
+	}
+
+	m_vMarker.clear();
+	m_vMarkerRef.clear();
+
+        //m_vContourROI.clear();
+
+        ClearContourROI();
+
+        m_strOverlayText = "";
+
+        m_fNormValue = -1.0;
+
+	return true;
+}
+
+
+bool YK16GrayImage::IsEmpty()
+{
+	if (m_pData == NULL)
+		return true;
+	else
+		return false;
+}
+
+bool YK16GrayImage::CreateImage(int width, int height, unsigned short usVal)
+{
+	if (width < 1 || height < 1)
+		return false;
+
+	if (usVal < 0 || usVal > 65535)
+		usVal = 0;
+
+	//if (m_pData != NULL)
+	//delete [] m_pData;
+
+	ReleaseBuffer();
+
+	m_iWidth = width;
+	m_iHeight = height;
+
+	int imgSize = width*height;
+	m_pData = new unsigned short [imgSize];
+
+	for (int i = 0 ; i<imgSize ; i++)
+	{
+		m_pData[i] = usVal;
+	}
+
+	//m_QImage = QImage(m_iWidth, m_iHeight,QImage::Format_RGB888);
+
+	return true;
+}
+
+
+bool YK16GrayImage::LoadRawImage(const char *filePath, int width, int height)
+{
+	if (width < 1 || height < 1)
+		return false;
+
+	//if (m_pData != NULL)
+	//	delete [] m_pData;	
+	ReleaseBuffer();
+
+	m_strFilePath = filePath;
+
+	m_iWidth = width;
+	m_iHeight = height;
+
+	int imgSize = width*height;
+	m_pData = new unsigned short [imgSize];	
+
+	//aqprintf("ImageInfo in LoadRawImage, w: %d  h: %d   %d  %d \n",width, height, m_iWidth, m_iHeight);
+
+	FILE* fd = NULL;
+	fd = fopen(filePath, "rb");
+
+	if (fd == NULL)
+		return false;
+
+	unsigned short buf = 0;	
+
+	for (int i = 0 ; i<imgSize ; i++)
+	{
+		fread(&buf, 2, 1, fd);
+		m_pData[i] = buf;
+	}	
+
+	fclose(fd);
+	return true;
+}
+
+
+bool YK16GrayImage::LoadRawImage(const char *filePath, int width, int height, int headerOffset, bool bInvert)
+{
+	if (width < 1 || height < 1)
+		return false;
+
+	//if (m_pData != NULL)
+	//	delete [] m_pData;	
+	ReleaseBuffer();
+
+	m_strFilePath = filePath;
+
+	m_iWidth = width;
+	m_iHeight = height;
+
+	int imgSize = width*height;
+	m_pData = new unsigned short[imgSize];
+
+	//aqprintf("ImageInfo in LoadRawImage, w: %d  h: %d   %d  %d \n",width, height, m_iWidth, m_iHeight);
+
+	FILE* fd = NULL;
+	fd = fopen(filePath, "rb");
+
+	if (fd == NULL)
+		return false;
+
+	unsigned short buf = 0;
+
+
+	unsigned short subVal = 0;
+	int invSig = 1;
+
+	if (bInvert)
+	{
+		subVal = 65535;
+		invSig = -1;
+	}
+	/*else
+	{
+		subVal = 0;
+		invSig = 1;
+	}*/
+
+
+
+	fseek(fd, headerOffset, 0);
+
+	for (int i = 0; i < imgSize; i++)
+	{
+		fread(&buf, 2, 1, fd);
+		m_pData[i] = (unsigned short)((buf - subVal) * invSig);
+	}	
+	
+	fclose(fd);
+	return true;
+}
+
+bool YK16GrayImage::CopyFromBuffer(unsigned short* pImageBuf, int width, int height)
+{
+	if (m_pData == NULL)
+		return false;
+	if (pImageBuf == NULL)
+		return false;
+	if (width != m_iWidth || height != m_iHeight)
+		return false;
+
+	int imgSize = m_iWidth*m_iHeight;
+
+	for (int i = 0 ; i<imgSize ; i++)
+	{
+		m_pData[i] = pImageBuf[i];
+	}
+
+	//CalcImageInfo();
+
+	return true;
+}
+bool YK16GrayImage::FillPixMap(int winMid, int winWidth) //0-65535 �� window level
+{	
+	if (m_pData == NULL)
+		return false;
+
+	if (m_pPixmap != NULL)
+	{		                
+		delete m_pPixmap;                
+		m_pPixmap = NULL;                
+	}	
+	m_pPixmap = new QPixmap(QSize(m_iWidth,m_iHeight)); //something happened here!!!: w: 4289140  h: 0	        
+
+	//8 bit gray buffer preparing
+
+
+	int size = m_iWidth*m_iHeight;
+
+	uchar* tmpData = new uchar [size*3];//RGB
+	unsigned short uppVal = (int)(winMid + winWidth/2.0);
+	unsigned short lowVal = (int)(winMid - winWidth/2.0);	        
+
+	//It takes 0.4 s in Release mode
+
+	for (int i = 0 ; i<m_iHeight ; i++) //So long time....
+	{
+		for (int j = 0 ; j<m_iWidth ; j++)
+		{
+			int tmpIdx = 3*(i*m_iWidth+j);
+
+
+			if (!m_bShowInvert)
+			{
+
+				if (m_pData[i*m_iWidth+j] >= uppVal)
+				{
+					//QRgb rgbVal = qRgb(255, 255, 255);
+					//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+					tmpData[tmpIdx+0] = 255;
+					tmpData[tmpIdx+1] = 255;
+					tmpData[tmpIdx+2] = 255;
+
+				}
+				else if (m_pData[i*m_iWidth+j] <= lowVal)
+				{			  
+					tmpData[tmpIdx+0] = 0;
+					tmpData[tmpIdx+1] = 0;
+					tmpData[tmpIdx+2] = 0;
+					//QRgb rgbVal = qRgb(0, 0, 0);
+					//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+				}
+				else
+				{
+					tmpData[tmpIdx+0] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal)/(double)winWidth * 255.0); //success
+					tmpData[tmpIdx+1] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal)/(double)winWidth * 255.0); //success
+					tmpData[tmpIdx+2] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal)/(double)winWidth * 255.0); //success				
+				}
+			}
+			else
+			{
+				if (m_pData[i*m_iWidth+j] >= uppVal)
+				{
+					//QRgb rgbVal = qRgb(255, 255, 255);
+					//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+					tmpData[tmpIdx+0] = 0;
+					tmpData[tmpIdx+1] = 0;
+					tmpData[tmpIdx+2] = 0;
+
+				}
+				else if (m_pData[i*m_iWidth+j] <= lowVal)
+				{
+					tmpData[tmpIdx+0] = 255;
+					tmpData[tmpIdx+1] = 255;
+					tmpData[tmpIdx+2] = 255;
+					//QRgb rgbVal = qRgb(0, 0, 0);
+					//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+				}
+				else
+				{
+					tmpData[tmpIdx+0] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal)/(double)winWidth * 255.0); //success
+					tmpData[tmpIdx+1] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal)/(double)winWidth * 255.0); //success
+					tmpData[tmpIdx+2] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal)/(double)winWidth * 255.0); //success				
+				}
+			}
+		}		
+	}	
+	///m_QImage.save("C:\\FromFillPixmap.png");//it works well
+
+	int iBytesPerLine = m_iWidth*3;        
+
+	QImage tmpQImage = QImage((unsigned char*)tmpData,m_iWidth, m_iHeight,iBytesPerLine, QImage::Format_RGB888); //not deep copy!
+
+
+	//image ratio (width / height) should be kept constant.
+	//PAN: center of image is moving
+
+	//m_QImage = tmpQImage.copy(0,0,m_iWidth, m_iHeight); //memory allocated here!!!
+
+	int newWidth = qRound(m_iWidth/m_fZoom);
+	int newHeight = qRound(m_iHeight/m_fZoom);
+
+	int centerX = m_iOffsetX + qRound(m_iWidth/2.0);
+	int centerY = m_iOffsetY + qRound(m_iHeight/2.0);
+
+	int newLeftTopX = centerX - qRound(newWidth/2.0);
+	int newLeftTopY = centerY - qRound(newHeight/2.0);
+
+	m_QImage = tmpQImage.copy(qRound(newLeftTopX), qRound(newLeftTopY), newWidth, newHeight); //memory allocated here!!!
+	
+
+	delete [] tmpData;
+	return true;
+}
+
+void YK16GrayImage::SetNormValueOriginal(float normval)
+{
+    m_fNormValue = normval;
+}
+
+
+//normval = Gy
+bool YK16GrayImage::FillPixMapDose()
+{
+    if (m_pData == NULL)
+        return false;
+
+    if (m_fNormValue <= 0.0)
+        return false;
+
+    int size = m_iWidth*m_iHeight;
+
+    uchar* tmpData = new uchar[size * 3];//RGB
+    unsigned short curVal = 0;
+    float originalVal = 0.0;
+    float percVal = 0.0;
+
+    for (int i = 0; i < m_iHeight; i++) //So long time....
+    {
+        for (int j = 0; j < m_iWidth; j++)
+        {
+            curVal = m_pData[i*m_iWidth + j];
+            originalVal = GetOriginalIntensityVal(curVal);
+            percVal = originalVal / m_fNormValue*100.0;
+
+            //use lookup table
+            QColor colorVal = GetColorFromDosePerc(percVal);
+
+            int tmpIdx = 3 * (i*m_iWidth + j);
+
+            tmpData[tmpIdx + 0] = colorVal.red();
+            tmpData[tmpIdx + 1] = colorVal.green();
+            tmpData[tmpIdx + 2] = colorVal.blue();
+        }
+    }
+
+    int iBytesPerLine = m_iWidth * 3;
+    QImage tmpQImage = QImage((unsigned char*)tmpData, m_iWidth, m_iHeight, iBytesPerLine, QImage::Format_RGB888); //not deep copy!
+
+
+    //image ratio (width / height) should be kept constant.
+    //PAN: center of image is moving
+
+    //m_QImage = tmpQImage.copy(0,0,m_iWidth, m_iHeight); //memory allocated here!!!
+
+    int newWidth = qRound(m_iWidth / m_fZoom);
+    int newHeight = qRound(m_iHeight / m_fZoom);
+
+    int centerX = m_iOffsetX + qRound(m_iWidth / 2.0);
+    int centerY = m_iOffsetY + qRound(m_iHeight / 2.0);
+
+    int newLeftTopX = centerX - qRound(newWidth / 2.0);
+    int newLeftTopY = centerY - qRound(newHeight / 2.0);
+
+    m_QImage = tmpQImage.copy(qRound(newLeftTopX), qRound(newLeftTopY), newWidth, newHeight); //memory allocated here!!!
+
+    delete[] tmpData;
+    return true;
+
+}
+
+//normval = Gy
+bool YK16GrayImage::FillPixMapDose(float normval)
+{
+    if (m_pData == NULL)
+        return false;
+
+    if (normval <= 0.0)
+        return false;
+
+    int size = m_iWidth*m_iHeight;
+
+    uchar* tmpData = new uchar[size * 3];//RGB
+    unsigned short curVal = 0;
+    float originalVal = 0.0;
+    float percVal = 0.0;
+
+    for (int i = 0; i < m_iHeight; i++) //So long time....
+    {
+        for (int j = 0; j < m_iWidth; j++)
+        {
+            curVal = m_pData[i*m_iWidth + j];
+            originalVal = GetOriginalIntensityVal(curVal);
+            percVal = originalVal / normval*100.0;
+
+            //use lookup table
+            QColor colorVal = GetColorFromDosePerc(percVal);
+
+            int tmpIdx = 3 * (i*m_iWidth + j);
+
+            tmpData[tmpIdx + 0] = colorVal.red();
+            tmpData[tmpIdx + 1] = colorVal.green();
+            tmpData[tmpIdx + 2] = colorVal.blue();
+        }
+    }    
+
+    int iBytesPerLine = m_iWidth * 3;
+    QImage tmpQImage = QImage((unsigned char*)tmpData, m_iWidth, m_iHeight, iBytesPerLine, QImage::Format_RGB888); //not deep copy!
+
+
+    //image ratio (width / height) should be kept constant.
+    //PAN: center of image is moving
+
+    //m_QImage = tmpQImage.copy(0,0,m_iWidth, m_iHeight); //memory allocated here!!!
+
+    int newWidth = qRound(m_iWidth / m_fZoom);
+    int newHeight = qRound(m_iHeight / m_fZoom);
+
+    int centerX = m_iOffsetX + qRound(m_iWidth / 2.0);
+    int centerY = m_iOffsetY + qRound(m_iHeight / 2.0);
+
+    int newLeftTopX = centerX - qRound(newWidth / 2.0);
+    int newLeftTopY = centerY - qRound(newHeight / 2.0);
+
+    m_QImage = tmpQImage.copy(qRound(newLeftTopX), qRound(newLeftTopY), newWidth, newHeight); //memory allocated here!!!
+
+    delete[] tmpData;
+    return true;
+
+}
+
+bool YK16GrayImage::FillPixMapGamma()
+{
+    if (m_pData == NULL)
+        return false;
+
+    int size = m_iWidth*m_iHeight;
+
+    uchar* tmpData = new uchar[size * 3];//RGB
+    unsigned short curVal = 0;
+    float originalVal = 0.0;
+    float percVal = 0.0;
+
+    for (int i = 0; i < m_iHeight; i++) //So long time....
+    {
+        for (int j = 0; j < m_iWidth; j++)
+        {
+                 if (i == 10 && j == qRound(m_iWidth / 2.0))
+                     int aa = 5;
+            curVal = m_pData[i*m_iWidth + j];
+            originalVal = GetOriginalIntensityVal(curVal);            
+
+            //use lookup table
+            QColor colorVal = GetColorFromGamma(originalVal);
+
+            //originalVal is always 0 or 1 now
+
+           /* if (originalVal > 0 && originalVal < 1)
+            {
+                cout << "number is = " << originalVal << " " << curVal << " " << m_fIntensityMag << endl;
+            }*/
+                
+
+
+
+            int tmpIdx = 3 * (i*m_iWidth + j);
+
+            tmpData[tmpIdx + 0] = colorVal.red();
+            tmpData[tmpIdx + 1] = colorVal.green();
+            tmpData[tmpIdx + 2] = colorVal.blue();
+        }
+    }
+
+    int iBytesPerLine = m_iWidth * 3;
+    QImage tmpQImage = QImage((unsigned char*)tmpData, m_iWidth, m_iHeight, iBytesPerLine, QImage::Format_RGB888); //not deep copy!
+
+
+    //image ratio (width / height) should be kept constant.
+    //PAN: center of image is moving
+
+    //m_QImage = tmpQImage.copy(0,0,m_iWidth, m_iHeight); //memory allocated here!!!
+
+    int newWidth = qRound(m_iWidth / m_fZoom);
+    int newHeight = qRound(m_iHeight / m_fZoom);
+
+    int centerX = m_iOffsetX + qRound(m_iWidth / 2.0);
+    int centerY = m_iOffsetY + qRound(m_iHeight / 2.0);
+
+    int newLeftTopX = centerX - qRound(newWidth / 2.0);
+    int newLeftTopY = centerY - qRound(newHeight / 2.0);
+
+    m_QImage = tmpQImage.copy(qRound(newLeftTopX), qRound(newLeftTopY), newWidth, newHeight); //memory allocated here!!!
+
+    delete[] tmpData;
+    return true;
+}
+
+
+bool YK16GrayImage::FillPixMapMinMax(int winMin, int winMax) //0-65535 �� window level
+{
+	if (winMin < 0 || winMax > 65535 || winMin > winMax)
+	{
+		winMin = 0;
+		winMax = 65535;
+	}
+
+	int midVal = (int)((winMin + winMax)/2.0);
+	int widthVal = winMax - winMin;        
+
+	return FillPixMap(midVal, widthVal);	
+}
+
+bool YK16GrayImage::SaveDataAsRaw (const char *filePath) //save 16 bit gray raw file
+{
+	if (m_pData == NULL)
+		return false;
+
+	int imgSize = m_iWidth*m_iHeight;		
+
+	FILE* fd = NULL;
+	fd = fopen(filePath, "wb");
+
+	for (int i = 0 ; i<imgSize ; i++)
+	{		
+		fwrite(&m_pData[i], 2, 1, fd);		
+	}	
+
+	fclose(fd);
+	return true;
+}
+//
+//bool YK16GrayImage::DrawToLabel( QLabel* lbDisplay ) //using pixmap
+//{
+//	if (m_pPixmap == NULL)
+//		return false;
+//
+//	int width = lbDisplay->width();
+//	int height = lbDisplay->height();
+//
+//	double ratio =m_pPixmap->width() / (double)width;
+//	int newHeight = m_pPixmap->height() / ratio;
+//
+//	//m_pPixmap->scaled(wid,showHeght,Qt::IgnoreAspectRatio)
+//	//lbDisplay->setPixmap(m_pPixmap->scaled(width, height, Qt::IgnoreAspectRatio));
+//	lbDisplay->setPixmap(m_pPixmap->scaled(width, newHeight, Qt::IgnoreAspectRatio));
+//
+//	/*int w = m_QImage.width();
+//	int h = m_QImage.height();*/
+//	//bool result = m_QImage.save("C:\\abcdefg.png");
+//
+//	return true;
+//}
+
+//
+//bool YK16GrayImage::CalcImageInfo (double& meanVal, double& STDV, double& minVal, double& maxVal)
+//{
+//	if (m_pData == NULL)
+//		return false;
+//
+//	int nTotal;
+//	long minPixel, maxPixel;
+//	int i;
+//	double pixel, sumPixel;
+//
+//	int npixels = m_iWidth*m_iHeight;
+//	nTotal = 0;
+//	//minPixel = 4095;
+//	minPixel = 65535;
+//	maxPixel = 0;
+//	sumPixel = 0.0;
+//
+//	for (i = 0; i < npixels; i++)
+//	{
+//		pixel = (double) m_pData[i];
+//		sumPixel += pixel;
+//		if (m_pData[i] > maxPixel)
+//			maxPixel = m_pData[i];
+//		if (m_pData[i] < minPixel)
+//			minPixel = m_pData[i];
+//		nTotal++;
+//	}
+//
+//	double meanPixelval = sumPixel / (double)nTotal;    
+//
+//	double sqrSum = 0.0;
+//	for (i = 0; i < npixels; i++)
+//	{
+//		sqrSum = sqrSum + pow(((double)m_pData[i] - meanPixelval),2.0);
+//	}
+//	double SD = sqrt(sqrSum/(double)nTotal);
+//
+//	meanVal = meanPixelval;
+//	STDV = SD;
+//	minVal = minPixel;
+//	maxVal = maxPixel;
+//
+//	return true;
+//}
+//
+
+
+
+ 
+bool YK16GrayImage::CalcImageInfo ()
+{
+	if (m_pData == NULL)
+		return false;
+
+	
+
+	int nTotal;
+	long minPixel, maxPixel;
+	int i;
+	double pixel, sumPixel;
+
+	int npixels = m_iWidth*m_iHeight;
+
+	if (npixels <= 0)
+	  return false;
+
+	nTotal = 0;
+	//minPixel = 4095;
+	minPixel = 65535;
+	maxPixel = 0;
+	sumPixel = 0.0;
+
+	for (i = 0; i < npixels; i++)
+	{
+		pixel = (double) m_pData[i];
+		sumPixel += pixel;
+		if (m_pData[i] > maxPixel)
+			maxPixel = m_pData[i];
+		if (m_pData[i] < minPixel)
+			minPixel = m_pData[i];
+		nTotal++;
+	}
+
+	double meanPixelval = 0.0;
+
+	if (nTotal <= 0)
+	  meanPixelval = 0.0;
+	else
+	  meanPixelval = sumPixel / (double)nTotal;
+
+	double sqrSum = 0.0;
+	for (i = 0; i < npixels; i++)
+	{
+		sqrSum = sqrSum + pow(((double)m_pData[i] - meanPixelval),2.0);
+	}
+	double SD = sqrt(sqrSum/(double)nTotal);
+
+	m_fPixelMean = meanPixelval;
+	m_fPixelSD = SD;
+	m_fPixelMin = minPixel;
+	m_fPixelMax = maxPixel;
+
+	return true;
+}
+
+
+double YK16GrayImage::CalcAveragePixelDiff(YK16GrayImage& other)
+{
+	if (m_pData == NULL || other.m_pData == NULL)
+		return 0.0;
+
+	int totalPixCnt = m_iWidth * m_iHeight;
+	double tmpSum = 0.0;
+	for (int i = 0 ; i<totalPixCnt ; i++)
+	{
+		tmpSum = tmpSum + fabs((double)m_pData[i] - (double)other.m_pData[i]);
+	}
+
+	return tmpSum / (double)totalPixCnt;
+}
+
+//bool YK16GrayImage::DoPixelReplacement(vector<BADPIXELMAP>& vPixelMapping )
+//{
+//	if (vPixelMapping.empty())
+//		return false;		
+//
+//	if (m_pData == NULL)
+//		return false;
+//
+//
+//
+//	int oriIdx, replIdx;
+//
+//	vector<BADPIXELMAP>::iterator it;
+//
+//	for (it = vPixelMapping.begin() ; it != vPixelMapping.end() ; it++)
+//	{
+//		BADPIXELMAP tmpData= (*it);
+//		oriIdx = tmpData.BadPixY * m_iWidth + tmpData.BadPixX;
+//		replIdx = tmpData.ReplPixY * m_iWidth + tmpData.ReplPixX;
+//		m_pData[oriIdx] = m_pData[replIdx];
+//	}
+//
+//
+//
+//	return true;
+//}
+
+
+
+//
+//void YK16GrayImage::CopyYKImage2ItkImage(YK16GrayImage* pYKImage, UnsignedShortImageType::Pointer& spTarImage)
+//{
+//	if (pYKImage == NULL)
+//		return;
+//	//Raw File open	
+//	//UnsignedShortImageType::SizeType tmpSize = 
+//	UnsignedShortImageType::RegionType region = spTarImage->GetRequestedRegion();
+//	UnsignedShortImageType::SizeType tmpSize = region.GetSize();
+//
+//	int sizeX = tmpSize[0];
+//	int sizeY = tmpSize[1];
+//
+//	if (sizeX < 1 || sizeY <1)
+//		return;
+//
+//	itk::ImageRegionIterator<UnsignedShortImageType> it(spTarImage, region);
+//
+//	int i = 0;
+//	for (it.GoToBegin() ; !it.IsAtEnd(); it++)
+//	{
+//		it.Set(pYKImage->m_pData[i]);
+//		i++;
+//	}
+//	//int totCnt = i;
+//	//writerType::Pointer writer = writerType::New();	
+//	//writer->SetInput(spTarImage);	
+//	//writer->SetFileName("C:\\ThisImageIs_spSrcImage.png");	//It works!
+//	//writer->Update();
+//}
+//void YK16GrayImage::CopyItkImage2YKImage(UnsignedShortImageType::Pointer& spSrcImage, YK16GrayImage* pYKImage)
+//{
+//	if (pYKImage == NULL)
+//		return;
+//	//Raw File open	
+//	//UnsignedShortImageType::SizeType tmpSize = 
+//	UnsignedShortImageType::RegionType region = spSrcImage->GetRequestedRegion();
+//	UnsignedShortImageType::SizeType tmpSize = region.GetSize();
+//
+//	int sizeX = tmpSize[0];
+//	int sizeY = tmpSize[1];
+//
+//	if (sizeX < 1 || sizeY <1)
+//		return;
+//
+//	//itk::ImageRegionConstIterator<UnsignedShortImageType> it(spSrcImage, region);
+//	itk::ImageRegionIterator<UnsignedShortImageType> it(spSrcImage, region);
+//
+//	int i = 0;
+//	for (it.GoToBegin() ; !it.IsAtEnd() ; it++)
+//	{
+//		pYKImage->m_pData[i] = it.Get();
+//		i++;
+//	}
+//	//int totCnt = i; //Total Count is OK
+//
+//	//int width = pYKImage->m_iWidth;
+//	//int height = pYKImage->m_iHeight;
+//
+//
+//	//writerType::Pointer writer = writerType::New();	
+//	//writer->SetInput(spSrcImage);	
+//	//writer->SetFileName("C:\\ThisImageIs_spSrcImage2.png");	//It works!
+//	//writer->Update();
+//}
+
+
+void YK16GrayImage::Swap(YK16GrayImage* pImgA, YK16GrayImage* pImgB)
+{
+	if (pImgA == NULL || pImgB == NULL )
+		return;
+
+	if (pImgA->IsEmpty() || pImgB->IsEmpty() )
+		return;
+
+	YK16GrayImage tmpImg(pImgA->m_iWidth, pImgB->m_iHeight);
+	tmpImg.CopyFromBuffer(pImgA->m_pData,pImgA->m_iWidth, pImgA->m_iHeight);
+	tmpImg.m_strFilePath = pImgA->m_strFilePath;
+
+	pImgA->CopyFromBuffer(pImgB->m_pData,pImgB->m_iWidth, pImgB->m_iHeight);
+	pImgA->m_strFilePath = pImgB->m_strFilePath;
+
+
+	pImgB->CopyFromBuffer(tmpImg.m_pData,tmpImg.m_iWidth, tmpImg.m_iHeight);
+	pImgB->m_strFilePath = tmpImg.m_strFilePath;
+}
+
+
+
+bool YK16GrayImage::SaveDataAsHis( const char *filePath, bool bInverse )
+{
+	if (m_pData == NULL)
+		return false;
+
+	if (m_pElektaHisHeader ==NULL)
+		return false;
+
+
+	FILE* fd = NULL;
+	fd = fopen(filePath, "wb");
+
+	fwrite(m_pElektaHisHeader, 100, 1, fd);
+
+	int imgSize = m_iWidth*m_iHeight;
+
+	for (int i = 0 ; i<imgSize ; i++)
+	{	
+		unsigned short tmpVal =0;
+		
+		if (bInverse)
+			tmpVal = 65535 - m_pData[i];
+		else
+			tmpVal = m_pData[i];			
+
+		fwrite(&tmpVal, 2, 1, fd);		
+	}	
+
+	fclose(fd);
+	
+	return true;
+
+}
+
+void YK16GrayImage::CopyHisHeader( const char *hisFilePath )
+{
+	// open file
+	std::ifstream file(hisFilePath, std::ios::in | std::ios::binary);
+
+	if ( file.fail() )
+		cout << "Fail to open" << "	" << hisFilePath << endl;		
+
+	// read header
+	if (m_pElektaHisHeader != NULL)
+		delete [] m_pElektaHisHeader;
+
+	m_pElektaHisHeader = new char [DEFAULT_ELEKTA_HIS_HEADER_SIZE]; // DEFAULT_ELEKTA_HIS_HEADER_SIZE = 100
+	file.read(m_pElektaHisHeader, DEFAULT_ELEKTA_HIS_HEADER_SIZE);	
+}
+//
+//void YK16GrayImage::Swap(YK16GrayImage* pImgA, YK16GrayImage* pImgB)
+//{
+//	if (pImgA == NULL || pImgB == NULL )
+//		return;
+//
+//	if (pImgA->IsEmpty() || pImgB->IsEmpty() )
+//		return;
+//
+//	YK16GrayImage tmpImg(pImgA->m_iWidth, pImgB->m_iHeight);
+//	tmpImg.CopyFromBuffer(pImgA->m_pData,pImgA->m_iWidth, pImgA->m_iHeight);
+//	tmpImg.m_strFilePath = pImgA->m_strFilePath;
+//
+//	pImgA->CopyFromBuffer(pImgB->m_pData,pImgB->m_iWidth, pImgB->m_iHeight);
+//	pImgA->m_strFilePath = pImgB->m_strFilePath;
+//
+//
+//	pImgB->CopyFromBuffer(tmpImg.m_pData,tmpImg.m_iWidth, tmpImg.m_iHeight);
+//	pImgB->m_strFilePath = tmpImg.m_strFilePath;
+//}
+
+
+
+
+void YK16GrayImage::CopyYKImage2ItkImage(YK16GrayImage* pYKImage, UnsignedShortImageType::Pointer& spTarImage)
+{
+	if (pYKImage == NULL)
+		return;
+	//Raw File open	
+	//UnsignedShortImageType::SizeType tmpSize = 
+	UnsignedShortImageType::RegionType region = spTarImage->GetRequestedRegion();
+	UnsignedShortImageType::SizeType tmpSize = region.GetSize();
+
+	int sizeX = tmpSize[0];
+	int sizeY = tmpSize[1];
+
+	if (sizeX < 1 || sizeY <1)
+		return;
+
+
+	if (sizeX != pYKImage->m_iWidth || sizeY != pYKImage->m_iHeight)
+	{
+		cout << "ERRROR! IN CopyYKImage2ItkImage. Image size should be matched" << endl;
+		return;
+	}
+
+
+	itk::ImageRegionIterator<UnsignedShortImageType> it(spTarImage, region);
+
+	int i = 0;
+	for (it.GoToBegin() ; !it.IsAtEnd(); ++it)
+	{
+		it.Set(pYKImage->m_pData[i]);
+		i++;
+	}
+
+	UnsignedShortImageType::SpacingType sp;
+	sp[0] = pYKImage->m_fSpacingX;
+	sp[1] = pYKImage->m_fSpacingY;
+	spTarImage->SetSpacing(sp);	
+	//int totCnt = i;
+	//writerType::Pointer writer = writerType::New();	
+	//writer->SetInput(spTarImage);	
+	//writer->SetFileName("C:\\ThisImageIs_spSrcImage.png");	//It works!
+	//writer->Update();
+}
+void YK16GrayImage::CopyItkImage2YKImage(UnsignedShortImageType::Pointer& spSrcImage, YK16GrayImage* pYKImage)
+{
+	if (pYKImage == NULL)
+		return;
+
+	
+	//Raw File open	
+	//UnsignedShortImageType::SizeType tmpSize = 
+	UnsignedShortImageType::RegionType region = spSrcImage->GetRequestedRegion();
+	UnsignedShortImageType::SizeType tmpSize = region.GetSize();
+
+	int sizeX = tmpSize[0];
+	int sizeY = tmpSize[1];
+
+	if (sizeX < 1 || sizeY <1)
+		return;
+
+	if (sizeX != pYKImage->m_iWidth || sizeY != pYKImage->m_iHeight)
+	{
+		cout << "ERRROR! IN CopyItkImage2YKImage. Image size should be matched" << endl;
+		return;
+	}
+	
+
+	//itk::ImageRegionConstIterator<UnsignedShortImageType> it(spSrcImage, region);
+	itk::ImageRegionIterator<UnsignedShortImageType> it(spSrcImage, region);
+
+	int i = 0;
+	for (it.GoToBegin() ; !it.IsAtEnd() ; ++it)
+	{
+		pYKImage->m_pData[i] = it.Get();
+		i++;
+	}
+
+	pYKImage->SetSpacing(spSrcImage->GetSpacing()[0], spSrcImage->GetSpacing()[1]);
+
+	//int totCnt = i; //Total Count is OK
+
+	//int width = pYKImage->m_iWidth;
+	//int height = pYKImage->m_iHeight;
+
+
+	//writerType::Pointer writer = writerType::New();	
+	//writer->SetInput(spSrcImage);	
+	//writer->SetFileName("C:\\ThisImageIs_spSrcImage2.png");	//It works!
+	//writer->Update();
+}
+
+bool YK16GrayImage::CalcImageInfo_ROI()
+{
+	if (m_pData == NULL)
+	{		
+		m_fPixelMean_ROI =-1.0;
+		m_fPixelSD_ROI=-1.0;
+		m_fPixelMin_ROI=-1.0;
+		m_fPixelMax_ROI=-1.0;
+		return false;
+	}
+
+	if (m_rtROI.width() < 1 || m_rtROI.height() < 1)
+	{
+		m_fPixelMean_ROI =-1.0;
+		m_fPixelSD_ROI=-1.0;
+		m_fPixelMin_ROI=-1.0;
+		m_fPixelMax_ROI=-1.0;
+		return false;
+	}		
+
+	int nTotal;
+	long minPixel, maxPixel;
+	
+	double pixel, sumPixel;
+
+	int npixels = m_iWidth*m_iWidth;
+	nTotal = 0;
+	//minPixel = 4095;
+	minPixel = 65535;
+	maxPixel = 0;
+	sumPixel = 0.0;
+
+
+	int i,j;
+
+	for (i =m_rtROI.top()  ; i<m_rtROI.bottom() ; i++)
+	{
+		for (j=m_rtROI.left() ; j < m_rtROI.right() ; j++)
+		{
+			int idx = m_iWidth*i + j;
+			pixel = (double) m_pData[idx];
+			sumPixel += pixel;
+			if (m_pData[idx] > maxPixel)
+				maxPixel = m_pData[idx];
+			if (m_pData[idx] < minPixel)
+				minPixel = m_pData[idx];
+			nTotal++;			
+		}
+	}
+
+	double meanPixelval = sumPixel / (double)nTotal;    
+
+	double sqrSum = 0.0;
+	/*for (i = 0; i < nTotal; i++)
+	{
+		sqrSum = sqrSum + pow(((double)m_pData[i] - meanPixelval),2.0);
+	}*/
+
+	for (i =m_rtROI.top()  ; i<m_rtROI.bottom() ; i++)
+	{
+		for (j=m_rtROI.left() ; j < m_rtROI.right() ; j++)
+		{
+			int idx = m_iWidth*i + j;
+			sqrSum = sqrSum + pow(((double)m_pData[idx] - meanPixelval),2.0);
+		}
+	}
+
+	double SD = sqrt(sqrSum/(double)nTotal);
+
+	m_fPixelMean_ROI = meanPixelval;
+	m_fPixelSD_ROI = SD;
+	m_fPixelMin_ROI = minPixel;
+	m_fPixelMax_ROI = maxPixel;
+
+	return true;
+}
+
+bool YK16GrayImage::setROI( int left, int top, int right, int bottom )
+{	
+	
+	if (left >= right || top >= bottom || left < 0 || right > m_iWidth-1 || top <0 || bottom > m_iHeight-1)
+	{
+		m_rtROI.setLeft(0);
+		m_rtROI.setTop(0);
+		m_rtROI.setRight(m_iWidth-1);
+		m_rtROI.setBottom(m_iHeight-1);
+		return false;
+	}
+	m_rtROI.setLeft(left);
+	m_rtROI.setTop(top);
+	m_rtROI.setRight(right);
+	m_rtROI.setBottom(bottom);
+	return true;
+}
+
+void YK16GrayImage::DrawROIOn( bool bROI_Draw )
+{	
+	m_bDrawROI = bROI_Draw;
+}
+
+bool YK16GrayImage::CloneImage( YK16GrayImage& other )
+{
+	if (other.m_pData == NULL)
+		return false;
+
+	//first, delete existing things
+	ReleaseBuffer(); //Redundancy. CreateImage will call this too. also Create is calling this func. 
+
+	int width = other.m_iWidth;
+	int height = other.m_iHeight;
+
+	CreateImage(width, height, 0);
+	CopyFromBuffer(other.m_pData, width, height);
+
+	if (other.m_pElektaHisHeader != NULL)
+	{
+	  m_pElektaHisHeader = new char [DEFAULT_ELEKTA_HIS_HEADER_SIZE]; 
+	  for (int i = 0 ; i<DEFAULT_ELEKTA_HIS_HEADER_SIZE ; i++ )
+	  {
+		m_pElektaHisHeader[i] = other.m_pElektaHisHeader[i];
+	  }
+	}
+
+	m_strFilePath = other.m_strFilePath;
+
+	m_fPixelMean= other.m_fPixelMean;
+	m_fPixelSD= other.m_fPixelSD;
+	m_fPixelMin= other.m_fPixelMin;
+	m_fPixelMax= other.m_fPixelMax;
+
+	m_rtROI = other.m_rtROI;	
+	m_fPixelMean_ROI=other.m_fPixelMean_ROI;
+	m_fPixelSD_ROI=other.m_fPixelSD_ROI;
+	m_fPixelMin_ROI=other.m_fPixelMin_ROI;
+	m_fPixelMax_ROI=other.m_fPixelMax_ROI;
+	m_bDrawROI= other.m_bDrawROI;	
+	m_QImage = other.m_QImage;
+	m_bShowInvert = other.m_bShowInvert;
+
+	SetSpacing(other.m_fSpacingX, other.m_fSpacingY);
+
+	m_ptProfileProbe = other.m_ptProfileProbe;
+	m_bDrawProfileX = other.m_bDrawProfileX;
+	m_bDrawProfileY = other.m_bDrawProfileY;
+
+	m_ptFOVCenter = other.m_ptFOVCenter; // data pos
+	m_iFOVRadius = other.m_iFOVRadius;//data pos (pixel)
+	m_bDrawFOVCircle = other.m_bDrawFOVCircle;
+
+	m_iTableTopPos = other.m_iTableTopPos;//data pos
+	m_bDrawTableLine = other.m_bDrawTableLine;
+
+	m_ptCrosshair = other.m_ptCrosshair; //data position
+	m_bDrawCrosshair = other.m_bDrawCrosshair;
+
+	m_ptSplitCenter = other.m_ptSplitCenter; //Fixed image with Moving image. center is based on dataPt.//Fixed Image: Left Top + Right Bottom, Moving: Right Top + Left Bottom
+	m_enSplitOption = other.m_enSplitOption;	
+
+	m_iOffsetX = other.m_iOffsetX;
+	m_iOffsetY = other.m_iOffsetY;
+	m_fZoom =other.m_fZoom;	
+
+	m_fResampleFactor = other.m_fResampleFactor;//i
+
+	m_strTimeStamp = other.m_strTimeStamp; //HHMMSSFFF (9digits)
+	m_iProcessingElapsed = other.m_iProcessingElapsed; //processing time in ms	
+
+	m_vMarker = other.m_vMarker;//position in data dimension (not either physical nor display)	
+	m_bDrawMarkers = other.m_bDrawMarkers;
+	m_iNumOfDispMarker = other.m_iNumOfDispMarker; //for display      e.g.)  0,1,2,3,4
+	m_iNumOfTrackMarker = other.m_iNumOfTrackMarker; //for tracking   GetMeanPos  e.g.)  0,1,2 only	
+	
+	m_vMarkerRef = other.m_vMarkerRef;//position in data dimension (not either physical nor display)
+	m_bvRefOutOfRange = other.m_bvRefOutOfRange;//leave a tag when calculated 2D ref position is out of image	
+	m_bDrawMarkersRef = other.m_bDrawMarkersRef;
+
+	//should be implemented later
+	m_track_priorErr = other.m_track_priorErr;
+	m_track_motionErr = other.m_track_motionErr;
+	m_track_CC_penalty = other.m_track_CC_penalty;
+
+	m_fMVGantryAngle = other.m_fMVGantryAngle;
+	m_fPanelOffsetX = other.m_fPanelOffsetX; //mm
+	m_fPanelOffsetY = other.m_fPanelOffsetY;//mm
+	m_bKVOn = other.m_bKVOn;
+	m_bMVOn = other.m_bMVOn;
+	m_iXVI_ElapseMS = other.m_iXVI_ElapseMS;
+
+	m_bDraw8Bit = other.m_bDraw8Bit;
+
+     return true;	
+}
+
+void YK16GrayImage::MultiplyConstant( double multiplyFactor )
+{
+	if (m_pData == NULL)
+		return;
+
+	for (int i = 0 ; i < m_iHeight ; i++)
+	{
+		for (int j = 0 ; j<m_iWidth ; j++)
+		{
+			m_pData[m_iWidth*i + j] = (unsigned short)(((double)m_pData[m_iWidth*i + j]) * multiplyFactor);
+		}
+	}
+
+}
+
+void YK16GrayImage::SetProfileProbePos( int dataX, int dataY )
+{
+	if (m_ptProfileProbe.y() >= 0 && m_ptProfileProbe.y() < m_iHeight &&
+		m_ptProfileProbe.x() >= 0 && m_ptProfileProbe.x() < m_iWidth)
+	{
+		m_ptProfileProbe.setX(dataX);
+		m_ptProfileProbe.setY(dataY);
+	}
+	else
+	{
+		m_ptProfileProbe.setX(0);
+		m_ptProfileProbe.setY(0);
+	}
+	
+
+}
+
+unsigned short YK16GrayImage::GetProfileProbePixelVal()
+{
+	unsigned short resultVal = 0;
+	if (m_pData == NULL)
+		return 0;
+
+	if (m_ptProfileProbe.y() >= 0 && m_ptProfileProbe.y() < m_iHeight &&
+		m_ptProfileProbe.x() >= 0 && m_ptProfileProbe.x() < m_iWidth)
+		resultVal = m_pData[m_iWidth*m_ptProfileProbe.y() + m_ptProfileProbe.x()];
+	else
+		resultVal = 0;		
+
+        return resultVal;
+}
+
+void YK16GrayImage::GetProfileData( int dataX, int dataY, QVector<double>& vTarget, enProfileDirection direction )
+{
+	if (m_pData == NULL)
+		return;
+
+	if (dataY < 0 || dataY >= m_iHeight ||
+		dataX < 0 || dataX >= m_iWidth)
+		return;
+
+	vTarget.clear();
+
+	if (direction == DIRECTION_HOR)
+	{
+		int fixedY  = dataY;
+		for (int j = 0 ; j< m_iWidth ; j++)
+		{
+			vTarget.push_back(m_pData[m_iWidth*fixedY + j]);
+		}
+	}
+	else if (direction == DIRECTION_VER)
+	{
+		//Upper to Lower profile
+
+		int fixedX  = dataX;
+		for (int i = 0 ; i< m_iHeight ; i++)
+		{
+			vTarget.push_back(m_pData[m_iWidth*i + fixedX]);
+		}
+	}
+
+        return;
+}
+void YK16GrayImage::GetProfileData(QVector<double>& vTarget, enProfileDirection direction )
+{
+	if (m_pData == NULL)
+		return;
+
+	int dataX = m_ptProfileProbe.x();
+	int dataY = m_ptProfileProbe.y();
+
+	if (dataY < 0 || dataY >= m_iHeight ||
+		dataX < 0 || dataX >= m_iWidth)
+		return;
+
+	vTarget.clear();
+
+	if (direction == DIRECTION_HOR)
+	{
+		int fixedY  = dataY;
+		for (int j = 0 ; j< m_iWidth ; j++)
+		{
+			vTarget.push_back((double)(m_pData[m_iWidth*fixedY + j]));
+		}
+	}
+	else if (direction == DIRECTION_VER)
+	{
+		//Upper to Lower profile
+
+		int fixedX  = dataX;
+		for (int i = 0 ; i< m_iHeight ; i++)
+		{
+			vTarget.push_back((double)(m_pData[m_iWidth*i + fixedX]));
+		}
+	}
+
+}
+
+bool YK16GrayImage::ConstituteFromTwo( YK16GrayImage& YKImg1,YK16GrayImage& YKImg2 )
+{
+    //Filtering
+    if (YKImg1.IsEmpty() || YKImg2.IsEmpty() || YKImg1.m_iWidth != YKImg2.m_iWidth || YKImg1.m_iHeight != YKImg2.m_iHeight
+        || YKImg1.m_iWidth * YKImg1.m_iHeight == 0)
+        return false;
+
+
+    if (m_enSplitOption < 0)
+    {
+        cout << "ConstituteFromTwo cannot be carried out due to the wrong split option" << endl;
+        return false;
+    }
+
+    int width = YKImg1.m_iWidth;
+    int height = YKImg1.m_iHeight;
+
+    int centerX = m_ptSplitCenter.x(); //data index
+    int centerY = m_ptSplitCenter.y();
+
+    //bool bPreserveInfo = true;
+    //CreateImage(width, height, 0, bPreserveInfo);//tracking data should be reserved
+    CloneImage(YKImg1); //copy all the tracking data as well //EXCEPT for Split center!!!!
+
+    //restore split center after clone
+    m_ptSplitCenter.setX(centerX);
+    m_ptSplitCenter.setY(centerY);
+
+    int i, j;
+    switch (m_enSplitOption)
+    {
+    case PRI_LEFT_TOP:
+        for (i = 0; i < centerY; i++)
+        {
+            for (j = 0; j < centerX; j++)
+            {
+                m_pData[width*i + j] = YKImg1.m_pData[width*i + j];
+            }
+        }
+
+        for (i = centerY; i < height; i++)
+        {
+            for (j = centerX; j < width; j++)
+            {
+                m_pData[width*i + j] = YKImg1.m_pData[width*i + j];
+            }
+        }
+
+        for (i = 0; i < centerY; i++)
+        {
+            for (j = centerX; j < width; j++)
+            {
+                m_pData[width*i + j] = YKImg2.m_pData[width*i + j];
+            }
+        }
+
+        for (i = centerY; i < height; i++)
+        {
+            for (j = 0; j < centerX; j++)
+            {
+                m_pData[width*i + j] = YKImg2.m_pData[width*i + j];
+            }
+        }
+
+        break;
+    default:
+        break;
+    }
+
+    return true;
+}
+
+void YK16GrayImage::EditImage_Flip()
+{
+  if (m_pData == NULL)
+	return;  
+
+  int imgSize = m_iWidth*m_iHeight;
+
+  if (imgSize <=0)
+	return;
+
+  //������ �ӽ� buffer ����
+  unsigned short* pPrevImg = new unsigned short [imgSize]; 
+
+  int i,j;
+
+  for (i = 0 ; i<m_iHeight ; i++)
+  {
+	for (j = 0 ; j<m_iWidth ; j++)
+	{	  
+		pPrevImg[i*m_iWidth + j] = m_pData[i*m_iWidth + j];				 
+	}
+  }
+
+  for (i = 0 ; i<m_iHeight ; i++)
+  {
+	for (j = 0 ; j<m_iWidth ; j++)
+	{
+	  int tmpX = j;
+	  int tmpY = m_iHeight-i-1;	  
+
+	  m_pData[i*m_iWidth + j] = pPrevImg[tmpY*m_iWidth + tmpX];			
+	}
+  }
+
+  delete [] pPrevImg;
+  return;
+}
+
+void YK16GrayImage::EditImage_Mirror()
+{
+  if (m_pData == NULL)
+	return;  
+
+  int imgSize = m_iWidth*m_iHeight;
+
+  if (imgSize <= 0)
+	return;
+
+  int i = 0 ; int j = 0;  
+
+  unsigned short* pPrevImg = new unsigned short [imgSize];  
+  
+
+  //��ȯ �� �̹����� copy
+
+  for (i = 0 ; i<m_iHeight ; i++)
+  {
+	for (j = 0 ; j<m_iWidth ; j++)
+	{	  
+		pPrevImg[i*m_iWidth + j] = m_pData[i*m_iWidth + j];	  
+	}
+  }
+
+  for (i = 0 ; i<m_iHeight ; i++)
+  {
+	for (j = 0 ; j<m_iWidth ; j++)
+	{
+	  int tmpX = m_iWidth-j-1;
+	  int tmpY = i;
+	  
+	  m_pData[i*m_iWidth + j] = pPrevImg[tmpY*m_iWidth + tmpX];	  
+	}
+  }
+
+  delete [] pPrevImg;
+
+  return;
+}
+
+bool YK16GrayImage::FillPixMapDual( int winMid1, int winMid2,int winWidth1, int winWidth2 )
+{
+  if (m_pData == NULL)
+		return false;
+
+	if (m_pPixmap != NULL)
+	{		                
+		delete m_pPixmap;                
+		m_pPixmap = NULL;                
+	}	
+	m_pPixmap = new QPixmap(QSize(m_iWidth,m_iHeight)); //something happened here!!!: w: 4289140  h: 0	        
+
+	//8 bit gray buffer preparing
+	int size = m_iWidth*m_iHeight;
+
+	uchar* tmpData = new uchar [size*3];//RGB
+
+	int uppVal1 = (int)(winMid1 + winWidth1/2.0);
+	int lowVal1 = (int)(winMid1 - winWidth1/2.0);	        
+
+	int uppVal2 = (int)(winMid2 + winWidth2/2.0);
+	int lowVal2 = (int)(winMid2 - winWidth2/2.0);
+
+
+	if (uppVal1 > 65535)
+	  uppVal1 = 65535;
+	if (uppVal2 > 65535)
+	  uppVal2 = 65535;
+
+	if (lowVal1 <= 0)
+	  lowVal1 = 0;
+	if (uppVal2 <= 0)
+	  uppVal2 = 0;
+
+	//It takes 0.4 s in Release mode
+
+	if (m_enSplitOption != PRI_LEFT_TOP)
+	  return false;
+
+
+	int splitX = m_ptSplitCenter.x();
+	int splitY = m_ptSplitCenter.y();
+
+
+	//1/4 sector
+	for (int i = 0 ; i<splitY ; i++) //So long time....
+	{
+		for (int j = 0 ; j< splitX ; j++)
+		{
+			int tmpIdx = 3*(i*m_iWidth+j);
+
+			if (!m_bShowInvert)
+			{
+				if (m_pData[i*m_iWidth+j] >= uppVal1)
+				{
+					//QRgb rgbVal = qRgb(255, 255, 255);
+					//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+					tmpData[tmpIdx+0] = 255;
+					tmpData[tmpIdx+1] = 255;
+					tmpData[tmpIdx+2] = 255;
+
+				}
+				else if (m_pData[i*m_iWidth+j] <= lowVal1)
+				{
+					tmpData[tmpIdx+0] = 0;
+					tmpData[tmpIdx+1] = 0;
+					tmpData[tmpIdx+2] = 0;
+					//QRgb rgbVal = qRgb(0, 0, 0);
+					//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+				}
+				else
+				{
+					tmpData[tmpIdx+0] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success
+					tmpData[tmpIdx+1] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success
+					tmpData[tmpIdx+2] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success				
+				}
+			}
+			else
+			{
+				if (m_pData[i*m_iWidth+j] >= uppVal1)
+				{
+					//QRgb rgbVal = qRgb(255, 255, 255);
+					//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+					tmpData[tmpIdx+0] = 0;
+					tmpData[tmpIdx+1] = 0;
+					tmpData[tmpIdx+2] = 0;
+
+				}
+				else if (m_pData[i*m_iWidth+j] <= lowVal1)
+				{
+					tmpData[tmpIdx+0] = 255;
+					tmpData[tmpIdx+1] = 255;
+					tmpData[tmpIdx+2] = 255;
+					//QRgb rgbVal = qRgb(0, 0, 0);
+					//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+				}
+				else
+				{
+					tmpData[tmpIdx+0] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success
+					tmpData[tmpIdx+1] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success
+					tmpData[tmpIdx+2] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success				
+				}
+			}
+		}		
+	}
+
+	//2/4 sector
+	for (int i = 0 ; i<splitY ; i++) //So long time....
+	{
+	  for (int j = splitX ; j< m_iWidth ; j++)
+	  {
+		int tmpIdx = 3*(i*m_iWidth+j);
+
+		if (!m_bShowInvert)
+		{
+
+		  if (m_pData[i*m_iWidth+j] >= uppVal2)
+		  {
+			//QRgb rgbVal = qRgb(255, 255, 255);
+			//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+			tmpData[tmpIdx+0] = 255;
+			tmpData[tmpIdx+1] = 255;
+			tmpData[tmpIdx+2] = 255;
+
+		  }
+		  else if (m_pData[i*m_iWidth+j] <= lowVal2)
+		  {
+			tmpData[tmpIdx+0] = 0;
+			tmpData[tmpIdx+1] = 0;
+			tmpData[tmpIdx+2] = 0;
+			//QRgb rgbVal = qRgb(0, 0, 0);
+			//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+		  }
+		  else
+		  {
+			tmpData[tmpIdx+0] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success
+			tmpData[tmpIdx+1] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success
+			tmpData[tmpIdx+2] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success				
+		  }
+		}
+		else
+		{
+		  if (m_pData[i*m_iWidth+j] >= uppVal2)
+		  {
+			//QRgb rgbVal = qRgb(255, 255, 255);
+			//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+			tmpData[tmpIdx+0] = 0;
+			tmpData[tmpIdx+1] = 0;
+			tmpData[tmpIdx+2] = 0;
+
+		  }
+		  else if (m_pData[i*m_iWidth+j] <= lowVal2)
+		  {
+			tmpData[tmpIdx+0] = 255;
+			tmpData[tmpIdx+1] = 255;
+			tmpData[tmpIdx+2] = 255;
+			//QRgb rgbVal = qRgb(0, 0, 0);
+			//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+		  }
+		  else
+		  {
+			tmpData[tmpIdx+0] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success
+			tmpData[tmpIdx+1] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success
+			tmpData[tmpIdx+2] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success				
+		  }
+		}
+	  }		
+	}
+
+	//3/4 sector
+	for (int i = splitY ; i<m_iHeight ; i++) //So long time....
+	{
+	  for (int j = 0 ; j< splitX ; j++)
+	  {
+		int tmpIdx = 3*(i*m_iWidth+j);
+
+		if (!m_bShowInvert)
+		{
+
+		  if (m_pData[i*m_iWidth+j] >= uppVal2)
+		  {
+			//QRgb rgbVal = qRgb(255, 255, 255);
+			//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+			tmpData[tmpIdx+0] = 255;
+			tmpData[tmpIdx+1] = 255;
+			tmpData[tmpIdx+2] = 255;
+
+		  }
+		  else if (m_pData[i*m_iWidth+j] <= lowVal2)
+		  {
+			tmpData[tmpIdx+0] = 0;
+			tmpData[tmpIdx+1] = 0;
+			tmpData[tmpIdx+2] = 0;
+			//QRgb rgbVal = qRgb(0, 0, 0);
+			//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+		  }
+		  else
+		  {
+			tmpData[tmpIdx+0] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success
+			tmpData[tmpIdx+1] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success
+			tmpData[tmpIdx+2] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success				
+		  }
+		}
+		else
+		{
+		  if (m_pData[i*m_iWidth+j] >= uppVal2)
+		  {
+			//QRgb rgbVal = qRgb(255, 255, 255);
+			//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+			tmpData[tmpIdx+0] = 0;
+			tmpData[tmpIdx+1] = 0;
+			tmpData[tmpIdx+2] = 0;
+
+		  }
+		  else if (m_pData[i*m_iWidth+j] <= lowVal2)
+		  {
+			tmpData[tmpIdx+0] = 255;
+			tmpData[tmpIdx+1] = 255;
+			tmpData[tmpIdx+2] = 255;
+			//QRgb rgbVal = qRgb(0, 0, 0);
+			//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+		  }
+		  else
+		  {
+			tmpData[tmpIdx+0] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success
+			tmpData[tmpIdx+1] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success
+			tmpData[tmpIdx+2] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal2)/(double)winWidth2 * 255.0); //success				
+		  }
+		}
+	  }		
+	}
+
+	//4/4 sector
+	for (int i = splitY ; i<m_iHeight ; i++) //So long time....
+	{
+	  for (int j = splitX ; j< m_iWidth ; j++)
+	  {
+		int tmpIdx = 3*(i*m_iWidth+j);
+
+		if (!m_bShowInvert)
+		{
+
+		  if (m_pData[i*m_iWidth+j] >= uppVal1)
+		  {
+			//QRgb rgbVal = qRgb(255, 255, 255);
+			//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+			tmpData[tmpIdx+0] = 255;
+			tmpData[tmpIdx+1] = 255;
+			tmpData[tmpIdx+2] = 255;
+
+		  }
+		  else if (m_pData[i*m_iWidth+j] <= lowVal1)
+		  {
+			tmpData[tmpIdx+0] = 0;
+			tmpData[tmpIdx+1] = 0;
+			tmpData[tmpIdx+2] = 0;
+			//QRgb rgbVal = qRgb(0, 0, 0);
+			//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+		  }
+		  else
+		  {
+			tmpData[tmpIdx+0] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success
+			tmpData[tmpIdx+1] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success
+			tmpData[tmpIdx+2] = (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success				
+		  }
+		}
+		else
+		{
+		  if (m_pData[i*m_iWidth+j] >= uppVal1)
+		  {
+			//QRgb rgbVal = qRgb(255, 255, 255);
+			//m_QImage.setPixel(j,i,qRgb(255, 255, 255));
+
+			tmpData[tmpIdx+0] = 0;
+			tmpData[tmpIdx+1] = 0;
+			tmpData[tmpIdx+2] = 0;
+
+		  }
+		  else if (m_pData[i*m_iWidth+j] <= lowVal1)
+		  {
+			tmpData[tmpIdx+0] = 255;
+			tmpData[tmpIdx+1] = 255;
+			tmpData[tmpIdx+2] = 255;
+			//QRgb rgbVal = qRgb(0, 0, 0);
+			//m_QImage.setPixel(j,i,qRgb(0, 0, 0));
+		  }
+		  else
+		  {
+			tmpData[tmpIdx+0] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success
+			tmpData[tmpIdx+1] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success
+			tmpData[tmpIdx+2] = 255 - (uchar) ((m_pData[i*m_iWidth+j] - lowVal1)/(double)winWidth1 * 255.0); //success				
+		  }
+		}
+	  }		
+	}
+
+	int iBytesPerLine = m_iWidth*3;        
+	QImage tmpQImage = QImage((unsigned char*)tmpData,m_iWidth, m_iHeight,iBytesPerLine, QImage::Format_RGB888); //not deep copy!
+
+	//Copy only a ROI region. All below are data-point, rather than display points
+	//Outside region wiill be filled with Black by QImage inherent function
+
+	//
+	int newWidth = qRound(m_iWidth/m_fZoom);
+	int newHeight = qRound(m_iHeight/m_fZoom);
+
+	int centerX = m_iOffsetX + qRound(m_iWidth/2.0);
+	int centerY = m_iOffsetY + qRound(m_iHeight/2.0);
+
+	int newLeftTopX = centerX - qRound(newWidth/2.0);//data position
+	int newLeftTopY = centerY - qRound(newHeight/2.0);	//data position
+	m_QImage = tmpQImage.copy(qRound(newLeftTopX),qRound(newLeftTopY), newWidth, newHeight); //memory allocated here!!!
+	
+
+	//m_QImage = tmpQImage.copy(0,0,m_iWidth, m_iHeight); //memory allocated here!!!        
+	//YKTEMP: is it needed? no it worked without below:
+	//*m_pPixmap = QPixmap::fromImage(m_QImage); //copy data to pre-allcated pixmap buffer
+
+	delete [] tmpData;
+	return true;
+
+}
+
+bool YK16GrayImage::FillPixMapMinMaxDual( int winMin1, int winMin2, int winMax1, int winMax2 )
+{
+  if (winMin1 < 0 || winMax1 > 65535 || winMin1 > winMax1)
+  {
+	winMin1 = 0;
+	winMax1 = 65535;
+  }
+
+  if (winMin2 < 0 || winMax2 > 65535 || winMin2 > winMax2)
+  {
+	winMin2 = 0;
+	winMax2 = 65535;
+  }
+
+  int midVal1 = (int)((winMin1 + winMax1)/2.0);
+  int midVal2 = (int)((winMin2 + winMax2)/2.0);
+
+  int widthVal1 = winMax1 - winMin1;        
+  int widthVal2 = winMax2 - winMin2;        
+
+  return FillPixMapDual(midVal1, midVal2, widthVal1, widthVal2);
+
+}
+
+bool YK16GrayImage::isPtInFirstImage(int dataX, int dataY)
+{ 
+  if (dataX < 0 || dataX >= m_iWidth || dataY < 0  || dataY >= m_iHeight)
+  {
+	cout << "Fatal error in isPtInFirstImage! Given point is out of image point" << endl;
+	return false;
+  }
+
+  if (m_enSplitOption == PRI_LEFT_TOP && !IsEmpty())
+  {
+	if ((dataX >= 0 && dataX < m_ptSplitCenter.x() && dataY >=0 && dataY < m_ptSplitCenter.y()) ||
+		(dataX >= m_ptSplitCenter.x() && dataX < m_iWidth && dataY >= m_ptSplitCenter.y() && dataY < m_iHeight))
+		return true;
+	else
+	  return false;
+  }
+  return false;
+}
+
+void YK16GrayImage::SetSplitCenter( QPoint& ptSplitCenter )
+{
+  if (IsEmpty())
+	return;
+  
+  if (ptSplitCenter.x() < 0  || ptSplitCenter.x() >= m_iWidth || ptSplitCenter.y() < 0  || ptSplitCenter.y() >= m_iHeight)
+  {
+	m_ptSplitCenter.setX(0);
+	m_ptSplitCenter.setY(0);
+  }
+  else
+	m_ptSplitCenter = ptSplitCenter;
+
+}
+
+void YK16GrayImage::SetZoom( double fZoom )
+{
+  if (fZoom <= 1)
+	m_fZoom = 1.0;
+  else 
+	m_fZoom = fZoom;	
+}
+
+void YK16GrayImage::MedianFilter( int iMedianSizeX, int iMedianSizeY )
+{
+  if (m_pData == NULL)
+	return;
+
+  UnsignedShortImageType::Pointer spTmpItkImg = UnsignedShortImageType::New();	
+
+  UnsignedShortImageType::SizeType size;
+  size[0] = m_iWidth;
+  size[1] = m_iHeight;
+  UnsignedShortImageType::IndexType idxStart;
+  idxStart[0] = 0;
+  idxStart[1] = 0;
+  UnsignedShortImageType::SpacingType spacing;	
+  if (m_fSpacingX*m_fSpacingY == 0)
+  {
+	spacing[0] = 1.0;
+	spacing[1] = 1.0;
+  }
+  else
+  {
+	spacing[0] = m_fSpacingX;
+	spacing[1] = m_fSpacingY;
+  }
+
+  UnsignedShortImageType::PointType origin;
+  origin[0] = size[0]*spacing[0]/-2.0;
+  origin[1] = size[1]*spacing[1]/-2.0;
+
+  UnsignedShortImageType::RegionType region;
+  region.SetSize(size);
+  region.SetIndex(idxStart);
+
+  spTmpItkImg->SetRegions(region);
+  spTmpItkImg->SetSpacing(spacing);
+  spTmpItkImg->SetOrigin(origin);
+  spTmpItkImg->Allocate();
+
+  CopyYKImage2ItkImage(this, spTmpItkImg);
+
+  typedef itk::MedianImageFilter<UnsignedShortImageType, UnsignedShortImageType> MedianFilterType;
+  MedianFilterType::Pointer medianFilter = MedianFilterType::New();
+  //medianFilter->SetInput(spTmpItkImg);	
+
+  MedianFilterType::InputSizeType radius;
+  radius[0] = qRound(iMedianSizeX/2.0);
+  radius[1] = qRound(iMedianSizeY/2.0);
+
+  medianFilter->SetRadius(radius);
+  medianFilter->SetInput(spTmpItkImg);
+  medianFilter->Update();
+
+
+  spTmpItkImg = medianFilter->GetOutput();
+
+  CopyItkImage2YKImage(spTmpItkImg, this);
+
+}
+
+UnsignedShortImageType::Pointer YK16GrayImage::CloneItkImage()
+{
+  if (m_pData == NULL)
+	return NULL;
+
+  UnsignedShortImageType::Pointer spTmpItkImg = UnsignedShortImageType::New();	
+
+  UnsignedShortImageType::SizeType size;
+  size[0] = m_iWidth;
+  size[1] = m_iHeight;
+  UnsignedShortImageType::IndexType idxStart;
+  idxStart[0] = 0;
+  idxStart[1] = 0;
+  UnsignedShortImageType::SpacingType spacing;	
+  if (m_fSpacingX*m_fSpacingY == 0)
+  {
+	spacing[0] = 1.0;
+	spacing[1] = 1.0;
+  }
+  else
+  {
+	spacing[0] = m_fSpacingX;
+	spacing[1] = m_fSpacingY;
+  }
+  UnsignedShortImageType::PointType origin;
+  origin[0] = size[0]*spacing[0]/-2.0;
+  origin[1] = size[1]*spacing[1]/-2.0;
+
+  UnsignedShortImageType::RegionType region;
+  region.SetSize(size);
+  region.SetIndex(idxStart);
+
+  spTmpItkImg->SetRegions(region);
+  spTmpItkImg->SetSpacing(spacing);
+  spTmpItkImg->SetOrigin(origin);
+  spTmpItkImg->Allocate();
+
+  //Raw File open	
+  //UnsignedShortImageType::SizeType tmpSize = 
+  //UnsignedShortImageType::RegionType region = spTmpItkImg->GetRequestedRegion();
+  //UnsignedShortImageType::SizeType tmpSize = region.GetSize();
+
+  //int sizeX = tmpSize[0];
+  //int sizeY = tmpSize[1];
+
+  //if (sizeX < 1 || sizeY <1)
+  //	return;
+
+  itk::ImageRegionIterator<UnsignedShortImageType> it(spTmpItkImg, spTmpItkImg->GetRequestedRegion());
+
+  int i = 0;
+  for (it.GoToBegin() ; !it.IsAtEnd(); ++it)
+  {
+	it.Set(m_pData[i]);
+	i++;
+  }
+
+  return spTmpItkImg;
+
+}
+
+void YK16GrayImage::ResampleImage( double fResampleFactor )
+{
+  if (m_pData == NULL)
+	return;
+
+  if (fResampleFactor <= 0)
+	return;
+
+  m_fResampleFactor = fResampleFactor;
+
+  UnsignedShortImageType::SizeType inputSize;
+  inputSize[0] = m_iWidth;
+  inputSize[1] = m_iHeight;
+
+  UnsignedShortImageType::SizeType outputSize;
+  outputSize[0] = qRound(m_iWidth*fResampleFactor);
+  outputSize[1] = qRound(m_iHeight*fResampleFactor);
+  //m_iWidth = outputSize[0];
+  //m_iHeight = outputSize[1];  	 
+
+  UnsignedShortImageType::SpacingType outputSpacing;
+
+  if (m_fSpacingX <=0 ||m_fSpacingY <=0)
+  {
+	m_fSpacingX = 1.0;
+	m_fSpacingY = 1.0;
+  }
+
+  outputSpacing[0] = m_fSpacingX * (static_cast<double>(inputSize[0]) / static_cast<double>(outputSize[0]));
+  outputSpacing[1] = m_fSpacingY * (static_cast<double>(inputSize[1]) / static_cast<double>(outputSize[1]));		  
+
+  UnsignedShortImageType::Pointer input = CloneItkImage();//returns Ushort itk image from data buf
+  UnsignedShortImageType::PointType outputOrigin = input->GetOrigin(); //-204.6 - 204.6  0  
+
+  //// Resample the image
+  //typedef itk::IdentityTransform<float, 2> TransformType;
+  typedef itk::ResampleImageFilter<UnsignedShortImageType, UnsignedShortImageType, float> ResampleImageFilterType;
+  ResampleImageFilterType::Pointer resample = ResampleImageFilterType::New();
+
+  typedef itk::AffineTransform< float, 2 >  TransformType;
+  TransformType::Pointer transform = TransformType::New();
+  typedef itk::NearestNeighborInterpolateImageFunction<UnsignedShortImageType, float >  InterpolatorType;
+  InterpolatorType::Pointer interpolator = InterpolatorType::New();
+  transform->SetIdentity();
+
+  resample->SetInput(input);
+  resample->SetOutputDirection( input->GetDirection() );
+  resample->SetInterpolator(interpolator);
+  resample->SetDefaultPixelValue( 50 );
+  resample->SetSize(outputSize);
+  resample->SetOutputSpacing(outputSpacing);
+  resample->SetOutputOrigin(outputOrigin);  
+  resample->SetTransform(transform);
+  resample->Update();
+  
+  UnsignedShortImageType::Pointer outputImg = resample->GetOutput();
+  UpdateFromItkImage(outputImg); //itk --> YKImage
+
+}
+
+void YK16GrayImage::UpdateFromItkImage( UnsignedShortImageType::Pointer& spRefItkImg )
+{
+  if (!spRefItkImg)
+	return;
+
+  if (m_pData != NULL)
+  {
+	delete [] m_pData;
+	m_pData = NULL;
+  }
+  if (m_pPixmap != NULL)
+  {
+	delete m_pPixmap;		m_pPixmap = NULL;
+  }	
+
+  UnsignedShortImageType::SizeType size = spRefItkImg->GetRequestedRegion().GetSize();
+  UnsignedShortImageType::SpacingType spacing = spRefItkImg->GetSpacing();
+  UnsignedShortImageType::PointType origin = spRefItkImg->GetOrigin();
+
+  m_iWidth = size[0];
+  m_iHeight = size[1];
+
+  SetSpacing(spacing[0], spacing[1]);
+  SetOrigin(origin[0], origin[1]);  
+  
+  m_pData = new unsigned short [m_iWidth*m_iHeight];
+
+  itk::ImageRegionIterator<UnsignedShortImageType> it(spRefItkImg, spRefItkImg->GetRequestedRegion());
+
+  int i = 0;
+  for (it.GoToBegin() ; !it.IsAtEnd() ; ++it)
+  {
+	m_pData[i] = it.Get();
+	i++;
+  }
+}
+
+void YK16GrayImage::UpdateFromItkImageFloat( FloatImageType2D::Pointer& spRefItkImg )
+{
+  if (!spRefItkImg)
+	return;
+
+  if (m_pData != NULL)
+  {
+	delete [] m_pData;
+	m_pData = NULL;
+  }
+  if (m_pPixmap != NULL)
+  {
+	delete m_pPixmap;		m_pPixmap = NULL;
+  }	
+
+  FloatImageType2D::SizeType size = spRefItkImg->GetRequestedRegion().GetSize();
+  FloatImageType2D::SpacingType spacing = spRefItkImg->GetSpacing();
+  FloatImageType2D::PointType origin = spRefItkImg->GetOrigin();
+
+  m_iWidth = size[0];
+  m_iHeight = size[1];
+
+
+  SetSpacing(spacing[0], spacing[1]);
+  SetOrigin(origin[0], origin[1]);
+
+  m_pData = new unsigned short [m_iWidth*m_iHeight];
+
+  itk::ImageRegionIterator<FloatImageType2D> it(spRefItkImg, spRefItkImg->GetRequestedRegion());
+
+  int i = 0;
+  for (it.GoToBegin() ; !it.IsAtEnd() ; ++it)
+  {
+	float curVal =it.Get();
+	unsigned short outVal;
+
+	if (curVal < 0.0)
+	  outVal = 0;
+	else if (curVal > 65535.0)
+	  outVal  = 65535;
+	else
+	  outVal = (unsigned short)qRound(curVal);
+
+	m_pData[i] = outVal;		
+	i++;
+  }
+}
+
+void YK16GrayImage::UpdateFromItkImageFloat(FloatImageType2D::Pointer& spRefItkImg, float fIntenistyMag, float fIntensityOffset, bool bYFlip)
+{
+    if (!spRefItkImg)
+        return;
+
+    if (m_pData != NULL)
+    {
+        delete[] m_pData;
+        m_pData = NULL;
+    }
+    if (m_pPixmap != NULL)
+    {
+        delete m_pPixmap;		m_pPixmap = NULL;
+    }
+
+    FloatImageType2D::SizeType size = spRefItkImg->GetRequestedRegion().GetSize();
+    FloatImageType2D::SpacingType spacing = spRefItkImg->GetSpacing();
+    FloatImageType2D::PointType origin = spRefItkImg->GetOrigin();
+
+    m_iWidth = size[0];
+    m_iHeight = size[1];
+
+    SetSpacing(spacing[0], spacing[1]);
+    SetOrigin(origin[0], origin[1]);    
+
+    m_pData = new unsigned short[m_iWidth*m_iHeight];
+
+    this->m_fIntensityMag = fIntenistyMag;
+    this->m_fIntensityOffset = fIntensityOffset;    
+
+
+    FloatImageType2D::Pointer spTmpImg;
+
+    if (bYFlip)
+    {
+        ////Let's flip image
+        typedef itk::FlipImageFilter< FloatImageType2D >  FilterType;
+
+        FilterType::Pointer flipFilter = FilterType::New();
+        typedef FilterType::FlipAxesArrayType FlipAxesArrayType;
+
+        FlipAxesArrayType arrFlipAxes;
+        arrFlipAxes[0] = 0;
+        arrFlipAxes[1] = 1;
+
+        flipFilter->SetFlipAxes(arrFlipAxes);
+        flipFilter->SetInput(spRefItkImg);
+        flipFilter->Update();      
+        spTmpImg = flipFilter->GetOutput();
+
+        //spTmpImg = spRefItkImg;
+    }
+    else
+    {
+        spTmpImg = spRefItkImg;
+    }
+
+    itk::ImageRegionIterator<FloatImageType2D> it(spTmpImg, spTmpImg->GetRequestedRegion());
+    //itk::ImageRegionIterator<FloatImageType2D> it(flipFilter->GetOutput(), flipFilter->GetOutput()->GetRequestedRegion());
+
+    int i = 0;
+    for (it.GoToBegin(); !it.IsAtEnd(); ++it)
+    {
+        float curVal = it.Get();
+        m_pData[i] = GetWrappingIntensityVal(curVal);
+        i++;
+    }
+}
+
+void YK16GrayImage::UpdateToItkImageFloat(FloatImageType2D::Pointer& spRefItkImg)
+{
+    /*if (m_pData != NULL)
+    {
+    delete[] m_pData;
+    m_pData = NULL;
+    }
+    if (m_pPixmap != NULL)
+    {
+    delete m_pPixmap;		m_pPixmap = NULL;
+    }
+
+    FloatImageType2D::SizeType size = spRefItkImg->GetRequestedRegion().GetSize();
+    FloatImageType2D::SpacingType spacing = spRefItkImg->GetSpacing();
+    FloatImageType2D::PointType origin = spRefItkImg->GetOrigin();
+
+    m_iWidth = size[0];
+    m_iHeight = size[1];
+
+    SetSpacing(spacing[0], spacing[1]);
+    SetOrigin(origin[0], origin[1]);
+
+    m_pData = new unsigned short[m_iWidth*m_iHeight];
+
+    itk::ImageRegionIterator<FloatImageType2D> it(spRefItkImg, spRefItkImg->GetRequestedRegion());
+
+    int i = 0;
+    for (it.GoToBegin(); !it.IsAtEnd(); it++)
+    {
+    float curVal = it.Get();
+    m_pData[i] = GetWrappingIntensityVal(curVal);
+    i++;
+    }*/
+}
+
+void YK16GrayImage::InvertImage()
+{
+  if (m_pData == NULL)
+	return;
+
+  int imgSize= m_iWidth * m_iHeight;
+  //Data inversion: Default for Elekta XVI system
+  for (int i = 0 ; i<imgSize ; i++)
+  {
+	m_pData[i] = 65535 - m_pData[i];
+  }	
+
+}
+
+unsigned short YK16GrayImage::GetPixelData(int x, int y)
+{
+	if (m_pData != NULL)
+		return m_pData[m_iWidth*y + x];
+	else
+		return 0;
+}
+
+void YK16GrayImage::AddMarkerPos(int dataX, int dataY)
+{
+	m_vMarker.push_back(QPoint(dataX, dataY));
+}
+
+void YK16GrayImage::ClearMarkerPos()
+{
+	m_vMarker.clear();
+}
+
+QPoint YK16GrayImage::GetMeanPos()
+{
+	double sumPosX = 0.0;
+	double sumPosY = 0.0;
+
+	if (m_vMarker.empty())
+		return (QPoint(0, 0));
+
+	vector<QPoint>::iterator it;
+
+	int maxNum = 0;
+	
+	if (m_iNumOfTrackMarker == 0)
+		return QPoint(0, 0);
+
+	if (m_iNumOfTrackMarker == -1)
+		maxNum = 100000;
+	else
+		maxNum = m_iNumOfTrackMarker;
+
+	int cnt = 0;
+	for (it = m_vMarker.begin(); it != m_vMarker.end(); it++)
+	{
+		sumPosX += (*it).x();
+		sumPosY += (*it).y();
+		cnt++;
+
+		if (cnt >= maxNum)
+			break;
+	}
+
+	return QPoint(qRound(sumPosX / (double)cnt), qRound(sumPosY / (double)cnt));
+}
+
+void YK16GrayImage::AddMarkerPosRef(int dataX, int dataY)
+{
+	m_vMarkerRef.push_back(QPoint(dataX, dataY));
+
+	int margin = 10;
+
+	if (dataX < margin || dataX > m_iWidth - margin ||
+		dataY < margin || dataY > m_iHeight - margin)
+		m_bvRefOutOfRange.push_back(true);
+	else
+		m_bvRefOutOfRange.push_back(false);
+
+}
+
+void YK16GrayImage::ClearMarkerPosRef()
+{
+	m_vMarkerRef.clear();
+	m_bvRefOutOfRange.clear();
+}
+
+QPoint YK16GrayImage::GetMeanPosRef()
+{
+	double sumPosX = 0.0;
+	double sumPosY = 0.0;
+
+	if (!existRefMarkers())
+		return QPoint(0, 0);
+
+
+	vector<QPoint>::iterator it;	
+
+	int cnt = 0;
+	for (it = m_vMarkerRef.begin(); it != m_vMarkerRef.end(); it++)
+	{
+		sumPosX += (*it).x();
+		sumPosY += (*it).y();
+		cnt++;		
+	}
+
+	if (cnt == 0)
+		return QPoint(0,0);	
+
+	return QPoint(qRound(sumPosX / (double)cnt), qRound(sumPosY / (double)cnt));
+}
+
+bool YK16GrayImage::existRefMarkers()
+{
+	if (m_vMarkerRef.empty())
+		return false;	
+
+	return true;
+}
+
+void YK16GrayImage::UpdateTrackingData(YK16GrayImage* pYKProcessedImage)
+{
+	m_strTimeStamp = pYKProcessedImage->m_strTimeStamp; //HHMMSSFFF (9digits)
+	m_iProcessingElapsed = pYKProcessedImage->m_iProcessingElapsed; //processing time in ms
+	//m_bDrawMarkers;
+	m_iNumOfDispMarker = pYKProcessedImage->m_iNumOfDispMarker; //for display      e.g.)  0,1,2,3,4
+	m_iNumOfTrackMarker = pYKProcessedImage->m_iNumOfTrackMarker; //for tracking   GetMeanPos  e.g.)  0,1,2 only	
+	//Reference marker group
+	m_vMarkerRef = pYKProcessedImage->m_vMarkerRef;//let's see it works
+	m_bvRefOutOfRange = pYKProcessedImage->m_bvRefOutOfRange;//leave a tag when calculated 2D ref position is out of image		
+	m_vMarker = pYKProcessedImage->m_vMarker;//position in data dimension (not either physical nor display)
+
+	//should be implemented later
+	m_track_priorErr = pYKProcessedImage->m_track_priorErr;
+	m_track_motionErr = pYKProcessedImage->m_track_motionErr;
+	m_track_CC_penalty = pYKProcessedImage->m_track_CC_penalty;
+
+	//already there!
+	m_fMVGantryAngle = pYKProcessedImage->m_fMVGantryAngle;
+	m_fPanelOffsetX = pYKProcessedImage->m_fPanelOffsetX; //mm
+	m_fPanelOffsetY = pYKProcessedImage->m_fPanelOffsetY; //mm
+	m_bKVOn = pYKProcessedImage->m_bKVOn; //mm
+	m_bMVOn = pYKProcessedImage->m_bMVOn; //mm
+	m_iXVI_ElapseMS = pYKProcessedImage->m_iXVI_ElapseMS; //mm
+
+	m_rtROI = pYKProcessedImage->m_rtROI;
+	m_bDraw8Bit = pYKProcessedImage->m_bDraw8Bit;
+}
+
+//void YK16GrayImage::AddContourROI(QString& strROIName, bool bDisplay, std::vector<QPoint>& vContourPts)
+//{
+//    CContourROI* pContourROI = new CContourROI();
+//
+//    pContourROI->_strNameROI = strROIName;
+//    pContourROI->_bDrawROI = bDisplay;
+//
+//    //dip copy of points
+//    vector<QPoint>::iterator it;
+//    for (it = vContourPts.begin(); it != vContourPts.end(); it++)
+//    {
+//        pContourROI->_vDataPt.push_back((*it));
+//    }
+//    m_vvContourROI.push_back(pContourROI);
+//}
+
+void YK16GrayImage::AddContourROI(QString& strROIName, bool bDisplay, QRgb color, int thickness, std::vector<QPoint>& vContourPts)
+{
+    CContourROI* pContourROI = new CContourROI();
+
+    pContourROI->_strNameROI = strROIName;
+    pContourROI->_bDrawROI = bDisplay;
+    pContourROI->_rgb = color;
+    pContourROI->_thick = thickness;
+
+    //dip copy of points
+    vector<QPoint>::iterator it;
+    for (it = vContourPts.begin(); it != vContourPts.end(); it++)
+    {
+        pContourROI->_vDataPt.push_back((*it));
+    }
+    m_vvContourROI.push_back(pContourROI);
+
+}
+
+void YK16GrayImage::ClearContourROI()
+{
+    vector<CContourROI*>::iterator it;
+    for (it = m_vvContourROI.begin(); it != m_vvContourROI.end(); it++)
+    {
+        delete (*it); //release mem of each item (pt data)
+    }
+
+    m_vvContourROI.clear();
+}
+
+bool YK16GrayImage::SetDisplayStatus(QString& strROIName, bool bDisplay)
+{
+    vector<CContourROI*>::iterator it;
+
+    CContourROI* pCurContour = NULL;
+    for (it = m_vvContourROI.begin(); it != m_vvContourROI.end(); it++)
+    {
+        pCurContour = (*it);
+
+        if (pCurContour->_strNameROI == strROIName)
+        {
+            pCurContour->_bDrawROI = bDisplay;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+float YK16GrayImage::GetOriginalIntensityVal(unsigned short usPixVal)
+{
+    if (m_fIntensityMag <= 0)   
+        return -1.0;
+
+    return (float)(usPixVal - m_fIntensityOffset) / m_fIntensityMag;
+}
+
+unsigned short YK16GrayImage::GetWrappingIntensityVal(float fPixVal)
+{
+    unsigned short outVal;
+     float fVal = fPixVal*m_fIntensityMag + m_fIntensityOffset;
+     if (fVal < 0.0)
+         outVal = 0;
+     else if (fVal > 65535.0)
+         outVal = 65535;
+     else
+         outVal = (unsigned short)qRound(fVal);
+
+     return outVal;
+}
+
+void YK16GrayImage::SetCrosshairPosPhys(float physX, float physY, enPLANE plane)
+{
+    if (m_fSpacingX*m_fSpacingY == 0)
+        return;
+
+    int dataX, dataY;
+
+    dataX = qRound((physX - m_fOriginX) / m_fSpacingX);
+    dataY = qRound((physY - m_fOriginY) / m_fSpacingY);
+
+    /* cout << "m_fOriginY: " << m_fOriginY << endl;
+     cout << "physY: " << physY << endl;*/
+
+    if (dataX < 0)
+        dataX = 0;
+    if (dataX >= m_iWidth)
+        dataX = m_iWidth - 1;
+
+    if (dataY < 0)
+        dataY = 0;
+    if (dataY >= m_iHeight)
+        dataY = m_iHeight - 1;
+
+    if (plane != PLANE_AXIAL)
+    {        
+        dataY = m_iHeight - dataY - 1;        
+    }    
+
+    m_ptCrosshair.setX(dataX);
+    m_ptCrosshair.setY(dataY);
+}
+
+QColor YK16GrayImage::GetColorFromDosePerc(float percVal) //to be edited to use LUT
+{
+   /* float blueRef = 40.0;
+    float greenRef = 80.0;
+    float redRef = 120.0;*/
+
+        
+
+    /*   if (percVal < blueRef)
+       {
+       blueVal = (int)((blueRef - percVal)*4.0+90.0);
+       greenVal = 0;
+       redVal = 0;
+       }
+
+       else if (percVal >= blueRef && percVal < greenRef)
+       {
+       blueVal = 0;
+       greenVal = (int)((greenRef - percVal)*4.0 + 90.0);
+       redVal = 0;
+       }
+       else if (percVal >= greenRef && percVal < redRef)
+       {
+       blueVal = 0;
+       greenVal = 0;;
+       redVal = (int)((redRef - percVal)*4.0 + 90.0);
+       }
+
+       else if (percVal > redRef)
+       {
+       blueVal = 255;
+       greenVal = 255;;
+       redVal = 255;
+       }       */
+    QColor color;
+    int blueVal, greenVal, redVal;
+
+    if (m_vColorTable.empty())
+        return color;
+
+    float minDosePerc = 0.0;
+    float maxDosePerc = 120.0; //120%
+    VEC3D curVal = QUTIL::GetRGBValueFromTable(m_vColorTable, minDosePerc, maxDosePerc, percVal);
+
+    redVal = qRound(curVal.x*255);
+    greenVal = qRound(curVal.y*255);
+    blueVal = qRound(curVal.z*255);
+
+    if (redVal > 255)
+        redVal = 255;
+    if (greenVal > 255)
+        greenVal = 255;
+    if (blueVal > 255)
+        blueVal = 255;
+
+    color.setRed(redVal);
+    color.setGreen(greenVal);
+    color.setBlue(blueVal);
+
+    return color;
+}
+
+QColor YK16GrayImage::GetColorFromGamma(float gammaVal)
+{    
+   QColor color;
+    int blueVal, greenVal, redVal;
+
+    if (m_vColorTable.empty())
+        return color;
+
+    VEC3D curVal; 
+    curVal = QUTIL::GetRGBValueFromTable(m_vColorTable, 0.0, 2.0, gammaVal);
+
+    redVal = qRound(curVal.x * 255);
+    greenVal = qRound(curVal.y * 255);
+    blueVal = qRound(curVal.z * 255);
+
+    if (redVal > 255)
+        redVal = 255;
+    if (greenVal > 255)
+        greenVal = 255;
+    if (blueVal > 255)
+        blueVal = 255;
+
+    color.setRed(redVal);
+    color.setGreen(greenVal);
+    color.setBlue(blueVal);
+
+    return color;
+}
+
+void YK16GrayImage::SetColorTable(vector<VEC3D>& vInputColorTable)
+{
+    if (vInputColorTable.empty())
+        return;
+
+    m_vColorTable.clear();
+
+    m_vColorTable = vInputColorTable; //deep copy?
+
+}
+
+unsigned short YK16GrayImage::GetCrosshairPixelData()
+{
+    return  GetPixelData(m_ptCrosshair.x(), m_ptCrosshair.y());
+}
+
+float YK16GrayImage::GetCrosshairOriginalData()
+{
+    return GetOriginalIntensityVal(GetCrosshairPixelData());
+}
+
+float YK16GrayImage::GetCrosshairPercData()
+{   
+
+    if (m_fNormValue > 0)
+    {
+        return (GetOriginalIntensityVal(GetCrosshairPixelData()) / m_fNormValue*100.0);
+    }
+    else
+        return 0.0;
+}
\ No newline at end of file
diff --git a/src/plastimatch/standalone/YK16GrayImage.h b/src/plastimatch/standalone/YK16GrayImage.h
new file mode 100644
index 0000000..855d085
--- /dev/null
+++ b/src/plastimatch/standalone/YK16GrayImage.h
@@ -0,0 +1,299 @@
+#pragma once
+
+//v20130830 : his header buffer, itk compatible
+
+class QPixmap;
+class QLabel;
+class QPainter;
+
+//class QImage;
+
+#define DEFAULT_WINLEVEL_MID 10000
+#define DEFAULT_WINLEVEL_WIDTH 20000
+
+#define DEFAULT_ELEKTA_HIS_HEADER_SIZE 100
+
+#include "itkImage.h"
+#include <QImage>
+#include <vector>
+#include <QVector>
+#include <QRgb>
+#include <QColor>
+
+#include "yk_config.h"
+
+struct BADPIXELMAP{
+	int BadPixX;
+	int BadPixY;
+	int ReplPixX;
+	int ReplPixY;
+};
+
+class CContourROI
+{
+public: 
+    CContourROI(void){ ; }
+    ~CContourROI(void){ _vDataPt.clear(); }
+
+    bool _bDrawROI;
+    QString _strNameROI;
+    QRgb _rgb; //QRgb(255,0,0)
+    int _thick;
+    std::vector<QPoint> _vDataPt;
+};
+
+enum enProfileDirection{
+	DIRECTION_HOR = 0,
+	DIRECTION_VER,	
+};
+
+enum enSplitOption{
+    PRI_LEFT_TOP = 0, //Primary Left Top
+    PRI_RIGHT_TOP, //Primary Left Top
+    PRI_LEFT,	
+    PRI_RIGHT,	
+    PRI_TOP,	
+    PRI_BOTTOM,	
+};
+
+
+typedef itk::Image<unsigned short, 2> UnsignedShortImageType;
+typedef itk::Image<float, 2> FloatImageType2D;
+
+using namespace std;
+
+class YK16GrayImage
+{	
+public:
+	YK16GrayImage(void);
+	YK16GrayImage(int width, int height);
+	~YK16GrayImage(void);
+
+	int m_iWidth;
+	int m_iHeight;
+	//added: 20140206
+	double m_fSpacingX; //[mm/px]
+	double m_fSpacingY;
+
+        double m_fOriginX;
+        double m_fOriginY;
+
+        float m_fIntensityMag;//intensity magnification factor default = 1.0;
+        float m_fIntensityOffset;//intensity Offset factor default = 0.0;
+        float m_fNormValue;
+
+        void SetIntensityModification(float intensityMag, float intensityOffset){ m_fIntensityMag = intensityMag; m_fIntensityOffset = intensityOffset; }
+        float GetOriginalIntensityVal(unsigned short usPixVal); //regarding m_fIntensityMag and m_fIntensityOffset
+        unsigned short GetWrappingIntensityVal(float fPixVal);
+
+	unsigned short* m_pData; // 0 - 65535
+
+	QPixmap* m_pPixmap; //Actually, no need!
+	QImage m_QImage;
+	//QPainter* m_pPainter;
+
+	bool LoadRawImage(const char *filePath, int width, int height);
+	bool LoadRawImage(const char *filePath, int width, int height, int headerOffset);
+	bool LoadRawImage(const char *filePath, int width, int height, int headerOffset, bool bInvert);
+	bool CopyFromBuffer(unsigned short* pImageBuf, int width, int height);
+	bool CloneImage(YK16GrayImage& other);
+
+	bool CreateImage(int width, int height, unsigned short usVal);
+
+	bool FillPixMap(int winMid, int winWidth);
+	bool FillPixMapMinMax(int winMin, int winMax); //0-65535 �� window level
+        
+        bool FillPixMapDose(float normval);
+        bool FillPixMapDose(); //m_fNormValue
+        void SetNormValueOriginal(float normval);
+
+        QColor GetColorFromDosePerc(float percVal);
+        bool FillPixMapGamma();
+        QColor GetColorFromGamma(float gammaVal);        
+
+	bool FillPixMapDual(int winMid1, int winMid2,int winWidth1, int winWidth2);
+	bool FillPixMapMinMaxDual(int winMin1, int winMin2, int winMax1, int winMax2); //0-65535 �� window level
+
+	bool SaveDataAsRaw (const char *filePath);
+	//bool DrawToLabel(QLabel* lbDisplay);
+
+	bool IsEmpty();
+	bool ReleaseBuffer();
+
+	//bool CalcImageInfo (double& meanVal, double& STDV, double& minVal, double& maxVal);
+	bool CalcImageInfo ();
+	double CalcAveragePixelDiff(YK16GrayImage& other);
+
+	bool DoPixelReplacement(std::vector<BADPIXELMAP>& vPixelMapping); //based on pixel mapping information, some bad pixels will be replaced with median pixel value near by
+
+	static void CopyYKImage2ItkImage(YK16GrayImage* pYKImage, UnsignedShortImageType::Pointer& spTarImage);
+	static void CopyItkImage2YKImage(UnsignedShortImageType::Pointer& spSrcImage, YK16GrayImage* pYKImage);
+
+	QString m_strFilePath;
+
+	double m_fPixelMean;
+	double m_fPixelSD;
+	double m_fPixelMin;
+	double m_fPixelMax;
+
+	static void Swap(YK16GrayImage* pImgA, YK16GrayImage* pImgB);
+
+	QRect m_rtROI;
+	bool setROI(int left, int top, int right, int bottom); //if there is error, go to default: entire image
+	bool CalcImageInfo_ROI();
+	double m_fPixelMean_ROI;
+	double m_fPixelSD_ROI;
+	double m_fPixelMin_ROI;
+	double m_fPixelMax_ROI;
+	bool m_bDrawROI;
+
+	void DrawROIOn(bool bROI_Draw); //only rectangle
+
+
+	//Elekta CBCT recon
+	char* m_pElektaHisHeader;
+	void CopyHisHeader(const char *hisFilePath);
+	//bool SaveDataAsHis (const char *filePath);
+	bool SaveDataAsHis( const char *filePath, bool bInverse );
+	bool m_bShowInvert;
+
+	void MultiplyConstant(double multiplyFactor);
+
+	void SetSpacing(double spacingX, double spacingY)
+	{
+		m_fSpacingX = spacingX;
+		m_fSpacingY = spacingY;
+	};
+        void SetOrigin(double originX, double originY)
+        {
+            m_fOriginX = originX;
+            m_fOriginY = originY;
+        };
+
+	QPoint m_ptProfileProbe; //Mouse Clicked Position --> Data
+	bool m_bDrawProfileX;
+	bool m_bDrawProfileY;
+
+	QPoint m_ptFOVCenter; // data pos
+	int m_iFOVRadius;//data pos (pixel)
+	bool m_bDrawFOVCircle;
+
+	int m_iTableTopPos;//data pos
+	bool m_bDrawTableLine;
+
+	QPoint m_ptCrosshair; //data position
+	bool m_bDrawCrosshair;
+
+	////ZOOM and PAN function. Using these information below, prepare the m_QImage for displaying 
+	//in qlabel in FillPixMap function
+	int m_iOffsetX; //for Pan function.. this is data based offset
+	int m_iOffsetY;
+	void SetOffset(int offsetX, int offsetY){m_iOffsetX = offsetX; m_iOffsetY = offsetY;}
+	double m_fZoom;
+	void SetZoom(double fZoom);
+	unsigned short GetPixelData(int x, int y);
+        unsigned short GetCrosshairPixelData();//Get pixel data of crosshair
+        float GetCrosshairOriginalData();//Get pixel data of crosshair
+        float GetCrosshairPercData();//Get pixel data of crosshair
+
+	//SPLIT VIEW
+	QPoint m_ptSplitCenter; //Fixed image with Moving image. center is based on dataPt.//Fixed Image: Left Top + Right Bottom, Moving: Right Top + Left Bottom        
+	int m_enSplitOption;
+	//This cetner is moved while Left Dragging //All split and crosshair are data point based!
+	void SetSplitOption(enSplitOption option) {m_enSplitOption = option;}
+	void SetSplitCenter(QPoint& ptSplitCenter);	//From mouse event, data point	
+	//void SetSplitCenter(int centerX, int centerY) {m_ptSplitCenter.setX(centerX); m_ptSplitCenter.setY(centerY);}//From mouse event, data point
+	bool ConstituteFromTwo(YK16GrayImage& YKImg1,YK16GrayImage& YKImg2); //YKImg1 and two should be in exactly same dimension and spacing
+	bool isPtInFirstImage(int dataX, int dataY);
+
+	void SetProfileProbePos(int dataX, int dataY);                
+        void SetCrosshairPosPhys(float physX, float physY, enPLANE plane);
+
+	unsigned short GetProfileProbePixelVal();	
+	void GetProfileData(int dataX, int dataY, QVector<double>& vTarget, enProfileDirection direction); 
+	void GetProfileData(QVector<double>& vTarget, enProfileDirection direction);
+       
+
+	void EditImage_Flip();
+	void EditImage_Mirror();
+
+	void MedianFilter(int iMedianSizeX, int iMedianSizeY);
+
+	double m_fResampleFactor;//if it is not the 1.0, the data is already resampled.	
+
+	UnsignedShortImageType::Pointer CloneItkImage();
+	void ResampleImage(double fResampleFactor);
+
+	void UpdateFromItkImage(UnsignedShortImageType::Pointer& spRefItkImg);
+	void UpdateFromItkImageFloat(FloatImageType2D::Pointer& spRefItkImg);
+        void UpdateFromItkImageFloat(FloatImageType2D::Pointer& spRefItkImg, float fIntenistyMag, float fIntensityOffset, bool bYFlip= false); 
+        void UpdateToItkImageFloat(FloatImageType2D::Pointer& spRefItkImg); //to be implemented
+
+	void InvertImage();
+
+	QString m_strTimeStamp; //HHMMSSFFF (9digits)
+	int m_iProcessingElapsed; //processing time in ms
+
+	//will be added later
+	/*void EditImage_CW90();
+	void EditImage_CCW90();
+	void EditImage_Rotation(double angle);*/
+
+	std::vector<QPoint> m_vMarker;//position in data dimension (not either physical nor display)
+	void AddMarkerPos(int dataX, int dataY);
+	void ClearMarkerPos();
+	bool m_bDrawMarkers;
+	int m_iNumOfDispMarker; //for display      e.g.)  0,1,2,3,4
+	int m_iNumOfTrackMarker; //for tracking   GetMeanPos  e.g.)  0,1,2 only
+	QPoint GetMeanPos(); //m_iNumOfTrackMarker based
+
+	//Reference marker group
+	std::vector<QPoint> m_vMarkerRef;//position in data dimension (not either physical nor display)
+	std::vector<bool> m_bvRefOutOfRange;//leave a tag when calculated 2D ref position is out of image
+	void AddMarkerPosRef(int dataX, int dataY);
+	void ClearMarkerPosRef();
+	bool m_bDrawMarkersRef;	
+	QPoint GetMeanPosRef(); //m_iNumOfDispMarker based
+	bool existRefMarkers();        
+
+	//should be implemented later
+	double m_track_priorErr;
+	double m_track_motionErr;
+	double m_track_CC_penalty;	
+
+	//Below are the flexmap related stuffs
+	float m_fMVGantryAngle;
+	float m_fPanelOffsetX; //mm
+	float m_fPanelOffsetY;//mm
+	bool m_bKVOn;
+	bool m_bMVOn;
+	int m_iXVI_ElapseMS;
+
+	void UpdateTrackingData(YK16GrayImage* pYKProcessedImage);
+	bool m_bDraw8Bit; //if some processing is done by 8bit
+
+        bool m_bDrawOverlayText;
+        QString m_strOverlayText;
+	
+        //2D points in data map. only outer contour points are included here after trimming out.
+        //std::vector<QPoint> m_vContourROI; //later it will be array or linked list to display mutliple ROIs
+
+        //std::vector<std::vector<QPoint>*> m_vvContourROI;
+        //std::vector<bool> m_vbDrawContourROI;
+        //bool m_bDrawContours;
+
+        std::vector<CContourROI*> m_vvContourROI;
+        void AddContourROI(QString& strROIName, bool bDisplay, QRgb color, int thickness, std::vector<QPoint>& vContourPts);
+        void ClearContourROI();// delete all the data of contourROI
+        bool SetDisplayStatus(QString& strROIName, bool bDisplay);
+        //void GetColorSingleROI()
+
+
+        vector<VEC3D> m_vColorTable;
+        //vector<VEC3D> m_vColorTableGammaLow;
+        //vector<VEC3D> m_vColorTableGammaHigh;
+
+        void SetColorTable(vector<VEC3D>& vInputColorTable);
+
+        
+};
\ No newline at end of file
diff --git a/src/plastimatch/standalone/YKThreadRegi.cpp b/src/plastimatch/standalone/YKThreadRegi.cpp
new file mode 100644
index 0000000..70001b3
--- /dev/null
+++ b/src/plastimatch/standalone/YKThreadRegi.cpp
@@ -0,0 +1,93 @@
+#include "YKThreadRegi.h"
+#include "register_gui.h"
+
+#include "registration.h"
+#include "registration_data.h"
+#include "registration_parms.h"
+
+#include "plm_exception.h"
+
+#include <QTime>
+
+using namespace std;
+
+YKThreadRegi::YKThreadRegi(register_gui* pParent, QString& strPathCommand, int iRowIndex)
+{     
+    m_strCommandFilePath = strPathCommand;
+    m_iProcessingTime = 0;  
+    m_iIndex = iRowIndex;
+
+    m_pParent = pParent;
+}
+
+YKThreadRegi::~YKThreadRegi()
+{
+  
+}
+
+void YKThreadRegi::run()//called by thread.start()
+{    
+    if (m_pParent == NULL)
+    {
+        exec();
+        return;
+    }        
+
+    m_pParent->m_mutex.lock(); //maybe not needed just for copying
+    if (m_iIndex >= m_pParent->m_vRegiQue.size())
+    {
+        m_pParent->m_mutex.unlock(); //maybe not needed just for copying    
+        exec();
+        return;
+    }
+
+    m_pParent->m_vRegiQue.at(m_iIndex).m_iStatus = ST_PENDING;
+    m_pParent->UpdateTable_Que();
+
+    m_pParent->m_mutex.unlock(); //maybe not needed just for copying        
+
+    std::string strPath = m_strCommandFilePath.toLocal8Bit().constData();
+    Registration reg;
+    if (reg.set_command_file(strPath) < 0) {
+        printf("Error.  could not load %s as command file.\n",
+            strPath.c_str());
+    }
+
+    std::string strFixed = reg.get_registration_parms()->get_fixed_fn(); //  return d_ptr->rparms;
+    std::string strMoving = reg.get_registration_parms()->get_moving_fn();   
+
+
+    QTime time;
+    time.start();
+    try {
+        reg.do_registration();
+    }
+    catch (Plm_exception e) {
+        printf("Your error was %s", e.what());
+
+        m_pParent->m_mutex.lock();        
+        m_iProcessingTime = time.elapsed();
+        m_pParent->m_vRegiQue.at(m_iIndex).m_iStatus = ST_ERROR;
+        m_pParent->m_vRegiQue.at(m_iIndex).m_fProcessingTime = m_iProcessingTime / 1000.0;
+        m_pParent->m_mutex.unlock();
+        exec();
+        return;
+    }
+
+    m_iProcessingTime = time.elapsed();
+
+    /*QString strDisp = "Done: " + QString::number(m_iProcessingTime/1000, 'f', 1) + " s";
+    m_pParent->SetTableText(m_iIndex, DEFAULT_NUM_COLUMN_MAIN - 1, strDisp);*/
+
+    m_pParent->m_mutex.lock(); //maybe not needed just for copying
+    m_pParent->m_vRegiQue.at(m_iIndex).m_iStatus = ST_DONE;
+    m_pParent->m_vRegiQue.at(m_iIndex).m_fProcessingTime = m_iProcessingTime / 1000.0;
+    m_pParent->UpdateTable_Que();
+    //m_pParent->m_vRegiQue.at(m_iIndex).m_fScore = 999.0;"not yet implemented"
+    m_pParent->CopyCommandFileToOutput(this->m_strCommandFilePath);
+
+    m_pParent->m_mutex.unlock(); //maybe not needed just for copying    
+    exec();//event roop Thread is still alive To quit thread, call exit()
+    //quit thread     
+}
+
diff --git a/src/plastimatch/standalone/YKThreadRegi.h b/src/plastimatch/standalone/YKThreadRegi.h
new file mode 100644
index 0000000..29a65a0
--- /dev/null
+++ b/src/plastimatch/standalone/YKThreadRegi.h
@@ -0,0 +1,28 @@
+#ifndef YKTHREADREGI_H
+#define YKTHREADREGI_H
+
+#include <QThread>
+#include <QString>
+
+using namespace std;
+class register_gui;
+
+class YKThreadRegi : public QThread
+{ 
+public:
+	//DPGMTracking* m_pParent;
+
+public:
+    //YKThreadRegi(register_gui* pParent, QString& strPathCommand);
+    YKThreadRegi(register_gui* pParent, QString& strPathCommand, int iRowIndex);
+    ~YKThreadRegi();    
+    
+    void run();
+
+    int m_iProcessingTime; //ms
+    QString m_strCommandFilePath;
+    register_gui* m_pParent;
+    int m_iIndex;
+};
+
+#endif // YKTHREADREGI_H
diff --git a/src/plastimatch/standalone/bspline_main.cxx b/src/plastimatch/standalone/bspline_main.cxx
index 8803b41..d1c9a77 100644
--- a/src/plastimatch/standalone/bspline_main.cxx
+++ b/src/plastimatch/standalone/bspline_main.cxx
@@ -88,8 +88,7 @@ main (int argc, char* argv[])
         }
     } else {
         bxf = new Bspline_xform;
-        bspline_xform_initialize (
-            bxf,
+        bxf->initialize (
             fixed->origin,
             fixed->spacing,
             fixed->dim,
@@ -107,7 +106,7 @@ main (int argc, char* argv[])
 
     /* Save output transform */
     if (options.output_xf_fn) {
-        bspline_xform_save (bxf, options.output_xf_fn);
+        bxf->save (options.output_xf_fn);
     }
 
     /* Create vector field from bspline coefficients and save */
diff --git a/src/plastimatch/standalone/bspline_opts.cxx b/src/plastimatch/standalone/bspline_opts.cxx
index 6c335cf..c5e3d7b 100644
--- a/src/plastimatch/standalone/bspline_opts.cxx
+++ b/src/plastimatch/standalone/bspline_opts.cxx
@@ -58,7 +58,7 @@ bspline_opts_parse_args (Bspline_options* options, int argc, char* argv[])
 {
     int i, rc;
     Bspline_parms* parms = &options->parms;
-    Reg_parms* reg_parms = parms->reg_parms;
+    Regularization_parms* reg_parms = parms->reg_parms;
     Bspline_landmarks* blm = parms->blm;
 
     for (i = 1; i < argc; i++) {
@@ -181,6 +181,7 @@ bspline_opts_parse_args (Bspline_options* options, int argc, char* argv[])
 	    } else {
 		print_usage ();
 	    }
+            parms->metric_lambda[0] = 1.;
 	}
 	else if (!strcmp (argv[i], "-s")) {
 	    if (i == (argc-1) || argv[i+1][0] == '-') {
diff --git a/src/plastimatch/standalone/check_grad.cxx b/src/plastimatch/standalone/check_grad.cxx
index 9ea6a8b..e7580d2 100644
--- a/src/plastimatch/standalone/check_grad.cxx
+++ b/src/plastimatch/standalone/check_grad.cxx
@@ -2,6 +2,7 @@
    See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
    ----------------------------------------------------------------------- */
 #include "plm_config.h"
+#include <fstream>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -17,6 +18,7 @@
 #include "mha_io.h"
 #include "plm_clp.h"
 #include "plm_image.h"
+#include "print_and_exit.h"
 #include "registration_metric_type.h"
 #include "volume.h"
 #include "volume_grad.h"
@@ -31,8 +33,10 @@ enum Check_grad_process {
 class Check_grad_opts {
 public:
     std::string fixed_fn;
+    std::string fixed_roi_fn;
     std::string moving_fn;
     std::string input_xf_fn;
+    std::string input_search_dir_fn;
     std::string output_fn;
     const char* xpm_hist_prefix;
 
@@ -53,10 +57,6 @@ public:
 
 public:
     Check_grad_opts () {
-	fixed_fn = "";
-	moving_fn = "";
-	input_xf_fn = "";
-	output_fn = "";
         xpm_hist_prefix = 0;
 	factr = 0;
 	pgtol = 0;
@@ -72,7 +72,7 @@ public:
 	random_range[1] = 0;
         control_point_index = 514;  // -1;
         debug_dir = "";
-        bsp_implementation = '0';
+        bsp_implementation = '\0';
         bsp_threading = BTHR_CPU;
         bsp_metric = REGISTRATION_METRIC_MSE;
     }
@@ -99,6 +99,14 @@ check_gradient (
     parms->moving = moving;
     parms->moving_grad = moving_grad;
     parms->implementation = options->bsp_implementation;
+    parms->metric_type[0] = options->bsp_metric;
+
+    /* Maybe we got a roi too */
+    Plm_image::Pointer pli_fixed_roi;
+    if (options->fixed_roi_fn != "") {
+        pli_fixed_roi = Plm_image::New (options->fixed_roi_fn);
+        parms->fixed_roi = pli_fixed_roi->get_volume_uchar().get();
+    }
 
     /* Set extra debug stuff, if desired */
     if (options->debug_dir != "") {
@@ -114,8 +122,7 @@ check_gradient (
         bxf = bspline_xform_load (options->input_xf_fn.c_str());
     } else {
         bxf = new Bspline_xform;
-        bspline_xform_initialize (
-            bxf,
+        bxf->initialize (
             fixed->origin,
             fixed->spacing,
             fixed->dim,
@@ -158,10 +165,26 @@ check_gradient (
 
     /* Save a copy of score and gradient */
     for (i = 0; i < bxf->num_coeff; i++) {
-        grad[i] = bst->ssd.grad[i];
+        grad[i] = bst->ssd.total_grad[i];
     }
     score = bst->ssd.score;
 
+    /* If a search dir was specified, use that instead of the gradient */
+    if (options->input_search_dir_fn != "") {
+        std::ifstream ifs (options->input_search_dir_fn.c_str());
+        if (!ifs) {
+            print_and_exit ("Failure opening %s for read\n",
+                options->input_search_dir_fn.c_str());
+        }
+        for (i = 0; i < bxf->num_coeff; i++) {
+            ifs >> grad[i];
+            if (!ifs) {
+                print_and_exit ("Incomplete search_dir in %s\n", 
+                    options->input_search_dir_fn.c_str());
+            }
+        }
+    }
+
     if (options->output_fn.empty()) {
         fp = stdout;
     } else {
@@ -306,7 +329,11 @@ parse_fn (
         "create various debug files", 1, "");
     parser->add_long_option ("H", "histogram-prefix", 
         "create MI histograms files with the specified prefix", 1, "");
-
+    parser->add_long_option ("", "input-search-dir",
+        "input file containing search direction", 1, "");
+    parser->add_long_option ("", "fixed-roi", 
+        "input file for fixed image roi", 1, "");
+        
     /* Parse the command line arguments */
     parser->parse (argc,argv);
 
@@ -365,12 +392,18 @@ parse_fn (
     if (parser->have_option ("input-xform")) {
         parms->input_xf_fn = parser->get_string("input-xform");
     }
+    if (parser->have_option ("input-search-dir")) {
+        parms->input_search_dir_fn = parser->get_string("input-search-dir");
+    }
     if (parser->have_option ("output")) {
         parms->output_fn = parser->get_string("output");
     }
     if (parser->have_option ("debug-dir")) {
         parms->debug_dir = parser->get_string ("debug-dir");
     }
+    if (parser->have_option ("fixed-roi")) {
+        parms->fixed_roi_fn = parser->get_string ("fixed-roi");
+    }
     if (parser->have_option ("histogram-prefix")) {
         parms->xpm_hist_prefix 
             = parser->get_string("histogram-prefix").c_str();
diff --git a/src/plastimatch/standalone/cmd_prompt_launcher.cxx b/src/plastimatch/standalone/cmd_prompt_launcher.cxx
index 7594263..88cb2ee 100644
--- a/src/plastimatch/standalone/cmd_prompt_launcher.cxx
+++ b/src/plastimatch/standalone/cmd_prompt_launcher.cxx
@@ -4,31 +4,31 @@
 
 #include <stdlib.h>
 
-#if defined (_WIN32)
-#include <windows.h>
-#include <direct.h>
-#define GetCurrentDir _getcwd
-#endif
-
+#if defined (_WIN32)
+#include <windows.h>
+#include <direct.h>
+#define GetCurrentDir _getcwd
+#endif
+
 #include <stdio.h>  /* defines FILENAME_MAX */
 
 int
 main (int argc, char* argv[])
 {
-    #if defined (_WIN32)
-    
-    char cCurrentPath[FILENAME_MAX];
-
-    if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath)))
-    {
-        return errno;
-    }
-    printf(cCurrentPath);
-    printf("\n");
-
-    ShellExecute(0, "open", "cmd", 0, (LPCSTR)cCurrentPath, SW_SHOW);
-    #else
-    printf ("Sorry, CMD prompt launcher is only for windows system.\n");
+    #if defined (_WIN32)
+    
+    char cCurrentPath[FILENAME_MAX];
+
+    if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath)))
+    {
+        return errno;
+    }
+    printf(cCurrentPath);
+    printf("\n");
+
+    ShellExecute(0, "open", "cmd", 0, (LPCSTR)cCurrentPath, SW_SHOW);
+    #else
+    printf ("Sorry, CMD prompt launcher is only for windows system.\n");
     #endif
    
     return 0;
diff --git a/src/plastimatch/standalone/colormap_customgamma.txt b/src/plastimatch/standalone/colormap_customgamma.txt
new file mode 100644
index 0000000..13458a0
--- /dev/null
+++ b/src/plastimatch/standalone/colormap_customgamma.txt
@@ -0,0 +1,100 @@
+0	0	1
+0	0.015873016	0.992063492
+0	0.031746032	0.984126984
+0	0.047619048	0.976190476
+0	0.063492063	0.968253968
+0	0.079365079	0.96031746
+0	0.095238095	0.952380952
+0	0.111111111	0.944444444
+0	0.126984127	0.936507937
+0	0.142857143	0.928571429
+0	0.158730159	0.920634921
+0	0.174603175	0.912698413
+0	0.19047619	0.904761905
+0	0.206349206	0.896825397
+0	0.222222222	0.888888889
+0	0.238095238	0.880952381
+0	0.253968254	0.873015873
+0	0.26984127	0.865079365
+0	0.285714286	0.857142857
+0	0.301587302	0.849206349
+0	0.317460317	0.841269841
+0	0.333333333	0.833333333
+0	0.349206349	0.825396825
+0	0.365079365	0.817460317
+0	0.380952381	0.80952381
+0	0.396825397	0.801587302
+0	0.412698413	0.793650794
+0	0.428571429	0.785714286
+0	0.444444444	0.777777778
+0	0.46031746	0.76984127
+0	0.476190476	0.761904762
+0	0.492063492	0.753968254
+0	0.507936508	0.746031746
+0	0.523809524	0.738095238
+0	0.53968254	0.73015873
+0	0.555555556	0.722222222
+0	0.571428571	0.714285714
+0	0.587301587	0.706349206
+0	0.603174603	0.698412698
+0	0.619047619	0.69047619
+0	0.634920635	0.682539683
+0	0.650793651	0.674603175
+0	0.666666667	0.666666667
+0	0.682539683	0.658730159
+0	0.698412698	0.650793651
+0	0.714285714	0.642857143
+0	0.73015873	0.634920635
+0	0.746031746	0.626984127
+0	0.761904762	0.619047619
+0	0.777777778	0.611111111
+1	0.708333333	0
+1	0.6875	0
+1	0.666666667	0
+1	0.645833333	0
+1	0.625	0
+1	0.604166667	0
+1	0.583333333	0
+1	0.5625	0
+1	0.541666667	0
+1	0.520833333	0
+1	0.5	0
+1	0.479166667	0
+1	0.458333333	0
+1	0.4375	0
+1	0.416666667	0
+1	0.395833333	0
+1	0.375	0
+1	0.354166667	0
+1	0.333333333	0
+1	0.3125	0
+1	0.291666667	0
+1	0.270833333	0
+1	0.25	0
+1	0.229166667	0
+1	0.208333333	0
+1	0.1875	0
+1	0.166666667	0
+1	0.145833333	0
+1	0.125	0
+1	0.083333333	0
+1	0.041666667	0
+1	0	0
+0.958333333	0	0
+0.916666667	0	0
+0.875	0	0
+0.833333333	0	0
+0.791666667	0	0
+0.75	0	0
+0.708333333	0	0
+0.666666667	0	0
+0.625	0	0
+0.583333333	0	0
+0.541666667	0	0
+0.5	0	0
+0.458333333	0	0
+0.416666667	0	0
+0.375	0	0
+0.333333333	0	0
+0.291666667	0	0
+0.25	0	0
diff --git a/src/plastimatch/standalone/colormap_jet.txt b/src/plastimatch/standalone/colormap_jet.txt
new file mode 100644
index 0000000..fa3342e
--- /dev/null
+++ b/src/plastimatch/standalone/colormap_jet.txt
@@ -0,0 +1,64 @@
+0	0	0.5625
+0	0	0.625
+0	0	0.6875
+0	0	0.75
+0	0	0.8125
+0	0	0.875
+0	0	0.9375
+0	0	1
+0	0.0625	1
+0	0.125	1
+0	0.1875	1
+0	0.25	1
+0	0.3125	1
+0	0.375	1
+0	0.4375	1
+0	0.5	1
+0	0.5625	1
+0	0.625	1
+0	0.6875	1
+0	0.75	1
+0	0.8125	1
+0	0.875	1
+0	0.9375	1
+0	1	1
+0.0625	1	0.9375
+0.125	1	0.875
+0.1875	1	0.8125
+0.25	1	0.75
+0.3125	1	0.6875
+0.375	1	0.625
+0.4375	1	0.5625
+0.5	1	0.5
+0.5625	1	0.4375
+0.625	1	0.375
+0.6875	1	0.3125
+0.75	1	0.25
+0.8125	1	0.1875
+0.875	1	0.125
+0.9375	1	0.0625
+1	1	0
+1	0.9375	0
+1	0.875	0
+1	0.8125	0
+1	0.75	0
+1	0.6875	0
+1	0.625	0
+1	0.5625	0
+1	0.5	0
+1	0.4375	0
+1	0.375	0
+1	0.3125	0
+1	0.25	0
+1	0.1875	0
+1	0.125	0
+1	0.0625	0
+1	0	0
+0.9375	0	0
+0.875	0	0
+0.8125	0	0
+0.75	0	0
+0.6875	0	0
+0.625	0	0
+0.5625	0	0
+0.5	0	0
diff --git a/src/plastimatch/standalone/create_wed_volumes.cxx b/src/plastimatch/standalone/create_wed_volumes.cxx
deleted file mode 100644
index 37a962f..0000000
--- a/src/plastimatch/standalone/create_wed_volumes.cxx
+++ /dev/null
@@ -1,107 +0,0 @@
-#include "volume.h"
-#include "wed_parms.h"
-
-//Should eventually be made into a class - for now, just some clean up.
-
-Volume* create_wed_volume (Wed_Parms* parms, Rpl_volume* rpl_vol)
-{
-
-   /* water equivalent depth volume has the same x,y dimensions as the rpl
-     * volume. Note: this means the wed x,y dimensions are equal to the
-     * aperture dimensions and the z-dimension is equal to the sampling
-     * resolution chosen for the rpl */
-    plm_long wed_dims[3];
-
-    Volume *vol = rpl_vol->get_vol ();
-    wed_dims[0] = vol->dim[0];
-    wed_dims[1] = vol->dim[1];
-    wed_dims[2] = vol->dim[2];
-
-    /////////////////////////////
-    //Should be insenstive to aperture rotation?
-    /*
-    Proj_volume *proj_vol = d_ptr->proj_vol;
-    double iso_src_vec[3];   //vector from isocenter to source
-    proj_vol->get_proj_matrix()->get_nrm(iso_src_vec);
-    */
-
-    float xoff = -(vol->dim[0] - parms->ic[0]);
-    float yoff = -(vol->dim[1] - parms->ic[1]);
-
-    float wed_off[3] = {xoff, yoff, 0.0f};
-    float wed_ps[3] = {1.0f, 1.0f, 1.0f};
-
-    return new Volume (wed_dims, wed_off, wed_ps, NULL, PT_FLOAT, 1);
-}
-
-static Volume*
-create_dew_volume (Wed_Parms* parms, Rt_plan *scene)
-{
-    Volume::Pointer patient_vol = scene->get_patient_volume();
-
-    float dew_off[3];
-    dew_off[0] = patient_vol->origin[0];
-    dew_off[1] = patient_vol->origin[1];
-    dew_off[2] = patient_vol->origin[2];
-
-    float dew_ps[3];
-    dew_ps[0] = patient_vol->spacing[0];
-    dew_ps[1] = patient_vol->spacing[1];
-    dew_ps[2] = patient_vol->spacing[2];
-
-    plm_long dew_dims[3];
-    dew_dims[0] = patient_vol->dim[0];
-    dew_dims[1] = patient_vol->dim[1];
-    dew_dims[2] = patient_vol->dim[2];
-
-    //If output volume dimensions were set in .cfg file, use these.
-    if (parms->dew_dim[0]!=-999.) {dew_dims[0]=parms->dew_dim[0];}
-    if (parms->dew_dim[1]!=-999.) {dew_dims[1]=parms->dew_dim[1];}
-    if (parms->dew_dim[2]!=-999.) {dew_dims[2]=parms->dew_dim[2];}
-
-    if (parms->dew_origin[0]!=-999.) {dew_off[0]=parms->dew_origin[0];}
-    if (parms->dew_origin[1]!=-999.) {dew_off[1]=parms->dew_origin[1];}
-    if (parms->dew_origin[2]!=-999.) {dew_off[2]=parms->dew_origin[2];}
-
-    if (parms->dew_spacing[0]!=-999.) {dew_ps[0]=parms->dew_spacing[0];}
-    if (parms->dew_spacing[1]!=-999.) {dew_ps[1]=parms->dew_spacing[1];}
-    if (parms->dew_spacing[2]!=-999.) {dew_ps[2]=parms->dew_spacing[2];}
-
-    return new Volume (dew_dims, dew_off, dew_ps, NULL, PT_FLOAT, 1);
-}
-
-Volume* create_proj_wed_volume (Rpl_volume* rpl_vol)
-{
-    float proj_wed_off[3] = {0.0f, 0.0f, 0.0f};
-    float proj_wed_ps[3] = {1.0f, 1.0f, 1.0f};
-    plm_long proj_wed_dims[3];
-
-    Volume *vol = rpl_vol->get_vol ();
-    proj_wed_dims[0] = vol->dim[0];
-    proj_wed_dims[1] = vol->dim[1];
-    proj_wed_dims[2] = 1;
-
-    return new Volume (proj_wed_dims, proj_wed_off, proj_wed_ps, NULL, PT_FLOAT, 1);
-}
-
-Volume* create_proj_sinogram_volume (Wed_Parms* parms, Volume *proj_wed_vol)
-{
-    float proj_wed_off[3];
-    proj_wed_off[0] = proj_wed_vol->origin[0];
-    proj_wed_off[1] = proj_wed_vol->origin[1];
-    proj_wed_off[2] = proj_wed_vol->origin[2];
-
-    float proj_wed_ps[3];
-    proj_wed_ps[0] = proj_wed_vol->spacing[0];
-    proj_wed_ps[1] = proj_wed_vol->spacing[1];
-    proj_wed_ps[2] = proj_wed_vol->spacing[2];
-
-    plm_long proj_wed_dims[3];
-    proj_wed_dims[0] = proj_wed_vol->dim[0];
-    proj_wed_dims[1] = proj_wed_vol->dim[1];
-    proj_wed_dims[2] = parms->sinogram_res;
-
-    return new Volume (proj_wed_dims, proj_wed_off, proj_wed_ps, NULL, PT_FLOAT, 1);
-}
-
-
diff --git a/src/plastimatch/standalone/drr_main.cxx b/src/plastimatch/standalone/drr_main.cxx
index 5d39eab..ec0b228 100644
--- a/src/plastimatch/standalone/drr_main.cxx
+++ b/src/plastimatch/standalone/drr_main.cxx
@@ -20,6 +20,7 @@
 #include "proj_image.h"
 #include "proj_matrix.h"
 #include "ray_trace.h"
+#include "string_util.h"
 #include "threading.h"
 #include "volume.h"
 
@@ -103,7 +104,7 @@ create_matrix_and_drr (
 {
     char mat_fn[256];
     char img_fn[256];
-    char multispectral_fn[256];
+    std::string details_fn;
     Proj_matrix *pmat = proj->pmat;
     double vup[3] = {
 	options->vup[0],
@@ -132,7 +133,7 @@ create_matrix_and_drr (
 
     /* Create projection matrix */
     sprintf (mat_fn, "%s%04d.txt", options->output_prefix, a);
-    proj_matrix_set (pmat, cam, tgt, vup, sid, ic, ps, ires);
+    pmat->set (cam, tgt, vup, sid, ic, ps, ires);
 
     if (options->output_format == OUTPUT_FORMAT_PFM) {
 	sprintf (img_fn, "%s%04d.pfm", options->output_prefix, a);
@@ -141,14 +142,18 @@ create_matrix_and_drr (
     } else {
 	sprintf (img_fn, "%s%04d.raw", options->output_prefix, a);
     }
-    sprintf (multispectral_fn, "%s%04d.msd", options->output_prefix, a);
+
+    if (options->output_details_prefix != "") {
+        options->output_details_fn = string_format ("%s%04d.txt",
+            options->output_details_prefix.c_str(), a);
+    }
 
     if (options->geometry_only) {
-	proj_image_save (proj, 0, mat_fn);
+	proj->save (0, mat_fn);
     } else {
 	drr_render_volume_perspective (proj, vol, ps, dev_state, options);
 	timer->start ();
-	proj_image_save (proj, img_fn, mat_fn);
+	proj->save (img_fn, mat_fn);
 	printf ("I/O time: %f sec\n", timer->report ());
     }
 
@@ -203,7 +208,7 @@ drr_render_volume (Volume* vol, Drr_options* options)
     /* Otherwise, loop through camera angles */
     else {
 	for (a = 0; a < options->num_angles; a++) {
-	    double angle = a * options->angle_diff;
+	    double angle = options->start_angle + a * options->angle_diff;
 	    double cam[3];
 	    double nrm[3];
 
diff --git a/src/plastimatch/standalone/drr_opts.cxx b/src/plastimatch/standalone/drr_opts.cxx
index a17e403..b2a8e57 100644
--- a/src/plastimatch/standalone/drr_opts.cxx
+++ b/src/plastimatch/standalone/drr_opts.cxx
@@ -19,6 +19,7 @@ print_usage (void)
 	"Usage: drr [options] [infile]\n"
 	"Options:\n"
 	" -A hardware       Either \"cpu\" or \"cuda\" (default=cpu)\n"
+	" -y angle          Gantry angle for source\n"
 	" -a num            Generate num equally spaced angles\n"
 	" -N angle          Difference between neighboring angles (in degrees)\n"
 	" -nrm \"x y z\"      Set the normal vector for the panel\n"
@@ -31,7 +32,7 @@ print_usage (void)
 	" -z \"s1 s2\"        Set the physical size of imager (in mm)\n"
 	" -w \"r1 r2 c1 c2\"  Only produce image for pixes in window (in pix)\n"
 	" -t outformat      Select output format: pgm, pfm or raw\n"
-	" -S outfile        Output ray tracing details\n"
+	" -S outprefix      Output ray tracing details\n"
 	//" -S                Output multispectral output files\n"
 	//" -i algorithm      Choose algorithm {exact,uniform,tri_exact,tri_approx}\n"
 	" -i algorithm      Choose algorithm {exact,uniform}\n"
@@ -52,13 +53,15 @@ drr_opts_init (Drr_options* options)
     options->image_resolution[1] = 128;
     options->image_size[0] = 600;
     options->image_size[1] = 600;
+    options->have_image_center = 0;
+    options->have_image_window = 0;
     options->isocenter[0] = 0.0f;
     options->isocenter[1] = 0.0f;
     options->isocenter[2] = 0.0f;
-    options->have_image_center = 0;
-    options->have_image_window = 0;
-    options->have_angle_diff = 0;
+
+    options->start_angle = 0.f;
     options->num_angles = 1;
+    options->have_angle_diff = 0;
     options->angle_diff = 1.0f;
 
     options->have_nrm = 0;
@@ -76,6 +79,7 @@ drr_opts_init (Drr_options* options)
     options->exponential_mapping = 0;
     options->output_format= OUTPUT_FORMAT_PFM;
     options->preprocess_attenuation = true;
+    options->output_details_prefix = "";
     options->output_details_fn = "";
     options->algorithm = DRR_ALGORITHM_EXACT;
     options->input_file = 0;
@@ -156,6 +160,13 @@ parse_args (Drr_options* options, int argc, char* argv[])
 	    if (++i >= argc) { print_usage(); }
 	    options->output_prefix = strdup (argv[i]);
 	}
+	else if (!strcmp (argv[i], "-y")) {
+	    if (++i >= argc) { print_usage(); }
+	    rc = sscanf (argv[i], "%g" , &options->start_angle);
+	    if (rc != 1) {
+		print_usage ();
+	    }
+	}
 	else if (!strcmp (argv[i], "-a")) {
 	    if (++i >= argc) { print_usage(); }
 	    rc = sscanf (argv[i], "%d" , &options->num_angles);
@@ -245,17 +256,17 @@ parse_args (Drr_options* options, int argc, char* argv[])
 	    }
 	}
 	else if (!strcmp (argv[i], "-w")) {
-	    /* Note: user inputs row, then column.  But internally they 
-	       are stored as column, then row. */
+	    /* Note: user inputs row start, row end, 
+               column start, column end */
 	    if (++i >= argc) { print_usage(); }
 	    rc = sscanf (argv[i], "%d %d %d %d",
-		&options->image_window[2],
-		&options->image_window[3],
 		&options->image_window[0],
-		&options->image_window[1]);
+		&options->image_window[1],
+		&options->image_window[2],
+		&options->image_window[3]);
 	    if (rc == 2) {
-		options->image_window[0] = options->image_window[2];
-		options->image_window[1] = options->image_window[3];
+		options->image_window[2] = options->image_window[0];
+		options->image_window[3] = options->image_window[1];
 	    } else if (rc != 4) {
 		print_usage ();
 	    }
@@ -287,7 +298,7 @@ parse_args (Drr_options* options, int argc, char* argv[])
 	}
 	else if (!strcmp (argv[i], "-S")) {
 	    if (++i >= argc) { print_usage(); }
-	    options->output_details_fn = argv[i];
+	    options->output_details_prefix = argv[i];
 	}
 	else if (!strcmp (argv[i], "-e")) {
 	    options->exponential_mapping = 1;
diff --git a/src/plastimatch/standalone/extract_contour.cxx b/src/plastimatch/standalone/extract_contour.cxx
deleted file mode 100644
index b4c7ea2..0000000
--- a/src/plastimatch/standalone/extract_contour.cxx
+++ /dev/null
@@ -1,114 +0,0 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-#include "plm_config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "itkContourExtractor2DImageFilter.h"
-#include "itkImage.h"
-#include "itkImageFileReader.h"
-#include "itkImageFileWriter.h"
-#include "itkImageLinearIteratorWithIndex.h"
-#include "itkImageSliceConstIteratorWithIndex.h"
-
-#include "itk_image_load.h"
-#include "itk_image_type.h"
-#include "slice_extract.h"
-
-/* -----------------------------------------------------------------------
-    Definitions
-   ----------------------------------------------------------------------- */
-typedef itk::ContourExtractor2DImageFilter<FloatImage2DType> ContourType;
-typedef ContourType::VertexType VertexType;
-typedef itk::ImageSliceConstIteratorWithIndex<FloatImageType> IteratorType;
-
-int
-main (int argc, char *argv[])
-{
-    FILE* fp;
-    FILE* file;
-    FloatImageType::IndexType k;
-    k[0] = 0;
-
-    if (argc < 2) {
-        printf ("Usage: extract_contour input_img [output_file]");
-        exit (-1);
-    }
-
-    FloatImageType::Pointer volume = itk_image_load_float (argv[1], 0);
-
-    IteratorType itSlice (volume, volume->GetLargestPossibleRegion ());
-    itSlice.SetFirstDirection (0);
-    itSlice.SetSecondDirection (1);
-
-    if (argc < 3) {
-        fp = fopen ("vertices_pixelcoord.txt", "w");
-        file = fopen ("vertices_physcoord.txt", "w");
-    } else {
-        char filename[50] = "";
-        char filename2[50] = "";
-        strcpy (filename, argv[2]);
-        strcat (filename, "_pixelcoord.txt");
-        strcpy (filename2, argv[2]);
-        strcat (filename2, "_physcoord.txt");
-        fp = fopen (filename, "w");
-        file = fopen (filename2, "w");
-    }
-
-    if (!fp || !file) {
-	if (fp) fclose (fp);
-	if (file) fclose (file);
-        printf ("Could not open vertices file for writing\n");
-        return -1;
-    }
-
-    while (!itSlice.IsAtEnd ()) {
-        k = itSlice.GetIndex ();
-        //printf("%2d\n", k[2]);
-
-        FloatImage2DType::Pointer slice;
-        slice = slice_extract (volume, k[2]);
-
-        ContourType::Pointer contour = ContourType::New ();
-
-        contour->SetContourValue (0.5);
-        contour->SetInput (slice);
-
-        try
-        {
-            contour->Update ();
-        }
-        catch (itk::ExceptionObject &err)
-        {
-            std::cout << "ExceptionObject caught !" << std::endl;
-            std::cout << err << std::endl;
-            return -1;
-        }
-
-        for (unsigned int i = 0; i < contour->GetNumberOfOutputs (); i++) {
-            ContourType::VertexListConstPointer vertices = contour->GetOutput (i)->GetVertexList ();
-            /*fprintf(fp,"%s %d%s%d\n","Contour",k[2],".",i);*/
-            /*fprintf(fp,"%d%s%d\n",k[2],".",i);*/
-            //fprintf(fp,"\n");
-            fprintf (fp, "%s %s %s\n", "NaN", "NaN", "NaN");
-            fprintf (file, "%s %s %s\n", "NaN", "NaN", "NaN");
-            for (unsigned int j = 0; j < vertices->Size (); j++) {
-                const VertexType& vertex = vertices->ElementAt (j);
-
-                fprintf (fp, "%.3f %.3f %2ld\n", vertex[0], vertex[1], k[2]);
-                fprintf (file, "%.3f %.3f %.3f \n", 
-		    vertex[0] * volume->GetSpacing()[0] + volume->GetOrigin()[0], 
-		    vertex[1] * volume->GetSpacing()[1] + volume->GetOrigin()[1], 
-		    k[2] * volume->GetSpacing()[2] + volume->GetOrigin()[2]);
-
-                //fprintf(fp,"%.3f %.3f %2d\n",vertex[0],vertex[1],k[2]*volume->GetSpacing()[2]);
-                //std::cout << vertex[0] <<" "<<vertex[1]<<" "<<k[2]<<std::endl;
-            }
-            //system("PAUSE");
-        }
-        itSlice.NextSlice ();
-    }
-    fclose (fp);
-    fclose (file);
-}
diff --git a/src/plastimatch/standalone/gamma_gui.cpp b/src/plastimatch/standalone/gamma_gui.cpp
new file mode 100644
index 0000000..c739ca6
--- /dev/null
+++ b/src/plastimatch/standalone/gamma_gui.cpp
@@ -0,0 +1,3353 @@
+#include "gamma_gui.h"
+#include <QString>
+#include <QFileDialog>
+#include <QListView>
+#include <QMessageBox>
+#include <fstream>
+#include "YK16GrayImage.h"
+
+#include "plm_image.h"
+#include "rt_study_metadata.h"
+#include "gamma_dose_comparison.h"
+
+#include <QFileInfo>
+#include "logfile.h"
+#include "pcmd_gamma.h"
+#include "print_and_exit.h"
+#include "plm_file_format.h"
+#include "rt_study.h"
+#include "qt_util.h"
+#include <QStandardItemModel>
+#include <QClipboard>
+
+#include "dcmtk_loader.h"
+#include "dcmtk_loader_p.h"
+#include "dcmtk_series.h"
+
+#include "dcmtk_config.h"
+#include "dcmtk/dcmdata/dctagkey.h"
+#include "dcmtk/dcmdata/dcsequen.h"
+#include "dcmtk/dcmdata/dcitem.h"
+#include "dcmtk/dcmdata/dcdeftag.h"
+
+#include <QDataStream>
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include "itkImageSliceIteratorWithIndex.h"
+#include "itkFlipImageFilter.h"
+
+
+gamma_gui::gamma_gui(QWidget *parent, Qt::WFlags flags)
+: QMainWindow(parent, flags)
+{
+    ui.setupUi(this);
+
+    m_pCurImageRef = new YK16GrayImage();
+    m_pCurImageComp = new YK16GrayImage();
+    m_pCurImageGamma3D = new YK16GrayImage();
+    m_pCurImageGamma2D = new YK16GrayImage();
+
+    QUTIL::LoadColorTable("colormap_jet.txt", m_vColormapDose);
+    QUTIL::LoadColorTable("colormap_customgamma.txt", m_vColormapGamma);
+    
+
+    m_pCurImageRef->SetColorTable(m_vColormapDose);
+    m_pCurImageComp->SetColorTable(m_vColormapDose);
+
+    m_pCurImageGamma3D->SetColorTable(m_vColormapGamma);
+    //m_pCurImageGamma3D->SetColorTable(m_vColormapGammaLow);
+
+    m_pCurImageGamma2D->SetColorTable(m_vColormapGamma);
+    //m_pCurImageGamma2D->SetColorTableGammaHigh(m_vColormapGammaLow);
+
+
+    connect(ui.labelReferDose, SIGNAL(Mouse_Pressed_Left()), this, SLOT(SLT_UpdateProbePosRef())); //added
+    connect(ui.labelCompDose, SIGNAL(Mouse_Pressed_Left()), this, SLOT(SLT_UpdateProbePosComp())); //added
+    connect(ui.labelGammaMap2D, SIGNAL(Mouse_Pressed_Left()), this, SLOT(SLT_UpdateProbePosGamma2D())); //added
+    connect(ui.labelGammaMap3D, SIGNAL(Mouse_Pressed_Left()), this, SLOT(SLT_UpdateProbePosGamma3D())); //added
+
+    connect(ui.labelReferDose, SIGNAL(Mouse_Left_DoubleClick()), this, SLOT(SLT_GoCenterPosRef())); //added
+    connect(ui.labelCompDose, SIGNAL(Mouse_Left_DoubleClick()), this, SLOT(SLT_GoCenterPosComp())); //added
+
+
+    connect(ui.labelReferDose, SIGNAL(Mouse_Wheel()), this, SLOT(SLT_MouseWheelUpdateRef())); //added
+    connect(ui.labelCompDose, SIGNAL(Mouse_Wheel()), this, SLOT(SLT_MouseWheelUpdateComp())); //added
+    connect(ui.labelGammaMap2D, SIGNAL(Mouse_Wheel()), this, SLOT(SLT_MouseWheelUpdateGamma2D())); //added
+    connect(ui.labelGammaMap3D, SIGNAL(Mouse_Wheel()), this, SLOT(SLT_MouseWheelUpdateGamma3D())); //added
+
+
+    connect(ui.labelReferDose, SIGNAL(Mouse_Move()), this, SLOT(SLT_UpdatePanSettingRef())); //added
+    connect(ui.labelCompDose, SIGNAL(Mouse_Move()), this, SLOT(SLT_UpdatePanSettingComp())); //added
+    connect(ui.labelGammaMap3D, SIGNAL(Mouse_Move()), this, SLOT(SLT_UpdatePanSettingGamma3D())); //added
+    connect(ui.labelGammaMap2D, SIGNAL(Mouse_Move()), this, SLOT(SLT_UpdatePanSettingGamma2D())); //added
+
+    connect(ui.labelReferDose, SIGNAL(Mouse_Pressed_Right()), this, SLOT(SLT_MousePressedRightRef())); //added
+    connect(ui.labelCompDose, SIGNAL(Mouse_Pressed_Right()), this, SLOT(SLT_MousePressedRightComp())); //added
+    connect(ui.labelGammaMap3D, SIGNAL(Mouse_Pressed_Right()), this, SLOT(SLT_MousePressedRightGamma3D())); //added
+    connect(ui.labelGammaMap2D, SIGNAL(Mouse_Pressed_Right()), this, SLOT(SLT_MousePressedRightGamma2D())); //added
+
+    connect(ui.labelReferDose, SIGNAL(Mouse_Released_Right()), this, SLOT(SLT_MouseReleasedRightRef())); //added
+    connect(ui.labelCompDose, SIGNAL(Mouse_Released_Right()), this, SLOT(SLT_MouseReleasedRightComp())); //added
+    connect(ui.labelGammaMap3D, SIGNAL(Mouse_Released_Right()), this, SLOT(SLT_MouseReleasedRightGamma3D())); //added
+    connect(ui.labelGammaMap2D, SIGNAL(Mouse_Released_Right()), this, SLOT(SLT_MouseReleasedRightGamma2D())); //added    
+
+
+    if (m_vColormapDose.size() < 1 || m_vColormapGamma.size() < 1 || m_vColormapGamma.size() < 1)
+    {
+        cout << "Fatal error!: colormap is not ready. Text files such as colormap_jet.txt should be checked." << endl;
+    }
+
+    m_pTableModel = NULL;
+
+    m_bGamma2DIsDone = false;
+
+    m_bMousePressedRightRef = false;
+    m_bMousePressedRightComp = false;
+    m_bMousePressedRightGamma3D = false;
+    m_bMousePressedRightGamma2D = false;
+
+    QUTIL::CreateItkDummyImg(m_spDummyLowMemImg, 10, 10, 10, 1.0);   
+
+    m_iLastLoadedIndex = -1;
+}
+
+gamma_gui::~gamma_gui()
+{
+    //delete m_pImgOffset;
+    //delete m_pImgGain;
+
+    //m_vPixelReplMap.clear(); //not necessary
+    //delete m_pView;
+     m_vRefDoseImages.clear();
+     m_vCompDoseImages.clear();
+     m_vGammaMapImages.clear();
+     
+    delete m_pCurImageRef;
+    delete m_pCurImageComp;
+    delete m_pCurImageGamma3D;
+    delete m_pCurImageGamma2D;
+
+    if (m_pTableModel != NULL)
+    {
+        delete m_pTableModel;
+        m_pTableModel = NULL;
+    }
+
+}
+
+void gamma_gui::SLT_Load_RD_Ref()
+{
+    QStringList tmpList = QFileDialog::getOpenFileNames(this, "Select one or more files to open", m_strPathInputDir, "3D dose file (*.dcm *.mha)");
+
+    int iFileCnt = tmpList.size();
+
+    if (iFileCnt < 1)
+        return;
+
+    ui.plainTextEdit_RD_Ref->clear();
+    m_strlistPath_RD_Original_Ref.clear();
+    m_strlistFileBaseName_Ref.clear();
+
+    m_strlistPath_RD_Original_Ref = tmpList;
+
+    for (int i = 0; i < iFileCnt; i++)
+    {
+        ui.plainTextEdit_RD_Ref->appendPlainText(m_strlistPath_RD_Original_Ref.at(i)); //just for display
+        QFileInfo tmpInfo = QFileInfo(m_strlistPath_RD_Original_Ref.at(i));
+        m_strlistFileBaseName_Ref.push_back(tmpInfo.completeBaseName());
+    }
+
+    QFileInfo finfo(m_strlistPath_RD_Original_Ref.at(0));
+    QDir crntDir = finfo.absoluteDir();
+    m_strPathInputDir = crntDir.absolutePath();
+}
+
+void gamma_gui::SLT_Load_RD_Comp()
+{
+    QStringList tmpList = QFileDialog::getOpenFileNames(this, "Select one or more files to open", m_strPathInputDir, "3D dose file (*.dcm *.mha)");
+
+    int iFileCnt = tmpList.size();
+
+    if (iFileCnt < 1)
+        return;
+    
+    ui.plainTextEdit_RD_Comp->clear();
+    m_strlistPath_RD_Original_Comp.clear();
+    m_strlistFileBaseName_Comp.clear();
+
+    m_strlistPath_RD_Original_Comp = tmpList;
+
+    for (int i = 0; i < iFileCnt; i++)
+    {
+        ui.plainTextEdit_RD_Comp->appendPlainText(m_strlistPath_RD_Original_Comp.at(i)); //just for display
+
+        QFileInfo tmpInfo = QFileInfo(m_strlistPath_RD_Original_Comp.at(i));
+
+        m_strlistFileBaseName_Comp.push_back(tmpInfo.completeBaseName());
+    }
+
+    QFileInfo finfo(m_strlistPath_RD_Original_Comp.at(0));
+    QDir crntDir = finfo.absoluteDir();
+    m_strPathInputDir = crntDir.absolutePath();
+}
+
+void gamma_gui::SLT_RunBatchGamma()
+{
+    //number of batch should be matched
+    int cntRef = m_strlistPath_RD_Original_Ref.count();
+    int cntComp = m_strlistPath_RD_Original_Comp.count();
+
+    QDir tmpCheckDir(m_strPathDirWorkDir);
+
+
+    if (m_strPathDirWorkDir.isEmpty())
+    {
+        QUTIL::ShowErrorMessage("Error! No work space is specified. Set it first.");
+        return;
+    }
+
+    if (!tmpCheckDir.exists())
+    {
+        QUTIL::ShowErrorMessage("Error! Current work space doesn't exist. Set it again");
+        return;
+    }        
+
+    if (cntRef*cntComp == 0 || cntRef != cntComp)
+    {
+        cout << "ERROR! Invalid input file counts" << endl;
+        return;
+    }
+
+    //Check the working directory. Subfolders..This should have no relavant subdirectories    
+    
+    QString strParamSet;
+    strParamSet.sprintf("_%dmm_%dp", ui.lineEdit_dta_tol->text().toInt(), ui.lineEdit_dose_tol->text().toInt());
+
+    float fResmp = ui.lineEdit_inhereResample->text().toFloat();
+
+    if (fResmp > 0 && ui.checkBox_inhereResample->isChecked())
+        strParamSet = strParamSet + "_rsmp" + QString::number(fResmp, 'd', 0);
+
+    if (ui.checkBox_Interp_search->isChecked())
+        strParamSet = strParamSet + "_interp";    
+
+
+    QString timeStamp = QUTIL::GetTimeStampDirName();
+
+    QString strSubRef = "DoseRef_" + timeStamp;
+    QString strSubComp = "DoseComp_"+ timeStamp;
+    QString strSubAnalysis = "Analysis_" + timeStamp + strParamSet;
+
+    //Create Folders
+
+    QDir crntWorkDir(m_strPathDirWorkDir);
+    crntWorkDir.mkdir(strSubRef);
+    crntWorkDir.mkdir(strSubComp);
+    crntWorkDir.mkdir(strSubAnalysis);
+
+    QString strPathDirReadRef = m_strPathDirWorkDir + "/" + strSubRef;
+    QString strPathDirReadComp = m_strPathDirWorkDir + "/" + strSubComp;
+    QString strPathDirAnalysis = m_strPathDirWorkDir + "/" + strSubAnalysis;
+
+    m_strlistPath_RD_Read_Ref.clear();
+    m_strlistPath_RD_Read_Comp.clear();
+
+    m_strlistBatchReport.clear();
+    m_strlistPath_Output_Gammamap.clear();
+    m_strlistPath_Output_Failure.clear();
+    m_strlistPath_Output_Report.clear();
+    m_vRefDose.clear();
+
+    QString dirPathFirstFileDir; //for saving workspace
+    QString dirPathFirstFileBase; //for saving workspace
+
+    for (int i = 0; i < cntRef; i++)
+    {
+        QString strPathRef = m_strlistPath_RD_Original_Ref.at(i);
+        QString strPathComp = m_strlistPath_RD_Original_Comp.at(i);
+
+        QFileInfo fInfoRef = QFileInfo(strPathRef);
+        QFileInfo fInfoComp = QFileInfo(strPathComp);        
+
+        QString baseNameRef = fInfoRef.completeBaseName();
+        QString baseNameComp = fInfoComp.completeBaseName();
+
+        if (i == 0) //first image location
+        {
+            //dirPathFirstFileDir = dirPath;
+            dirPathFirstFileBase = baseNameComp;
+        }
+        QString strPathBkupRef = strPathDirReadRef + "/" + baseNameRef + ".mha";
+        QString strPathBkupComp = strPathDirReadComp + "/" + baseNameComp + ".mha";
+
+
+        if (strPathRef.length() < 2 || strPathComp.length() < 2)
+            continue;//skip this pair
+
+        Gamma_parms parms;
+        //Gamma param: should come from the UI
+        parms.b_ref_only_threshold = false;
+        parms.mask_image_fn = "";
+        //parms->reference_dose;
+        parms.gamma_max = 2.0;
+        parms.b_compute_full_region = false;
+        parms.b_resample_nn = false; //default: false
+
+        //From File List
+        parms.ref_image_fn = strPathRef.toLocal8Bit().constData();
+        parms.cmp_image_fn = strPathComp.toLocal8Bit().constData();
+
+        //From GUI
+        if (ui.checkBox_inhereResample->isChecked())
+            parms.f_inherent_resample_mm = ui.lineEdit_inhereResample->text().toDouble();
+        else
+            parms.f_inherent_resample_mm = -1.0;
+        
+        parms.b_interp_search = ui.checkBox_Interp_search->isChecked();
+        
+        if (ui.radioButton_localGamma->isChecked())
+        {
+            parms.b_local_gamma = true;
+        }
+        else
+        {
+            parms.b_local_gamma = false;
+
+        }
+
+        float inputRefDose = ui.lineEdit_refDoseInGy->text().toFloat();
+
+        if (inputRefDose <= 0) //blank
+        {
+            parms.have_reference_dose = false;
+            parms.reference_dose = 0.0;
+        }
+        else
+        {
+            parms.have_reference_dose = true;
+            parms.reference_dose = inputRefDose;
+        }
+
+        parms.dta_tolerance = ui.lineEdit_dta_tol->text().toDouble();
+        parms.dose_tolerance = ui.lineEdit_dose_tol->text().toDouble() / 100.0;//gui input: 3% --> param: 0.03
+        parms.f_analysis_threshold = ui.lineEdit_cutoff_dose->text().toDouble() / 100.0;
+        
+        //Saving folder: comp folder. FileName Should Include dta, dose, local/global      
+
+        QString strLocGlob;
+
+        if (parms.b_local_gamma)
+            strLocGlob = "loc";        
+        else
+            strLocGlob = "glb";
+
+        QString strSettingAbs = QString::number(parms.dta_tolerance, 'f', 0) + "mm_" + ""
+            + QString::number(parms.dose_tolerance*100.0, 'f', 0) + "%_" + strLocGlob;
+
+
+        QString outputPath = strPathDirAnalysis + "/" + baseNameComp + "_gammamap" + ".mha";
+        parms.out_image_fn = outputPath.toLocal8Bit().constData();
+        m_strlistPath_Output_Gammamap.push_back(outputPath);
+      
+
+        //if (ui.checkBox_failuremap_output->isChecked())
+        //{
+            //QString outputPath = dirPath + "/" + baseName + "_failmap_" + strSettingAbs + ".mha";
+        outputPath = strPathDirAnalysis + "/" + baseNameComp + "_failmap" + ".mha";
+        parms.out_failmap_fn = outputPath.toLocal8Bit().constData();
+        m_strlistPath_Output_Failure.push_back(outputPath);
+        //}           
+
+        //QString outputPath = dirPath + "/" + baseName + "_report_" + strSettingAbs + ".txt";
+        outputPath = strPathDirAnalysis + "/" + baseNameComp + "_report" + ".txt";
+        parms.out_report_fn = outputPath.toLocal8Bit().constData();
+        m_strlistPath_Output_Report.push_back(outputPath);
+
+        float refDoseGy;
+        QString overallReport = GammaMain(&parms, refDoseGy, strPathBkupRef, strPathBkupComp);        
+        m_strlistBatchReport.push_back(overallReport);
+
+        m_strlistPath_RD_Read_Ref.push_back(strPathBkupRef);
+        m_strlistPath_RD_Read_Comp.push_back(strPathBkupComp);
+
+        m_vRefDose.push_back(refDoseGy);        
+    }
+    //Save WorkSpace File for future loading
+
+    
+    //QString strPathGammaWorkSpace = m_strPathDirWorkDir + "/" + dirPathFirstFileBase + "_" + strParamSet + "_" + QString("%1").arg(cntRef) + "cases.gws"; //gamma work space
+
+    QString strPathGammaWorkSpace = m_strPathDirWorkDir + "/" + strSubAnalysis + ".gws"; //gamma work space
+    QString strFilePathReport = m_strPathDirWorkDir + "/" + strSubAnalysis + "BatchReport.txt"; //gamma work space
+
+    SaveCurrentGammaWorkSpace(strPathGammaWorkSpace);
+
+    cout << cntRef << " analysis were successfully done!" << endl;
+
+    SLT_LoadResults();
+
+    //After the batch mode analysis, export the simpe report.    
+    //Only when the number of files is > 1
+//    if (cntRef == 1)
+  //      return;    
+
+    SaveBatchGamma3DSimpleReport(strFilePathReport);
+        
+        /*QString fileName = QFileDialog::getSaveFileName(this, "Save batch report file", "", "report (*.txt)", 0, 0);
+
+        if (fileName.length() < 1)
+        return;
+
+        ofstream fout;
+        fout.open(fileName.toLocal8Bit().constData());
+        fout << "Reference_File\t"
+        << "Compare_File\t"
+        << "dta_tolerance[mm]\t"
+        << "dose_tolerance[%]\t"
+        << "doseCutoff[%]\t"
+        << "Local/Global\t"
+        << "Ref_dose[Gy]\t"
+        << "VoxNumAnalyzed\t"
+        << "VoxNumPassed\t"
+        << "GammaPassRate[%]" << endl;
+
+        for (int i = 0; i < cntRef; i++)
+        {
+        fout << m_strlistBatchReport.at(i).toLocal8Bit().constData() << endl;
+        }
+
+        fout.close();*/
+}
+
+void gamma_gui::SaveBatchGamma3DSimpleReport(QString& strFilePath)
+{
+    //QString fileName = QFileDialog::getSaveFileName(this, "Save batch report file", "", "report (*.txt)", 0, 0);
+
+    /*if (fileName.length() < 1)
+        return;*/
+    if (m_strlistBatchReport.count() < 1)
+    {
+        cout << "Error! No report is available" << endl;
+        return;
+    }        
+
+    ofstream fout;
+    fout.open(strFilePath.toLocal8Bit().constData());
+    fout << "Reference_File\t"
+        << "Compare_File\t"
+        << "dta_tolerance[mm]\t"
+        << "dose_tolerance[%]\t"
+        << "doseCutoff[%]\t"
+        << "Local/Global\t"
+        << "Ref_dose[Gy]\t"
+        << "VoxNumAnalyzed\t"
+        << "VoxNumPassed\t"
+        << "GammaPassRate[%]" << endl;
+
+    int cnt = m_strlistBatchReport.count();
+
+    for (int i = 0; i < cnt; i++)
+    {
+        fout << m_strlistBatchReport.at(i).toLocal8Bit().constData() << endl;
+    }
+
+    fout.close();
+}
+
+
+void gamma_gui::SLTM_SaveBatchModeSimpleReport()
+{
+    QString filePath = QFileDialog::getSaveFileName(this, "Save batch report file", m_strPathDirWorkDir, "report (*.txt)", 0, 0);
+
+    if (filePath.length() < 1)
+        return;
+
+    QFileInfo fInfo(filePath);
+
+    if (fInfo.suffix() != "txt" && fInfo.suffix() != "TXT")
+    {
+        cout << "Saving filter didn't work. Saving this file as txt." << endl;
+        filePath = filePath + ".txt";
+    }
+
+    SaveBatchGamma3DSimpleReport(filePath);
+}
+
+
+QString gamma_gui::GammaMain(Gamma_parms* parms, float& refDoseGy, const QString& strPathBkupRef, const QString& strPathBkupComp)
+{
+    QString reportResult;
+    Gamma_dose_comparison gdc;
+
+    //DICOM_RD compatible (added by YK, Feb 2015)
+    //In the prev version, RD couldn't be read directly due to the scale factor inside of the DICOM file.
+    //work-around was to use (plastimatch convert ...)
+    //here, that feature has been integrated into plastimatch gamma
+    Plm_file_format file_type_ref, file_type_comp;
+    Rt_study rt_study_ref, rt_study_comp;
+
+    file_type_ref = plm_file_format_deduce(parms->ref_image_fn.c_str());
+    file_type_comp = plm_file_format_deduce(parms->cmp_image_fn.c_str());
+
+    if (file_type_ref == PLM_FILE_FMT_DICOM_DOSE) {
+        rt_study_ref.load(parms->ref_image_fn.c_str(), file_type_ref);
+        if (rt_study_ref.has_dose()){
+            gdc.set_reference_image(rt_study_ref.get_dose()->clone());
+        }
+        else{
+            gdc.set_reference_image(parms->ref_image_fn.c_str());
+        }
+    }
+    else {
+        gdc.set_reference_image(parms->ref_image_fn.c_str());
+    }
+
+    if (file_type_comp == PLM_FILE_FMT_DICOM_DOSE) {
+        rt_study_comp.load(parms->cmp_image_fn.c_str(), file_type_comp);
+        if (rt_study_comp.has_dose()) {
+            gdc.set_compare_image(rt_study_comp.get_dose()->clone());
+        }
+        else {
+            gdc.set_compare_image(parms->cmp_image_fn.c_str());
+        }
+
+    }
+    else {
+        gdc.set_compare_image(parms->cmp_image_fn.c_str());
+    }
+    //End DICOM-RD    
+
+    if (parms->mask_image_fn != "") {
+        gdc.set_mask_image(parms->mask_image_fn);
+    }
+
+    gdc.set_spatial_tolerance(parms->dta_tolerance);
+    gdc.set_dose_difference_tolerance(parms->dose_tolerance);
+    if (parms->have_reference_dose) {
+        gdc.set_reference_dose(parms->reference_dose);
+    }
+    gdc.set_gamma_max(parms->gamma_max);
+
+    /*Extended by YK*/
+    gdc.set_interp_search(parms->b_interp_search);//default: false
+    gdc.set_local_gamma(parms->b_local_gamma);//default: false
+    gdc.set_compute_full_region(parms->b_compute_full_region);//default: false
+    gdc.set_resample_nn(parms->b_resample_nn); //default: false
+    gdc.set_ref_only_threshold(parms->b_ref_only_threshold);
+
+    if (parms->f_inherent_resample_mm > 0.0) {
+        gdc.set_inherent_resample_mm(parms->f_inherent_resample_mm);
+    }
+
+    if (parms->f_analysis_threshold > 0) {
+        gdc.set_analysis_threshold(parms->f_analysis_threshold);//0.1 = 10%
+    }
+
+    gdc.run();
+
+    if (parms->out_image_fn != "") {
+        Plm_image::Pointer gamma_image = gdc.get_gamma_image();
+        gamma_image->save_image(parms->out_image_fn);
+    }
+
+    if (parms->out_failmap_fn != "") {
+        gdc.get_fail_image()->save_image(parms->out_failmap_fn);
+    }
+
+    if (parms->out_report_fn != "") {
+        //Export utput text using gdc.get_report_string();
+        std::ofstream fout;
+        fout.open(parms->out_report_fn.c_str());
+        if (!fout.fail()){
+            fout << gdc.get_report_string();
+            fout << "Reference_file_name\t" << parms->ref_image_fn.c_str() << std::endl;
+            fout << "Compare_file_name\t" << parms->cmp_image_fn.c_str() << std::endl;            
+            fout.close();
+        }
+    }
+    printf ("Pass rate = %2.6f %%\n", gdc.get_pass_fraction() * 100.0);
+    
+    //Composite a result string
+    //FileName
+    QFileInfo info_ref = QFileInfo(parms->ref_image_fn.c_str());
+    QFileInfo info_comp = QFileInfo(parms->cmp_image_fn.c_str());   
+    
+    QString fileName_ref = info_ref.fileName();
+    QString fileName_comp = info_comp.fileName();
+
+    QString strTol_dta = QString::number(parms->dta_tolerance,'f',2);
+    QString strTol_dose = QString::number(parms->dose_tolerance*100.0, 'f', 0);
+    QString strDoseCutoff = QString::number(parms->f_analysis_threshold*100, 'f', 0);
+
+    QString strLocalOrGlobal;
+    if (parms->b_local_gamma)
+        strLocalOrGlobal = "local";
+    else
+        strLocalOrGlobal = "global";
+
+    //if this is dcm, save the mha files
+    //if (info_ref.suffix() == "dcm" || info_ref.suffix() == "DCM")
+    //{
+        //QString newPath = info_ref.absolutePath() + "/" + info_ref.completeBaseName() + ".mha";        
+
+    if (!strPathBkupRef.isEmpty())
+    {
+        Plm_image* pImg = gdc.get_ref_image();
+        pImg->save_image(strPathBkupRef.toLocal8Bit().constData());
+    }
+        
+    //}
+
+    //if (info_comp.suffix() == "dcm" || info_comp.suffix() == "DCM")
+    //{
+        //QString newPath = info_comp.absolutePath() + "/" + info_comp.completeBaseName() + ".mha";
+        //QString newPath = info_comp.absolutePath() + "/" + info_comp.completeBaseName() + ".mha";
+
+    if (!strPathBkupComp.isEmpty())
+    {
+        Plm_image* pImg = gdc.get_comp_image();
+        pImg->save_image(strPathBkupComp.toLocal8Bit().constData());
+    }    
+    QString strRef_dose = QString::number(gdc.get_reference_dose(), 'f', 2);//Gy
+    QString strVoxNumAnalyzed = QString::number(gdc.get_analysis_num_vox(), 'f', 2);
+    QString strVoxNumPassed = QString::number(gdc.get_passed_num_vox(), 'f', 2);
+    QString strPassRate = QString::number(gdc.get_pass_fraction()*100.0, 'f', 2);
+
+    reportResult = fileName_ref + "\t"
+        + fileName_comp + "\t"
+        + strTol_dta + "\t"
+        + strTol_dose + "\t"
+        + strDoseCutoff + "\t"
+        + strLocalOrGlobal + "\t"
+        + strRef_dose + "\t"
+        + strVoxNumAnalyzed + "\t"
+        + strVoxNumPassed + "\t"
+        + strPassRate;
+    
+    refDoseGy = gdc.get_reference_dose();
+    return reportResult;
+}
+
+void gamma_gui::SLT_ProfileView()
+{
+    //m_pView->show();
+
+}
+
+void gamma_gui::SLT_DrawDoseImages()
+{
+    // refer to probe positions, selected 3D file (spPointer), plane direction
+
+}
+
+void gamma_gui::SLT_DrawGammaMap3D()
+{
+
+}
+
+void gamma_gui::SLT_DrawGammaMap2D()
+{
+
+}
+
+void gamma_gui::Load_FilesToMem()
+{
+    /*int cntRef = m_strlistPath_RD_Original_Ref.count();
+    int cntComp = m_strlistPath_RD_Original_Comp.count();*/
+
+    int cntRef = m_strlistPath_RD_Read_Ref.count();
+    int cntComp = m_strlistPath_RD_Read_Comp.count();
+
+    int cntGamma = m_strlistPath_Output_Gammamap.count();
+
+    if (cntRef*cntComp == 0 || cntRef != cntComp || cntRef != cntGamma)
+    {
+        cout << "Error: number should be matched" << endl;
+        return;
+    }
+
+    m_vRefDoseImages.clear();
+    m_vCompDoseImages.clear();
+    m_vGammaMapImages.clear();
+
+    m_iLastLoadedIndex = -1;
+    
+    QString strPath_ref, strPath_comp, strPath_gamma;
+
+    for (int i = 0; i < cntRef; i++)
+    {   
+        strPath_ref = m_strlistPath_RD_Read_Ref.at(i); //always readable mha format
+        strPath_comp = m_strlistPath_RD_Read_Comp.at(i);
+        strPath_gamma = m_strlistPath_Output_Gammamap.at(i);
+
+        QFileInfo info_ref = QFileInfo(strPath_ref);
+        QFileInfo info_comp = QFileInfo(strPath_comp);
+    
+        //if (info_ref.suffix() == "dcm" || info_ref.suffix() == "DCM")
+        //{
+        //    //strPath_ref = info_ref.absolutePath() + "/" + info_ref.completeBaseName() + ".mha";            
+        //    strPath_ref = info_ref.absolutePath() + "/" + info_ref.completeBaseName() + ".mha";
+        //}
+
+        //if (info_comp.suffix() == "dcm" || info_comp.suffix() == "DCM")
+        //{
+        //    //strPath_comp = info_comp.absolutePath() + "/" + info_comp.completeBaseName() + ".mha";
+        //    strPath_comp = info_comp.absolutePath() + "/" + info_comp.completeBaseName() + ".mha";
+        //}
+
+        bool bLowMem = ui.checkBox_low_mem->isChecked();       
+
+
+        FloatImageType::Pointer spImgRef = FloatImageType::New();
+        FloatImageType::Pointer spImgComp = FloatImageType::New();
+        FloatImageType::Pointer spImgGamma = FloatImageType::New();
+
+
+
+        if (bLowMem)
+        {
+            spImgRef = m_spDummyLowMemImg;
+            spImgComp = m_spDummyLowMemImg;
+            spImgGamma = m_spDummyLowMemImg;
+        }         
+        else
+        {
+            QUTIL::LoadFloatImage3D(strPath_ref.toLocal8Bit().constData(), spImgRef);
+            QUTIL::LoadFloatImage3D(strPath_comp.toLocal8Bit().constData(), spImgComp);
+            QUTIL::LoadFloatImage3D(strPath_gamma.toLocal8Bit().constData(), spImgGamma);
+        }
+
+        m_vRefDoseImages.push_back(spImgRef);        
+        m_vCompDoseImages.push_back(spImgComp);
+        m_vGammaMapImages.push_back(spImgGamma);        
+    }    
+}
+
+void gamma_gui::SLT_LoadResults()
+{    
+    cout << "Loading image data... please wait" << endl;        
+    Load_FilesToMem();    
+    cout << "Dose and Gamma data are successfully loaded" << endl;
+
+    disconnect(ui.comboBoxCompareFile, SIGNAL(currentIndexChanged(int)), this, SLOT(SLT_WhenSelectCombo()));
+    SLT_UpdateComboContents();
+
+    connect(ui.comboBoxCompareFile, SIGNAL(currentIndexChanged(int)), this, SLOT(SLT_WhenSelectCombo()));        
+
+    SLT_WhenSelectCombo(); //initialization
+
+    SLT_GoCenterPosRef();
+    SLT_GoCenterPosComp();
+    //SLT_DrawAll();
+}
+
+void gamma_gui::SLT_UpdateComboContents() //compare image based..
+{    
+    QComboBox* crntCombo = ui.comboBoxCompareFile;
+    crntCombo->clear();    
+
+    int cntComp = m_strlistFileBaseName_Comp.count();
+
+    for (int i = 0; i < cntComp; i++)
+    {
+        //SLT_WHenComboSelect should be disconnected here
+        crntCombo->addItem(m_strlistFileBaseName_Comp.at(i));        
+    }    
+}
+void gamma_gui::SLT_DrawAll()
+{
+    //Get combo box selection
+    QComboBox* crntCombo = ui.comboBoxCompareFile;
+    //QString curStr = crntCombo->currentText(); //this should be basename
+    int curIdx = crntCombo->currentIndex(); //this should be basename    
+
+    int iCnt = crntCombo->count();
+
+    if (iCnt < 1)
+        return;
+
+    if ((int)(m_vRefDoseImages.size()) != iCnt ||
+        (int)(m_vCompDoseImages.size()) != iCnt)
+    {
+        cout << "Error! iCnt not matching in DrawAll" << endl;
+        return;
+    }
+
+    //ui.labelReferDose->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    //ui.labelReferDose->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    //ui.labelCompDose->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    //ui.labelCompDose->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    //ui.labelGammaMap3D->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    //ui.labelGammaMap3D->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    //ui.labelGammaMap2D->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    //ui.labelGammaMap2D->setFixedHeight(DEFAULT_LABEL_HEIGHT);    
+
+
+    //Convert3DTo2D (float2D, get radio, probe pos from GUI)
+    //Get3DData (ref, comp, gamma)
+    FloatImageType::Pointer spCurRef = m_vRefDoseImages.at(curIdx);
+    FloatImageType::Pointer spCurComp = m_vCompDoseImages.at(curIdx);
+    //FloatImageType::Pointer spCurRef = m_spRefDoseImages;
+    //FloatImageType::Pointer spCurComp = m_spCompDoseImages;
+
+    FloatImageType::Pointer spCurGamma;
+    
+      if ((int)(m_vGammaMapImages.size()) == iCnt)
+          spCurGamma = m_vGammaMapImages.at(curIdx);
+    /*if (m_pCurImageGamma3D)
+        spCurGamma = m_spGammaMapImages;*/
+
+    //DICOM
+    float probePosX = ui.lineEdit_ProbePosX->text().toFloat();
+    float probePosY = ui.lineEdit_ProbePosY->text().toFloat();
+    float probePosZ = ui.lineEdit_ProbePosZ->text().toFloat();
+
+    //Move them to Global due to 2D gamma export
+    /*FloatImage2DType::Pointer m_spCurRef2D;
+    FloatImage2DType::Pointer m_spCurComp2D;*/
+    //Move them to Global due to 2D gamma export
+
+    FloatImage2DType::Pointer spCurGamma2DFrom3D; 
+    double finalPos1, finalPos2, finalPos3;
+
+    enPLANE curPlane = PLANE_AXIAL;
+    float fixedPos = 0.0;
+
+    float probePos2D_X =0.0;
+    float probePos2D_Y = 0.0;
+
+    bool bYFlip = false;
+    
+    if (ui.radioButtonAxial->isChecked())
+    {
+        curPlane = PLANE_AXIAL;
+        fixedPos = probePosZ;
+
+        probePos2D_X = probePosX;
+        probePos2D_Y = probePosY;
+    }
+    else if (ui.radioButtonSagittal->isChecked())
+    {
+        curPlane = PLANE_SAGITTAL;
+        fixedPos = probePosX;
+
+        probePos2D_X = probePosY;
+        probePos2D_Y = probePosZ;  //YKDebug: may be reversed     
+        bYFlip = true;
+    }
+    else if (ui.radioButtonFrontal->isChecked())
+    {
+        curPlane = PLANE_FRONTAL;
+        fixedPos = probePosY;        
+
+        probePos2D_X = probePosX;
+        probePos2D_Y = probePosZ;//YKDebug: may be reversed   
+        bYFlip = true;
+    }
+    
+    QUTIL::Get2DFrom3DByPosition(spCurRef, m_spCurRef2D, curPlane, fixedPos, finalPos1);
+    QUTIL::Get2DFrom3DByPosition(spCurComp, m_spCurComp2D, curPlane, fixedPos, finalPos2);
+
+  //  if (curPlane == PLANE_FRONTAL)
+    //    QUTIL::SaveFloatImage2D("D:\\testFrontal.mha", m_spCurRef2D);//it is flipped!
+
+
+    if (spCurGamma)
+    {
+        QUTIL::Get2DFrom3DByPosition(spCurGamma, spCurGamma2DFrom3D, curPlane, fixedPos, finalPos3);
+    }    
+
+    //Actually, frontal and sagittal image should be flipped for display purpose (in axial, Y is small to large, SAG and FRONTAL, Large to Small (head to toe direction)
+    //Let's not change original data itself due to massing up the origin. Only change the display image
+    //Point probe and profiles, other things works fine.
+
+
+    //YKImage receives 2D float
+    m_pCurImageRef->UpdateFromItkImageFloat(m_spCurRef2D, GY2YKIMG_MAG, NON_NEG_SHIFT, bYFlip); //flip Y for display only
+    m_pCurImageComp->UpdateFromItkImageFloat(m_spCurComp2D, GY2YKIMG_MAG, NON_NEG_SHIFT, bYFlip);
+
+    //cout << "GAMMA2YKIMG_MAG= " << GAMMA2YKIMG_MAG << endl;
+
+    if (spCurGamma2DFrom3D)
+    {
+        m_pCurImageGamma3D->UpdateFromItkImageFloat(spCurGamma2DFrom3D, GAMMA2YKIMG_MAG, NON_NEG_SHIFT, bYFlip);
+    }
+
+    
+    if (m_spGamma2DResult)
+    {
+        m_pCurImageGamma2D->UpdateFromItkImageFloat(m_spGamma2DResult, GAMMA2YKIMG_MAG, NON_NEG_SHIFT, bYFlip);
+    }
+
+    float doseGyNormRef = 0.01 * (ui.sliderNormRef->value());
+    float doseGyNormComp = 0.01 *(ui.sliderNormComp->value());
+
+    //cout << "doseGyNormComp= " << doseGyNormRef << endl;
+    //PixMap
+
+    m_pCurImageRef->SetNormValueOriginal(doseGyNormRef);
+    m_pCurImageComp->SetNormValueOriginal(doseGyNormComp);
+
+    m_pCurImageRef->FillPixMapDose();
+    m_pCurImageComp->FillPixMapDose();
+    //m_pCurImageRef->FillPixMapDose(doseGyNormRef);
+    //m_pCurImageComp->FillPixMapDose(doseGyNormComp);
+    m_pCurImageGamma3D->FillPixMapGamma();
+    m_pCurImageGamma2D->FillPixMapGamma();
+    
+    m_pCurImageRef->SetCrosshairPosPhys(probePos2D_X, probePos2D_Y, curPlane);
+    m_pCurImageComp->SetCrosshairPosPhys(probePos2D_X, probePos2D_Y, curPlane);
+    m_pCurImageGamma3D->SetCrosshairPosPhys(probePos2D_X, probePos2D_Y, curPlane);
+    m_pCurImageGamma2D->SetCrosshairPosPhys(probePos2D_X, probePos2D_Y, curPlane);
+
+    m_pCurImageRef->m_bDrawCrosshair = true;
+    m_pCurImageComp->m_bDrawCrosshair = true;
+    m_pCurImageGamma3D->m_bDrawCrosshair = true;
+    m_pCurImageGamma2D->m_bDrawCrosshair = true;
+
+    m_pCurImageRef->m_bDrawOverlayText = true;
+    m_pCurImageComp->m_bDrawOverlayText = true;
+    m_pCurImageGamma3D->m_bDrawOverlayText = true;
+    m_pCurImageGamma2D->m_bDrawOverlayText = true;
+
+    QString strValRef = QString::number(m_pCurImageRef->GetCrosshairOriginalData() * 100, 'f', 1) + " cGy";
+    QString strValComp = QString::number(m_pCurImageComp->GetCrosshairOriginalData() * 100, 'f', 1) + " cGy";
+    QString strValGamma3D = QString::number(m_pCurImageGamma3D->GetCrosshairOriginalData(), 'f', 2);
+    QString strValGamma2D = QString::number(m_pCurImageGamma2D->GetCrosshairOriginalData(), 'f', 2);
+
+    float fPercRef = m_pCurImageRef->GetCrosshairPercData();
+    float fPercComp = m_pCurImageComp->GetCrosshairPercData();
+    QString strPercRef = QString::number(fPercRef, 'f', 1) + "%";
+    QString strPercComp = QString::number(fPercComp, 'f', 1) + "%";
+    QString strDelta = QString::number(fPercComp - fPercRef, 'f', 1) + "%";   
+
+    m_pCurImageRef->m_strOverlayText = strValRef + " [" + strPercRef + "]";
+    m_pCurImageComp->m_strOverlayText = strValComp + " [" + strPercComp + "]" + ",          Delta= " + strDelta;
+    m_pCurImageGamma3D->m_strOverlayText = strValGamma3D;
+    m_pCurImageGamma2D->m_strOverlayText = strValGamma2D;
+
+    ui.labelReferDose->SetBaseImage(m_pCurImageRef);
+    ui.labelCompDose->SetBaseImage(m_pCurImageComp);
+    ui.labelGammaMap3D->SetBaseImage(m_pCurImageGamma3D);
+    ui.labelGammaMap2D->SetBaseImage(m_pCurImageGamma2D);
+    //ui.labelRefDose
+    //Set probe point in YKImage and crosshair display on
+    //Update Table and Plot    
+    //Display
+    //label.update()
+    ui.labelReferDose->update();
+    ui.labelCompDose->update();
+    ui.labelGammaMap3D->update();
+    ui.labelGammaMap2D->update();
+
+    //Update Table and Chart
+
+    //1) prepare vector float
+    vector<QPointF> vProfileRef, vProfileComp, vProfileGamma3D, vProfileGamma2D;
+    enPROFILE_DIRECTON enDirection = PRIFLE_HOR;
+    float fixedPosProfile;
+
+    if (ui.radioButtonHor->isChecked())
+    {
+        enDirection = PRIFLE_HOR;
+    }
+    else if (ui.radioButtonVert->isChecked())
+    {
+        enDirection = PRIFLE_VER;
+    }
+
+    if (curPlane == PLANE_AXIAL)
+    {
+        if (enDirection == PRIFLE_HOR)        
+            fixedPosProfile = probePosY;
+        else        
+            fixedPosProfile = probePosX;
+        
+    }
+    else if (curPlane == PLANE_SAGITTAL)
+    {
+        if (enDirection == PRIFLE_HOR)
+            fixedPosProfile = probePosZ;
+        else
+            fixedPosProfile = probePosY;
+        
+    }
+    else if (curPlane == PLANE_FRONTAL)
+    {
+        if (enDirection == PRIFLE_HOR)
+            fixedPosProfile = probePosZ;
+        else
+            fixedPosProfile = probePosX;
+    }
+   
+    QUTIL::GetProfile1DByPosition(m_spCurRef2D, vProfileRef, fixedPosProfile, enDirection);
+    QUTIL::GetProfile1DByPosition(m_spCurComp2D, vProfileComp, fixedPosProfile, enDirection);
+    QUTIL::GetProfile1DByPosition(spCurGamma2DFrom3D, vProfileGamma3D, fixedPosProfile, enDirection);
+    //QUTIL::GetProfile1DByPosition(spCurRef2D, vProfileGamma2D, fixedPosProfile, enDirection);
+    //cout << "vectorSize= " << vProfileRef.size() << endl;
+    
+    //fNorm: Gy
+    //float doseGyNormRef = 0.01 * (ui.sliderNormRef->value());
+    //float doseGyNormComp = 0.01 *(ui.sliderNormComp->value());
+    UpdateTable(vProfileRef, vProfileComp, vProfileGamma3D, doseGyNormRef, doseGyNormComp, 1.0, 100.0, 100.0, 1.0);
+
+    SLT_DrawGraph(ui.checkBoxAutoAdjust->isChecked());
+
+    if (m_pTableModel == NULL)
+    {
+
+        //cout << "TableModel is NULL" << endl;
+    }
+
+    if (m_pTableModel != NULL)
+    {
+        //cout << "table column " << m_pTableModel->columnCount() << endl;
+        //cout << "table row " << m_pTableModel->rowCount() << endl;
+        ui.tableViewProfile->setModel(m_pTableModel);
+        ui.tableViewProfile->resizeColumnsToContents();
+    }
+    
+
+    //Update LineEdit using rounded fixed value
+    if (curPlane == PLANE_AXIAL)
+    {
+        //ui.lineEdit_ProbePosZ->setText(QString("%1").arg(finalPos1));//finalPos1 is for all images
+        ui.lineEdit_ProbePosZ->setText(QString::number(finalPos1,'f', 1));
+    }
+    else if (curPlane == PLANE_SAGITTAL)
+    {
+        ui.lineEdit_ProbePosX->setText(QString::number(finalPos1, 'f', 1));
+    }
+    else if (curPlane == PLANE_FRONTAL)
+    {
+        ui.lineEdit_ProbePosY->setText(QString::number(finalPos1, 'f', 1));
+    }
+
+    ui.tableViewProfile->update();
+
+    SLT_UpdateReportTxt();
+ 
+}
+
+void gamma_gui::SLT_UpdateReportTxt()
+{
+    QComboBox* crntCombo = ui.comboBoxCompareFile;
+    int curIdx = crntCombo->currentIndex(); //this should be basename    
+
+    int iCnt = crntCombo->count();
+
+    if (iCnt < 1)
+        return;
+
+    //Update plainTextEditGammaResult    
+    ui.plainTextEditGammaResult->clear();
+    if (m_strlistPath_Output_Report.count() == iCnt)
+    {
+        QString curPath = m_strlistPath_Output_Report.at(curIdx);
+        QStringList strList = QUTIL::LoadTextFile(curPath.toLocal8Bit().constData());
+
+        for (int i = 0; i < strList.count(); i++)
+            ui.plainTextEditGammaResult->appendPlainText(strList.at(i));
+    }
+
+    QTextCursor txtCursor = ui.plainTextEditGammaResult->textCursor();
+    //position where you want it
+    txtCursor.setPosition(0);
+    ui.plainTextEditGammaResult->setTextCursor(txtCursor);
+}
+
+
+void gamma_gui::SLT_UpdateProbePosRef()
+{
+    UpdateProbePos(ui.labelReferDose);
+}
+
+void gamma_gui::SLT_UpdateProbePosComp()
+{
+    UpdateProbePos(ui.labelCompDose);
+
+}
+
+void gamma_gui::SLT_UpdateProbePosGamma2D()
+{
+    UpdateProbePos(ui.labelGammaMap2D);
+}
+
+void gamma_gui::SLT_UpdateProbePosGamma3D()
+{
+    UpdateProbePos(ui.labelGammaMap3D);
+}
+
+void gamma_gui::UpdateProbePos(qyklabel* qlabel)
+{
+
+    YK16GrayImage* pYKImg = qlabel->m_pYK16Image;
+
+    if (pYKImg == NULL)
+        return;
+
+    QPoint viewPt = QPoint(qlabel->x, qlabel->y);
+    QPoint crntDataPt = qlabel->GetDataPtFromViewPt(viewPt.x(), viewPt.y());
+
+    float originX = pYKImg->m_fOriginX;
+    float originY = pYKImg->m_fOriginY;
+
+    float spacingX = pYKImg->m_fSpacingX;
+    float spacingY = pYKImg->m_fSpacingY;
+
+    //float iWidth = pYKImg->m_iWidth;
+    float iHeight = pYKImg->m_iHeight;
+
+    float physPosX = 0.0;
+    float physPosY = 0.0;
+
+
+    //connect(ui.labelReferDose, SIGNAL(Mouse_Pressed_Left()), this, SLOT(SLT_UpdateProbePosRef())); //added
+    //connect(ui.labelCompDose, SIGNAL(Mouse_Pressed_Left()), this, SLOT(SLT_UpdateProbePosComp())); //added
+    //connect(ui.labelGammaMap2D, SIGNAL(Mouse_Pressed_Left()), this, SLOT(SLT_UpdateProbePosGamma2D())); //added
+    //connect(ui.labelGammaMap3D, SIGNAL
+
+    //Update Probe position
+    // ui.lineEdit_ProbePosX
+
+    enPLANE curPlane = PLANE_AXIAL;
+
+    if (ui.radioButtonAxial->isChecked())
+    {
+        curPlane = PLANE_AXIAL;
+
+        physPosX = crntDataPt.x()*spacingX + originX;
+        physPosY = crntDataPt.y()*spacingY + originY;
+
+
+        ui.lineEdit_ProbePosX->setText(QString::number(physPosX, 'f', 1));
+        ui.lineEdit_ProbePosY->setText(QString::number(physPosY, 'f', 1));
+                //ui.lineEdit_ProbePosZ
+    }
+    else if (ui.radioButtonSagittal->isChecked())
+    {
+        curPlane = PLANE_SAGITTAL;
+
+        physPosX = crntDataPt.x()*spacingX + originX;
+        physPosY = (iHeight - crntDataPt.y() - 1)*spacingY + originY;        
+
+        ui.lineEdit_ProbePosY->setText(QString::number(physPosX, 'f', 1));
+        ui.lineEdit_ProbePosZ->setText(QString::number(physPosY, 'f', 1));
+
+
+    }
+    else if (ui.radioButtonFrontal->isChecked())
+    {
+        curPlane = PLANE_FRONTAL;
+
+        physPosX = crntDataPt.x()*spacingX + originX;
+        physPosY = (iHeight - crntDataPt.y() - 1)*spacingY + originY;
+
+        //ui.lineEdit_ProbePosX->setText(QString("%1").arg(physPosX));
+        //ui.lineEdit_ProbePosZ->setText(QString("%1").arg(physPosY));
+
+        ui.lineEdit_ProbePosX->setText(QString::number(physPosX, 'f', 1));
+        ui.lineEdit_ProbePosZ->setText(QString::number(physPosY, 'f', 1));
+    }   
+  
+    
+    SLT_DrawAll();
+}
+
+void gamma_gui::SLT_DrawTable()
+{
+    
+
+}
+
+void gamma_gui::SLT_DrawChart()
+{
+
+}
+
+void gamma_gui::UpdateTable(vector<QPointF>& vData1, vector<QPointF>& vData2, vector<QPointF>& vData3,
+    float fNorm1, float fNorm2, float fNorm3, float fMag1, float fMag2, float fMag3)
+{
+    int numOfData = 3;
+
+    if (m_pTableModel != NULL)
+    {
+        delete m_pTableModel;
+        m_pTableModel = NULL;
+    }
+
+    int columnSize = 1;
+    int rowSize1, rowSize2, rowSize3 = 0;
+
+    columnSize = numOfData * 2;
+
+    rowSize1 = vData1.size();
+    rowSize2 = vData2.size();
+    rowSize3 = vData3.size();
+
+    int maxRowSize = 0;
+    if (rowSize1 > rowSize2)
+    {
+        if (rowSize1 < rowSize3)
+            maxRowSize = rowSize3;
+        else
+            maxRowSize = rowSize1;
+
+    }
+    else
+    {
+        if (rowSize2 < rowSize3)
+            maxRowSize = rowSize3;
+        else
+            maxRowSize = rowSize2;
+    }
+
+    if (maxRowSize == 0)
+    {
+        //cout << "MaxRowSize is 0" << endl;
+        return;
+    }
+
+    if (fNorm1 <= 0 || fNorm2 <= 0 || fNorm3 <= 0)
+        return;
+
+    m_pTableModel = new QStandardItemModel(maxRowSize, columnSize, this); //2 Rows and 3 Columns
+    m_pTableModel->setHorizontalHeaderItem(0, new QStandardItem(QString("mm")));
+    m_pTableModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Ref_cGy")));    
+    m_pTableModel->setHorizontalHeaderItem(2, new QStandardItem(QString("Com_cGy")));
+    m_pTableModel->setHorizontalHeaderItem(3, new QStandardItem(QString("Ref_%")));
+    m_pTableModel->setHorizontalHeaderItem(4, new QStandardItem(QString("Com_%")));    
+    m_pTableModel->setHorizontalHeaderItem(5, new QStandardItem(QString("Gamma")));
+
+
+    bool bData2Exists = false;
+    bool bData3Exists = false;
+
+    for (int i = 0; i < maxRowSize; i++)
+    {
+        if (i < rowSize2)
+            bData2Exists = true;
+        if (i < rowSize3)
+            bData3Exists = true;
+
+        qreal tmpValX1 = vData1.at(i).x();
+        qreal tmpValY1 = vData1.at(i).y()*fMag1; //Gy --> cGy
+        qreal tmpValY1_Perc = vData1.at(i).y() / fNorm1 * 100.0;
+
+        QString strValX, strValY1, strValY2, strValY1_Perc, strValY2_Perc, strValY3;
+
+
+
+        strValX = QString::number(tmpValX1, 'f', 1); //cGy
+        strValY1 = QString::number(tmpValY1, 'f', 1); //cGy
+        strValY1_Perc = QString::number(tmpValY1_Perc, 'f', 1); //%
+
+        if (bData2Exists)
+        {
+            //qreal tmpValX2 = vData2.at(i).x();
+            qreal tmpValY2 = vData2.at(i).y()*fMag2;
+            qreal tmpValY2_Perc = vData2.at(i).y() / fNorm2 * 100.0; //fNorm : Gy not cGy            
+
+            strValY2 = QString::number(tmpValY2, 'f', 1); //cGy        
+            strValY2_Perc = QString::number(tmpValY2_Perc, 'f', 1); //%            
+        }
+
+        if (bData3Exists)
+        {
+            //qreal tmpValX3 = vData3.at(i).x();
+            qreal tmpValY3 = vData3.at(i).y()*fMag3;
+            //qreal tmpValY3_Perc = vData3.at(i).y() / fNorm3 * 100.0; //fNorm : Gy not cGy
+
+            strValY3 = QString::number(tmpValY3, 'f', 2); //gamma
+        }       
+
+        m_pTableModel->setItem(i, 0, new QStandardItem(strValX));
+        m_pTableModel->setItem(i, 1, new QStandardItem(strValY1));
+        m_pTableModel->setItem(i, 2, new QStandardItem(strValY2));
+        m_pTableModel->setItem(i, 3, new QStandardItem(strValY1_Perc));
+        m_pTableModel->setItem(i, 4, new QStandardItem(strValY2_Perc));
+        m_pTableModel->setItem(i, 5, new QStandardItem(strValY3));
+    }
+
+    ui.tableViewProfile->setModel(m_pTableModel);
+    ui.tableViewProfile->resizeColumnsToContents();
+}
+
+void gamma_gui::SLT_CopyTableToClipboard()
+{
+    qApp->clipboard()->clear();
+
+    QStringList list;
+
+    int rowCnt = m_pTableModel->rowCount();
+    int columnCnt = m_pTableModel->columnCount();
+
+    //list << "\n";
+    //for (int i = 0 ; i < columnCnt ; i++)		
+    //{
+    //QFileInfo tmpInfo = QFileInfo(ui.lineEdit_Cur3DFileName->text());
+    //list << "Index";	
+    //list << tmpInfo.baseName();
+    list << "\n";
+
+    /*  m_pTableModel->setHorizontalHeaderItem(0, new QStandardItem(QString("mm")));
+      m_pTableModel->setHorizontalHeaderItem(1, new QStandardItem(QString("Ref_cGy")));
+      m_pTableModel->setHorizontalHeaderItem(2, new QStandardItem(QString("Com_cGy")));
+      m_pTableModel->setHorizontalHeaderItem(3, new QStandardItem(QString("Ref_%")));
+      m_pTableModel->setHorizontalHeaderItem(4, new QStandardItem(QString("Com_%")));
+      m_pTableModel->setHorizontalHeaderItem(5, new QStandardItem(QString("Gmma100")));
+      */
+    list << "mm";
+    list << "Ref_cGy";
+    list << "Com_cGy";
+    list << "Ref_%";
+    list << "Com_%";
+    list << "Gamma";
+    list << "\n";
+
+    for (int j = 0; j < rowCnt; j++)
+    {
+        for (int i = 0; i < columnCnt; i++)
+        {
+            QStandardItem* item = m_pTableModel->item(j, i);
+            list << item->text();
+        }
+        list << "\n";
+    }
+
+    qApp->clipboard()->setText(list.join("\t"));
+
+}
+
+void gamma_gui::SLT_DrawGraph(bool bInitMinMax)
+{
+    if (m_pTableModel == NULL)
+        return;
+
+
+    bool bNorm = ui.checkBoxNormalized->isChecked();// show percentage chart
+
+    //Draw only horizontal, center
+
+    QVector<double> vAxisX1; //can be rows or columns
+    QVector<double> vAxisY1;
+
+    QVector<double> vAxisX2; //can be rows or columns
+    QVector<double> vAxisY2;
+
+    QVector<double> vAxisX3; //can be rows or columns
+    QVector<double> vAxisY3;
+
+    //QStandardItemModel 	m_pTableModel.item()
+    int dataLen = m_pTableModel->rowCount();
+   // int columnLen = m_pTableModel->columnCount();
+
+    if (dataLen < 1)
+        return;
+
+    ui.customPlotProfile->clearGraphs();
+
+    double minX = 9999.0;
+    double maxX = -1.0;
+
+    double minY = 9999.0;
+    double maxY = -1.0;
+
+    for (int i = 0; i< dataLen; i++)
+    {
+        QStandardItem* tableItem1 = m_pTableModel->item(i, 0);
+        QStandardItem* tableItem2 = m_pTableModel->item(i, 1);
+        QStandardItem* tableItem3 = m_pTableModel->item(i, 2);
+        QStandardItem* tableItem4 = m_pTableModel->item(i, 3);
+        QStandardItem* tableItem5 = m_pTableModel->item(i, 4);
+        QStandardItem* tableItem6 = m_pTableModel->item(i, 5);
+
+        double tableVal1 = tableItem1->text().toDouble();
+        double tableVal2 = tableItem2->text().toDouble();
+        double tableVal3 = tableItem3->text().toDouble();
+        double tableVal4 = tableItem4->text().toDouble();
+        double tableVal5 = tableItem5->text().toDouble();
+        double tableVal6 = tableItem6->text().toDouble();
+
+        if (minX > tableVal1)
+            minX = tableVal1;
+        if (maxX < tableVal1)
+            maxX = tableVal1;
+
+        if (minY > tableVal2)
+            minY = tableVal2;
+        if (maxY < tableVal2)
+            maxY = tableVal2;
+
+        
+
+        if (bNorm) //%
+        {            
+            vAxisX1.push_back(tableVal1);
+            vAxisY1.push_back(tableVal4);
+
+            vAxisX2.push_back(tableVal1);
+            vAxisY2.push_back(tableVal5);
+
+            vAxisX3.push_back(tableVal1);
+            vAxisY3.push_back(tableVal6);
+        }
+        else
+        {
+            vAxisX1.push_back(tableVal1);
+            vAxisY1.push_back(tableVal2);
+
+            vAxisX2.push_back(tableVal1);
+            vAxisY2.push_back(tableVal3);
+
+            vAxisX3.push_back(tableVal1);
+            vAxisY3.push_back(tableVal6);
+        }        
+    }
+
+    ui.customPlotProfile->addGraph();
+    ui.customPlotProfile->graph(0)->setData(vAxisX1, vAxisY1);
+    ui.customPlotProfile->graph(0)->setPen(QPen(Qt::blue));
+    ui.customPlotProfile->graph(0)->setName("Ref. dose");
+
+    ui.customPlotProfile->addGraph();
+    ui.customPlotProfile->graph(1)->setData(vAxisX2, vAxisY2);
+    ui.customPlotProfile->graph(1)->setPen(QPen(Qt::red));
+    ui.customPlotProfile->graph(1)->setName("Compared dose");
+
+    /* ui.customPlotProfile->addGraph();
+     ui.customPlotProfile->graph(2)->setData(vAxisX3, vAxisY3);
+     ui.customPlotProfile->graph(2)->setPen(QPen(Qt::green));
+     ui.customPlotProfile->graph(2)->setName("Gamma");*/
+
+    if (bInitMinMax)
+    {
+        //float marginX = 10;
+        //float marginY = 100;
+        ui.lineEditXMin->setText(QString("%1").arg(minX));
+        ui.lineEditXMax->setText(QString("%1").arg(maxX));
+        //ui.lineEditYMin->setText(QString("%1").arg(minY));
+        //ui.lineEditYMax->setText(QString("%1").arg(maxY + marginY));
+    }    
+
+    double tmpXMin = ui.lineEditXMin->text().toDouble();
+    double tmpXMax = ui.lineEditXMax->text().toDouble();
+    double tmpYMin = ui.lineEditYMin->text().toDouble();
+    double tmpYMax = ui.lineEditYMax->text().toDouble();
+
+    ui.customPlotProfile->xAxis->setRange(tmpXMin, tmpXMax);
+    ui.customPlotProfile->yAxis->setRange(tmpYMin, tmpYMax);
+
+    ui.customPlotProfile->xAxis->setLabel("mm");
+
+    if (bNorm)
+        ui.customPlotProfile->yAxis->setLabel("%");
+    else
+        ui.customPlotProfile->yAxis->setLabel("cGy");
+    
+    ui.customPlotProfile->setTitle("Dose profile");
+
+    QFont titleFont = font();
+    titleFont.setPointSize(10);
+
+    ui.customPlotProfile->setTitleFont(titleFont);
+
+    ui.customPlotProfile->legend->setVisible(true);
+    QFont legendFont = font();  // start out with MainWindow's font..
+    legendFont.setPointSize(8); // and make a bit smaller for legend
+    ui.customPlotProfile->legend->setFont(legendFont);
+    ui.customPlotProfile->legend->setPositionStyle(QCPLegend::psTopRight);
+    ui.customPlotProfile->legend->setBrush(QBrush(QColor(255, 255, 255, 200)));
+    ui.customPlotProfile->replot();
+}
+
+void gamma_gui::SLT_RunGamma2D()
+{
+    if (!m_spCurRef2D)
+        return;
+    if (!m_spCurComp2D)
+        return;
+    //Find export folder.
+    if (m_strlistPath_RD_Read_Comp.empty())
+        return;
+
+    if (m_strlistFileBaseName_Comp.empty())
+        return;
+
+    QComboBox* crntCombo = ui.comboBoxCompareFile;
+    int iCnt = crntCombo->count();
+    if (iCnt < 1 || m_strlistFileBaseName_Comp.count() != iCnt)
+        return;
+
+    int curIdx = crntCombo->currentIndex(); //this should be basename       
+
+    if (curIdx < 0)
+        return;
+
+    //QString strPathOutputRoot = m_strlistPath_RD_Comp.at(curIdx);
+    ////QString strPathOutputRoot = m_strPathDirWorkDir;
+    //QFileInfo fInfo(strPathOutputRoot);
+    //QDir crntDir = fInfo.absolutePath();  
+
+    QDir crntDir = QDir(m_strPathDirWorkDir);
+
+    if (!crntDir.exists())
+    {
+        QUTIL::ShowErrorMessage("Error! working space is not ready");
+    }
+
+    //Get Current plane
+    
+    float probePosX = ui.lineEdit_ProbePosX->text().toFloat();
+    float probePosY = ui.lineEdit_ProbePosY->text().toFloat();
+    float probePosZ = ui.lineEdit_ProbePosZ->text().toFloat();    
+
+    QString curBaseNameRef = m_strlistFileBaseName_Ref.at(curIdx);
+    QString curBaseNameComp = m_strlistFileBaseName_Comp.at(curIdx);
+
+    QString strPlane;
+    if (ui.radioButtonAxial->isChecked())
+    {
+        strPlane = "AXL_Z" + QString::number(probePosZ, 'f', 1) + "mm";
+        //fixedPos = probePosZ;
+    }
+    else if (ui.radioButtonSagittal->isChecked())
+    {
+        strPlane = "SAG_X" + QString::number(probePosX, 'f', 1) + "mm";
+        //fixedPos = probePosX;
+    }
+    else if (ui.radioButtonFrontal->isChecked())
+    {
+        strPlane = "FRN_Y" + QString::number(probePosY, 'f', 1) + "mm";
+        //fixedPos = probePosY;
+    }
+
+    QString subDirName = curBaseNameComp + "_" + strPlane;
+    bool tmpResult = crntDir.mkdir(subDirName); //what if the directory exists?	
+
+    if (!tmpResult)
+        cout << "Warning! Directory for 2D gamma already exists. files will be overwritten." << endl;
+
+    //QString strSavingFolder = crntDir.absolutePath() + "/" + subDirName;
+    QString strSavingFolder = crntDir.absolutePath() + "/" + subDirName;
+
+    QDir dirSaving(strSavingFolder);
+    if (!dirSaving.exists())
+    {
+        cout << "Dir is not found:" << strSavingFolder.toLocal8Bit().constData() << endl;
+        return;
+    }
+
+    /* From now on, the target folder is ready */
+
+    QString tmpFilePathRef = strSavingFolder + "/" + "Gamma2DRef.mha";
+    QString tmpFilePathComp = strSavingFolder + "/" + "Gamma2DComp.mha";    
+
+    //Save current 2D
+    m_spCurRef2D;
+    m_spCurComp2D;
+
+    QUTIL::SaveFloatImage2D(tmpFilePathRef.toLocal8Bit().constData(), m_spCurRef2D);
+    QUTIL::SaveFloatImage2D(tmpFilePathComp.toLocal8Bit().constData(), m_spCurComp2D);        
+
+    Gamma_parms parms;
+    //Gamma param: should come from the UI
+
+    parms.mask_image_fn = "";
+    //parms->reference_dose;
+    parms.gamma_max = 2.0;
+    parms.b_compute_full_region = false;
+    parms.b_resample_nn = false; //default: false
+    parms.b_ref_only_threshold = false;
+
+    //From File List
+    parms.ref_image_fn = tmpFilePathRef.toLocal8Bit().constData();
+    parms.cmp_image_fn = tmpFilePathComp.toLocal8Bit().constData();
+
+    //From GUI
+    if (ui.checkBox_inhereResample->isChecked())
+        parms.f_inherent_resample_mm = ui.lineEdit_inhereResample->text().toDouble();
+    else
+        parms.f_inherent_resample_mm = -1.0;
+
+    parms.b_interp_search = ui.checkBox_Interp_search->isChecked();
+
+    if (ui.radioButton_localGamma->isChecked())
+    {
+
+        parms.b_local_gamma = true;        
+    }
+    else
+    {
+
+        parms.b_local_gamma = false;        
+    }
+
+    float inputRefDose = ui.lineEdit_refDoseInGy->text().toFloat();
+
+    if (inputRefDose <= 0) //blank
+    {
+        parms.have_reference_dose = false;
+    }
+    else
+    {
+        parms.have_reference_dose = true;
+        parms.reference_dose = inputRefDose;
+    }
+
+    parms.dta_tolerance = ui.lineEdit_dta_tol->text().toDouble();
+    parms.dose_tolerance = ui.lineEdit_dose_tol->text().toDouble() / 100.0;//gui input: 3% --> param: 0.03
+    parms.f_analysis_threshold = ui.lineEdit_cutoff_dose->text().toDouble() / 100.0;
+
+    //Saving folder: comp folder. FileName Should Include dta, dose, local/global
+
+    //QFileInfo fInfo = QFileInfo(tmpFilePathComp);
+    //QString dirPath = fInfo.absolutePath();
+    //QString baseName = fInfo.completeBaseName();
+
+    QString strLocGlob;
+
+    if (parms.b_local_gamma)
+        strLocGlob = "loc";
+    else
+        strLocGlob = "glb";
+
+    QString strSettingAbs = QString::number(parms.dta_tolerance, 'f', 0) + "mm_" + ""
+        + QString::number(parms.dose_tolerance*100.0, 'f', 0) + "%_" + strLocGlob;
+
+    //QString tmpFilePathComp = strSavingFolder + "/" + "Gamma2DComp.mha";
+
+    QString strPathGamma2D = strSavingFolder + "/" + "gamma2D" + ".mha";
+ //   if (ui.checkBox_gammamap_output->isChecked())
+   // {        
+        parms.out_image_fn = strPathGamma2D.toLocal8Bit().constData();     
+   // }
+
+    QString strPathFailmap2D = strSavingFolder + "/" + "fail2D" + ".mha";
+    //if (ui.checkBox_failuremap_output->isChecked())
+   // {
+        parms.out_failmap_fn = strPathFailmap2D.toLocal8Bit().constData();
+   // }
+
+    QString strPathReport = strSavingFolder + "/" + "text_report" + ".txt";
+    cout << "strPathReport= " << strPathReport.toLocal8Bit().constData() << endl;
+
+    parms.out_report_fn = strPathReport.toLocal8Bit().constData();
+
+    float refDoseGy;
+    QString overallReport = GammaMain(&parms, refDoseGy); //report for a single case
+    //Update GUI
+
+    //Read gammap2D
+
+    //m_spGamma2DResult;
+    QUTIL::LoadFloatImage2D(strPathGamma2D.toLocal8Bit().constData(), m_spGamma2DResult);    
+
+    //Update plainTextEditGammaResult
+    QFileInfo reportInfo = QFileInfo(strPathReport);
+
+    if (!reportInfo.exists())
+    {
+        cout << "Error! output text doesn't exist." << endl;
+        return;
+    }    
+
+
+    //Update Report txt here
+    ui.plainTextEditGammaResult2D->clear();
+    QStringList strList = QUTIL::LoadTextFile(strPathReport.toLocal8Bit().constData());
+
+    for (int i = 0; i < strList.count(); i++)
+        ui.plainTextEditGammaResult2D->appendPlainText(strList.at(i));
+
+    QTextCursor txtCursor = ui.plainTextEditGammaResult2D->textCursor();
+    //position where you want it
+    txtCursor.setPosition(0);
+    ui.plainTextEditGammaResult2D->setTextCursor(txtCursor);
+
+
+    //SaveIBA Image format
+
+    
+    QString IBAFilePathRef = strSavingFolder + "/" + strPlane + "_" + curBaseNameRef + ".OPG";
+    QString IBAFilePathComp = strSavingFolder + "/" + strPlane + "_" + curBaseNameComp + ".OPG";
+    
+    SaveDoseIBAGenericTXTFromItk(IBAFilePathRef.toLocal8Bit().constData(), m_spCurRef2D);
+    SaveDoseIBAGenericTXTFromItk(IBAFilePathComp.toLocal8Bit().constData(), m_spCurComp2D);
+    
+    SLT_RestoreZoomPan();
+    SLT_DrawAll();
+}
+
+void gamma_gui::SLT_GoCenterPosRef()
+{
+    QComboBox* crntCombo = ui.comboBoxCompareFile;    
+    int curIdx = crntCombo->currentIndex(); //this should be basename       
+
+    if (curIdx < 0)
+        return;
+
+    if (m_vRefDoseImages.empty())
+        return;
+
+    FloatImageType::Pointer spCurFloat3D = m_vRefDoseImages.at(curIdx);
+
+    FloatImageType::PointType ptOrigin = spCurFloat3D->GetOrigin();
+    FloatImageType::SizeType imgSize = spCurFloat3D->GetBufferedRegion().GetSize(); //dimension
+    FloatImageType::SpacingType imgSpacing = spCurFloat3D->GetSpacing(); //dimension    
+
+    VEC3D middlePtPos;
+    middlePtPos.x = ptOrigin[0] + imgSize[0] * imgSpacing[0] / 2.0;
+    middlePtPos.y = ptOrigin[1] + imgSize[1] * imgSpacing[1] / 2.0;
+    middlePtPos.z = ptOrigin[2] + imgSize[2] * imgSpacing[2] / 2.0;
+
+    ui.lineEdit_ProbePosX->setText(QString::number(middlePtPos.x, 'f', 1));
+    ui.lineEdit_ProbePosY->setText(QString::number(middlePtPos.y, 'f', 1));
+    ui.lineEdit_ProbePosZ->setText(QString::number(middlePtPos.z, 'f', 1));
+
+    SLT_DrawAll(); //triggered when Go Position button
+}
+
+void gamma_gui::SLT_GoCenterPosComp()
+{
+    QComboBox* crntCombo = ui.comboBoxCompareFile;
+    int curIdx = crntCombo->currentIndex(); //this should be basename       
+
+    if (curIdx < 0)
+        return;
+
+    if (m_vCompDoseImages.empty())
+        return;
+
+    FloatImageType::Pointer spCurFloat3D = m_vCompDoseImages.at(curIdx);
+
+    FloatImageType::PointType ptOrigin = spCurFloat3D->GetOrigin();
+    FloatImageType::SizeType imgSize = spCurFloat3D->GetBufferedRegion().GetSize(); //dimension
+    FloatImageType::SpacingType imgSpacing = spCurFloat3D->GetSpacing(); //dimension    
+
+    VEC3D middlePtPos;
+    middlePtPos.x = ptOrigin[0] + imgSize[0] * imgSpacing[0] / 2.0;
+    middlePtPos.y = ptOrigin[1] + imgSize[1] * imgSpacing[1] / 2.0;
+    middlePtPos.z = ptOrigin[2] + imgSize[2] * imgSpacing[2] / 2.0;
+
+    ui.lineEdit_ProbePosX->setText(QString::number(middlePtPos.x, 'f', 1));
+    ui.lineEdit_ProbePosY->setText(QString::number(middlePtPos.y, 'f', 1));
+    ui.lineEdit_ProbePosZ->setText(QString::number(middlePtPos.z, 'f', 1));
+
+    SLT_DrawAll(); //triggered when Go Position button
+}
+
+void gamma_gui::SLT_NormCompFromRefNorm() //button
+{
+    QString crntNormComp = ui.lineEditNormComp->text();
+    float crntNormF = crntNormComp.toFloat();
+
+    if (crntNormF <= 0)
+        return;
+
+    //Get RefValue
+    int iCurrRefNormVal = ui.sliderNormRef->value(); //cGy
+    int iCurrCompNormVal = qRound(iCurrRefNormVal*crntNormF);
+
+    ui.sliderNormComp->setValue(iCurrCompNormVal);
+}
+
+
+void gamma_gui::SaveDoseIBAGenericTXTFromItk(QString strFilePath, FloatImage2DType::Pointer& spFloatDose)
+{
+    if (!spFloatDose)
+        return;
+
+    //Set center point first (from MainDLg --> TIF) //this will update m_rXPos.a, b
+    //SetRationalCenterPosFromDataCenter(dataCenterPoint);
+    //POINT ptCenter = GetDataCenter(); //will get dataPt from m_rXPos    
+
+    FloatImage2DType::PointType ptOrigin = spFloatDose->GetOrigin();
+    FloatImage2DType::SizeType imgSize = spFloatDose->GetBufferedRegion().GetSize(); //dimension
+    FloatImage2DType::SpacingType imgSpacing = spFloatDose->GetSpacing(); //dimension    
+
+    int imgWidth = imgSize[0];
+    int imgHeight = imgSize[1];
+
+    float spacingX = imgSpacing[0];
+    float spacingY = imgSpacing[1];
+
+    float originX = ptOrigin[0];
+    float originY = ptOrigin[1];
+
+    ofstream fout;
+    fout.open(strFilePath.toLocal8Bit().constData());
+
+    fout << "<opimrtascii>" << endl;
+    fout << endl;
+
+    fout << "<asciiheader>" << endl;
+    fout << "Separator:	[TAB]" << endl;
+    fout << "Workspace Name:	n/a" << endl;
+    fout << "File Name:	n/a" << endl;
+    fout << "Image Name:	n/a" << endl;
+    fout << "Radiation Type:	Photons" << endl;
+    fout << "Energy:	0.0 MV" << endl;
+    fout << "SSD:	10.0 cm" << endl;
+    fout << "SID:	100.0 cm" << endl;
+    fout << "Field Size Cr:	10.0 cm" << endl;
+    fout << "Field Size In:	10.0 cm" << endl;
+    fout << "Data Type:	Abs. Dose" << endl;
+    fout << "Data Factor:	1.000" << endl;
+    fout << "Data Unit:	mGy" << endl;
+    fout << "Length Unit:	cm" << endl;
+    fout << "Plane:	" << "XY" << endl;
+    fout << "No. of Columns:	" << imgWidth << endl;
+    fout << "No. of Rows:	" << imgHeight << endl;
+    fout << "Number of Bodies:	" << 1 << endl;
+    fout << "Operators Note:	made by ykp" << endl;
+    fout << "</asciiheader>" << endl;
+    fout << endl;
+
+    fout << "<asciibody>" << endl;
+    fout << "Plane Position:     0.00 cm" << endl;
+    fout << endl;
+
+    fout << "X[cm]" << "\t";
+
+    QString strTempX;
+    double fPosX = 0.0;
+
+    for (int i = 0; i < imgWidth; i++)
+    {
+        fPosX = (originX + i*spacingX) * 0.1;//mm --> cm        
+        fout << QString::number(fPosX, 'f', 3).toLocal8Bit().constData() << "\t";
+    }
+    fout << endl;
+
+    fout << "Y[cm]" << endl;
+
+    QString strTempY;
+    double fPosY = 0.0; //cm
+
+    int imgVal = 0; // mGy, integer
+
+
+    itk::ImageRegionConstIterator<FloatImage2DType> it(spFloatDose, spFloatDose->GetRequestedRegion());
+    it.GoToBegin();
+
+
+    float* pImg;
+    pImg = new float [imgWidth*imgHeight];
+        
+    int idx = 0;
+    for (it.GoToBegin(); !it.IsAtEnd(); ++it)
+    {
+        pImg[idx] = it.Get();
+        idx++;
+    }
+
+    for (int i = imgHeight - 1; i >= 0; i--)
+    {
+        //spacing: mm/px --> to make a cm, * 0.1 
+        fPosY = (originY + i*spacingY) * 0.1; //--> Image writing From bottom to Top   // Y value < 0  means Inferior in XY plane --> more make sense
+        QString strYVal = QString::number(fPosY, 'f', 3);
+        fout << strYVal.toLocal8Bit().constData() << "\t";
+
+        for (int j = 0; j < imgWidth; j++)
+        {
+            imgVal = qRound(pImg[imgWidth*i + j] * 1000); // Gy --> mGy
+            fout << imgVal << "\t";
+        }
+        fout << endl;
+    }
+
+    delete [] pImg;
+
+    fout << "</asciibody>" << endl;
+    fout << "</opimrtascii>" << endl;
+
+    //  POINT GetDataCenter();//data center index, 0 based
+    // double m_spacingX;	// mm/pixel
+    //double m_spacingY;// mm/pixel
+
+    fout.close();
+
+    return;
+}
+
+void gamma_gui::SLT_WhenSelectCombo()
+{
+    QComboBox* crntCombo = ui.comboBoxCompareFile;    
+    int curIdx = crntCombo->currentIndex(); //this should be basename    
+    int iCnt = crntCombo->count();
+    if (iCnt < 1)
+        return;
+
+
+    if ((int)m_vRefDose.size() != iCnt ||
+        (int)m_strlistPath_RD_Read_Ref.size() != iCnt ||
+        (int)m_strlistPath_RD_Read_Comp.size() != iCnt ||
+        (int)m_strlistPath_Output_Gammamap.size() != iCnt)
+    {
+        cout << "Error! SLT_WhenSelectCombo file count doesn't match!" << endl;
+
+        cout << "crntComboCnt " << iCnt << endl;
+        cout << "m_vRefDose " << m_vRefDose.size() << endl;
+        cout << "m_strlistPath_RD_Read_Ref " << m_strlistPath_RD_Read_Ref.size() << endl;
+        cout << "m_strlistPath_RD_Read_Comp " << m_strlistPath_RD_Read_Comp.size() << endl;
+        cout << "m_strlistPath_Output_Gammamap " << m_strlistPath_Output_Gammamap.size() << endl;
+
+        return;
+    }
+        
+    if ((int)(m_vRefDoseImages.size()) != iCnt ||
+        (int)(m_vCompDoseImages.size()) != iCnt ||
+        (int)(m_vGammaMapImages.size()) != iCnt)
+    {
+        cout << "Error! ItkImage Pointer count doesn't match!" << endl;
+        return;
+    }
+
+
+    
+
+    disconnect(ui.sliderNormRef, SIGNAL(valueChanged(int)), this, SLOT(SLT_DrawAll()));
+    disconnect(ui.sliderNormComp, SIGNAL(valueChanged(int)), this, SLOT(SLT_DrawAll()));
+
+    ui.sliderNormRef->setValue(qRound(m_vRefDose.at(curIdx) * 100)); //Gy to cGy
+    ui.sliderNormComp->setValue(qRound(m_vRefDose.at(curIdx) * 100)); //Gy to cGy
+
+    connect(ui.sliderNormRef, SIGNAL(valueChanged(int)), this, SLOT(SLT_DrawAll()));
+    connect(ui.sliderNormComp, SIGNAL(valueChanged(int)), this, SLOT(SLT_DrawAll()));
+
+
+    QString strPath_ref = m_strlistPath_RD_Read_Ref.at(curIdx); //always readable mha format
+    QString strPath_comp = m_strlistPath_RD_Read_Comp.at(curIdx);
+    QString strPath_gamma = m_strlistPath_Output_Gammamap.at(curIdx);
+
+    QFileInfo info_ref = QFileInfo(strPath_ref);
+    QFileInfo info_comp = QFileInfo(strPath_comp); 
+
+    bool bLowMem = ui.checkBox_low_mem->isChecked();
+
+    if (bLowMem)
+    {
+        /*FloatImageType::Pointer spImgRef = m_vRefDoseImages.at(curIdx);
+        FloatImageType::Pointer spImgComp = m_vCompDoseImages.at(curIdx);
+        FloatImageType::Pointer spImgGamma = m_vGammaMapImages.at(curIdx);       */
+        cout << "Loading image from file. Index= " << curIdx << endl;
+        
+        QUTIL::LoadFloatImage3D(strPath_ref.toLocal8Bit().constData(), m_vRefDoseImages.at(curIdx));
+        QUTIL::LoadFloatImage3D(strPath_comp.toLocal8Bit().constData(), m_vCompDoseImages.at(curIdx));
+        QUTIL::LoadFloatImage3D(strPath_gamma.toLocal8Bit().constData(), m_vGammaMapImages.at(curIdx));
+
+
+        if (m_iLastLoadedIndex >= 0)
+        {
+            m_vRefDoseImages.at(m_iLastLoadedIndex) = m_spDummyLowMemImg;
+            m_vCompDoseImages.at(m_iLastLoadedIndex) = m_spDummyLowMemImg;
+            m_vGammaMapImages.at(m_iLastLoadedIndex) = m_spDummyLowMemImg;
+        }
+
+        m_iLastLoadedIndex = curIdx;
+    }
+    else
+    {
+        //do nothing
+    }
+
+
+  
+
+    SLT_WhenChangePlane(); //RestorePanZoom + DrawAll
+    //SLT_DrawAll();
+}
+
+
+
+
+void gamma_gui::SLT_MouseWheelUpdateRef()
+{
+    if (ui.labelReferDose->m_pYK16Image == NULL||
+        ui.labelCompDose->m_pYK16Image == NULL ||
+        ui.labelGammaMap3D->m_pYK16Image == NULL)
+    {
+        return;
+    }
+
+    QComboBox* crntCombo = ui.comboBoxCompareFile;
+    int curIdx = crntCombo->currentIndex(); //this should be basename    
+
+    if (curIdx < 0)
+        return;
+
+    if (curIdx >= (int)(m_vRefDoseImages.size()))
+        return;
+
+    FloatImageType::Pointer spCurImg = m_vRefDoseImages.at(curIdx);
+
+    VEC3D ptLimitStart = { 0.0, 0.0, 0.0 };
+    VEC3D ptLimitEnd = { 0.0, 0.0, 0.0 };
+
+    QUTIL::GetGeometricLimitFloatImg(spCurImg, ptLimitStart, ptLimitEnd);
+
+    if (ui.checkBox_ScrollZoom->isChecked())
+    {
+        double oldZoom = ui.labelReferDose->m_pYK16Image->m_fZoom;
+        double fWeighting = 0.2;
+        float vZoomVal = oldZoom + ui.labelReferDose->m_iMouseWheelDelta * fWeighting;
+        ui.labelReferDose->m_pYK16Image->SetZoom(vZoomVal);
+        ui.labelCompDose->m_pYK16Image->SetZoom(vZoomVal);
+        ui.labelGammaMap3D->m_pYK16Image->SetZoom(vZoomVal);
+
+        if (ui.labelGammaMap2D->m_pYK16Image != NULL)
+            ui.labelGammaMap2D->m_pYK16Image->SetZoom(vZoomVal);
+    }
+    else //change slice
+    {
+        double fWeighting = 1.0;
+        enPLANE curPlane = PLANE_AXIAL;
+        float probePosX = ui.lineEdit_ProbePosX->text().toFloat();
+        float probePosY = ui.lineEdit_ProbePosY->text().toFloat();
+        float probePosZ = ui.lineEdit_ProbePosZ->text().toFloat();
+
+        if (ui.radioButtonAxial->isChecked())
+        {
+            curPlane = PLANE_AXIAL;
+            probePosZ = probePosZ + ui.labelReferDose->m_iMouseWheelDelta*fWeighting;
+
+            if (probePosZ <= ptLimitStart.z)
+                probePosZ = ptLimitStart.z;
+
+            if (probePosZ >= ptLimitEnd.z)
+                probePosZ = ptLimitEnd.z;
+
+            
+            ui.lineEdit_ProbePosZ->setText(QString::number(probePosZ, 'f', 1));
+        }
+        else if (ui.radioButtonSagittal->isChecked())
+        {
+            curPlane = PLANE_SAGITTAL;
+            probePosX = probePosX + ui.labelReferDose->m_iMouseWheelDelta*fWeighting;
+            if (probePosX <= ptLimitStart.x)
+                probePosX = ptLimitStart.x;
+
+            if (probePosX >= ptLimitEnd.x)
+                probePosX = ptLimitEnd.x;            
+
+            ui.lineEdit_ProbePosX->setText(QString::number(probePosX, 'f', 1));          
+        }
+        else if (ui.radioButtonFrontal->isChecked())
+        {
+            curPlane = PLANE_FRONTAL;
+            probePosY = probePosY + ui.labelReferDose->m_iMouseWheelDelta*fWeighting;
+            if (probePosY <= ptLimitStart.y)
+                probePosY = ptLimitStart.y;
+            if (probePosY >= ptLimitEnd.y)
+                probePosY = ptLimitEnd.y;       
+
+            ui.lineEdit_ProbePosY->setText(QString::number(probePosY, 'f', 1));
+        }        
+    }
+
+    SLT_DrawAll();
+}
+
+void gamma_gui::SLT_MouseWheelUpdateComp()
+{
+    if (ui.labelReferDose->m_pYK16Image == NULL ||
+        ui.labelCompDose->m_pYK16Image == NULL ||
+        ui.labelGammaMap3D->m_pYK16Image == NULL)
+    {
+        return;
+    }
+
+    QComboBox* crntCombo = ui.comboBoxCompareFile;
+    int curIdx = crntCombo->currentIndex(); //this should be basename    
+
+    if (curIdx < 0)
+        return;
+
+    if (curIdx >= (int)(m_vRefDoseImages.size()))
+        return;
+
+    FloatImageType::Pointer spCurImg = m_vCompDoseImages.at(curIdx);
+
+    VEC3D ptLimitStart = { 0.0, 0.0, 0.0 };
+    VEC3D ptLimitEnd = { 0.0, 0.0, 0.0 };
+
+    QUTIL::GetGeometricLimitFloatImg(spCurImg, ptLimitStart, ptLimitEnd);
+
+
+
+
+    if (ui.checkBox_ScrollZoom->isChecked())
+    {
+        double oldZoom = ui.labelCompDose->m_pYK16Image->m_fZoom;
+        double fWeighting = 0.2;
+        float vZoomVal = oldZoom + ui.labelCompDose->m_iMouseWheelDelta * fWeighting;
+
+        ui.labelReferDose->m_pYK16Image->SetZoom(vZoomVal);
+        ui.labelCompDose->m_pYK16Image->SetZoom(vZoomVal);
+        ui.labelGammaMap3D->m_pYK16Image->SetZoom(vZoomVal);
+
+        if (ui.labelGammaMap2D->m_pYK16Image != NULL)
+            ui.labelGammaMap2D->m_pYK16Image->SetZoom(vZoomVal);
+
+    }
+    else //change slice
+    {
+        double fWeighting = 1.0;
+        enPLANE curPlane = PLANE_AXIAL;
+        float probePosX = ui.lineEdit_ProbePosX->text().toFloat();
+        float probePosY = ui.lineEdit_ProbePosY->text().toFloat();
+        float probePosZ = ui.lineEdit_ProbePosZ->text().toFloat();
+
+        if (ui.radioButtonAxial->isChecked())
+        {
+            curPlane = PLANE_AXIAL;
+            probePosZ = probePosZ + ui.labelCompDose->m_iMouseWheelDelta*fWeighting;
+
+
+            if (probePosZ <= ptLimitStart.z)
+                probePosZ = ptLimitStart.z;
+
+            if (probePosZ >= ptLimitEnd.z)
+                probePosZ = ptLimitEnd.z;
+
+
+
+            ui.lineEdit_ProbePosZ->setText(QString::number(probePosZ, 'f', 1));
+        }
+        else if (ui.radioButtonSagittal->isChecked())
+        {
+            curPlane = PLANE_SAGITTAL;
+            probePosX = probePosX + ui.labelCompDose->m_iMouseWheelDelta*fWeighting;
+
+            if (probePosX <= ptLimitStart.x)
+                probePosX = ptLimitStart.x;
+
+            if (probePosX >= ptLimitEnd.x)
+                probePosX = ptLimitEnd.x;
+
+            ui.lineEdit_ProbePosX->setText(QString::number(probePosX, 'f', 1));
+        }
+        else if (ui.radioButtonFrontal->isChecked())
+        {
+            curPlane = PLANE_FRONTAL;
+            probePosY = probePosY + ui.labelCompDose->m_iMouseWheelDelta*fWeighting;
+
+            if (probePosY <= ptLimitStart.y)
+                probePosY = ptLimitStart.y;
+            if (probePosY >= ptLimitEnd.y)
+                probePosY = ptLimitEnd.y;
+
+            ui.lineEdit_ProbePosY->setText(QString::number(probePosY, 'f', 1));
+        }
+    }
+
+    SLT_DrawAll();
+
+}
+
+void gamma_gui::SLT_MouseWheelUpdateGamma2D()
+{
+
+}
+
+void gamma_gui::SLT_MouseWheelUpdateGamma3D()
+{
+    if (ui.labelReferDose->m_pYK16Image == NULL ||
+        ui.labelCompDose->m_pYK16Image == NULL ||
+        ui.labelGammaMap3D->m_pYK16Image == NULL)
+    {
+        return;
+    }
+
+    QComboBox* crntCombo = ui.comboBoxCompareFile;
+    int curIdx = crntCombo->currentIndex(); //this should be basename    
+
+    if (curIdx < 0)
+        return;
+
+    if (curIdx >= (int)(m_vRefDoseImages.size()))
+        return;
+
+    FloatImageType::Pointer spCurImg = m_vGammaMapImages.at(curIdx);
+
+    VEC3D ptLimitStart = { 0.0, 0.0, 0.0 };
+    VEC3D ptLimitEnd = { 0.0, 0.0, 0.0 };
+
+    QUTIL::GetGeometricLimitFloatImg(spCurImg, ptLimitStart, ptLimitEnd);
+
+    if (ui.checkBox_ScrollZoom->isChecked())
+    {
+        double oldZoom = ui.labelGammaMap3D->m_pYK16Image->m_fZoom;
+        double fWeighting = 0.2;
+        float vZoomVal = oldZoom + ui.labelGammaMap3D->m_iMouseWheelDelta * fWeighting;
+
+        ui.labelReferDose->m_pYK16Image->SetZoom(vZoomVal);
+        ui.labelCompDose->m_pYK16Image->SetZoom(vZoomVal);
+        ui.labelGammaMap3D->m_pYK16Image->SetZoom(vZoomVal);
+
+        if (ui.labelGammaMap2D->m_pYK16Image != NULL)
+            ui.labelGammaMap2D->m_pYK16Image->SetZoom(vZoomVal);
+
+    }
+    else //change slice
+    {
+        double fWeighting = 1.0;
+        enPLANE curPlane = PLANE_AXIAL;
+        float probePosX = ui.lineEdit_ProbePosX->text().toFloat();
+        float probePosY = ui.lineEdit_ProbePosY->text().toFloat();
+        float probePosZ = ui.lineEdit_ProbePosZ->text().toFloat();
+
+        if (ui.radioButtonAxial->isChecked())
+        {
+            curPlane = PLANE_AXIAL;
+            probePosZ = probePosZ + ui.labelGammaMap3D->m_iMouseWheelDelta*fWeighting;
+
+            if (probePosZ <= ptLimitStart.z)
+                probePosZ = ptLimitStart.z;
+            if (probePosZ >= ptLimitEnd.z)
+                probePosZ = ptLimitEnd.z;
+
+            ui.lineEdit_ProbePosZ->setText(QString::number(probePosZ, 'f', 1));
+        }
+        else if (ui.radioButtonSagittal->isChecked())
+        {
+            curPlane = PLANE_SAGITTAL;
+            probePosX = probePosX + ui.labelGammaMap3D->m_iMouseWheelDelta*fWeighting;
+
+            if (probePosX <= ptLimitStart.x)
+                probePosX = ptLimitStart.x;
+            if (probePosX >= ptLimitEnd.x)
+                probePosX = ptLimitEnd.x;
+            ui.lineEdit_ProbePosX->setText(QString::number(probePosX, 'f', 1));
+        }
+        else if (ui.radioButtonFrontal->isChecked())
+        {
+            curPlane = PLANE_FRONTAL;
+            probePosY = probePosY + ui.labelGammaMap3D->m_iMouseWheelDelta*fWeighting;
+
+            if (probePosY <= ptLimitStart.y)
+                probePosY = ptLimitStart.y;
+            if (probePosY >= ptLimitEnd.y)
+                probePosY = ptLimitEnd.y;
+
+
+            ui.lineEdit_ProbePosY->setText(QString::number(probePosY, 'f', 1));
+        }
+    }
+
+    SLT_DrawAll();
+}
+
+void gamma_gui::SLT_MouseMoveUpdateRef()
+{ 
+
+}
+
+void gamma_gui::SLT_MouseMoveUpdateComp()
+{
+
+}
+
+void gamma_gui::SLT_MouseMoveUpdateGamma2D()
+{
+
+}
+
+void gamma_gui::SLT_MouseMoveUpdateGamma3D()
+{
+
+}
+
+void gamma_gui::SLT_RestoreZoomPan()
+{
+  
+
+    if (ui.labelReferDose->m_pYK16Image == NULL ||
+        ui.labelCompDose->m_pYK16Image == NULL ||
+        ui.labelGammaMap3D->m_pYK16Image == NULL)
+    {
+        return;
+    }
+
+    ui.labelReferDose->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    ui.labelReferDose->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    ui.labelCompDose->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    ui.labelCompDose->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    ui.labelGammaMap3D->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    ui.labelGammaMap3D->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    ui.labelGammaMap2D->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    ui.labelGammaMap2D->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    ui.labelReferDose->m_pYK16Image->SetZoom(1.0);
+    ui.labelCompDose->m_pYK16Image->SetZoom(1.0);
+    ui.labelGammaMap3D->m_pYK16Image->SetZoom(1.0);
+
+    if (ui.labelGammaMap2D->m_pYK16Image != NULL)
+        ui.labelGammaMap2D->m_pYK16Image->SetZoom(1.0);
+
+
+    ui.labelReferDose->m_pYK16Image->SetOffset(0,0);
+    ui.labelCompDose->m_pYK16Image->SetOffset(0, 0);
+    ui.labelGammaMap3D->m_pYK16Image->SetOffset(0, 0);
+
+    if (ui.labelGammaMap2D->m_pYK16Image != NULL)
+        ui.labelGammaMap2D->m_pYK16Image->SetOffset(0, 0);
+}
+
+void gamma_gui::SLT_WhenChangePlane()
+{
+    ///*ui.labelReferDose->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    //ui.labelReferDose->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    //ui.labelCompDose->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    //ui.labelCompDose->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    //ui.labelGammaMap3D->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    //ui.labelGammaMap3D->setFixedHeight(DEFAULT_LABEL_HEIGHT);
+
+    //ui.labelGammaMap2D->setFixedWidth(DEFAULT_LABEL_WIDTH);
+    //ui.labelGammaMap2D->setFixedHeight(DEFAULT_LABEL_HEIGHT);   */ 
+
+    SLT_RestoreZoomPan();
+    SLT_DrawAll();
+}
+
+void gamma_gui::SLT_MousePressedRightRef()
+{    
+    m_bMousePressedRightRef = true;
+    WhenMousePressedRight(ui.labelReferDose);
+}
+
+void gamma_gui::WhenMousePressedRight(qyklabel* pWnd)
+{
+    if (pWnd->m_pYK16Image == NULL)
+        return;
+
+    m_ptPanStart.setX(pWnd->x);
+    m_ptPanStart.setY(pWnd->y);
+
+    m_ptOriginalDataOffset.setX(pWnd->m_pYK16Image->m_iOffsetX);
+    m_ptOriginalDataOffset.setY(pWnd->m_pYK16Image->m_iOffsetY);
+}
+
+
+void gamma_gui::SLT_MousePressedRightComp()
+{
+    m_bMousePressedRightComp = true;
+    WhenMousePressedRight(ui.labelCompDose);
+}
+
+void gamma_gui::SLT_MousePressedRightGamma3D()
+{
+    m_bMousePressedRightGamma3D = true;
+    WhenMousePressedRight(ui.labelGammaMap3D);
+}
+
+void gamma_gui::SLT_MousePressedRightGamma2D()
+{
+    m_bMousePressedRightGamma2D = true;
+    WhenMousePressedRight(ui.labelGammaMap2D);
+}
+
+void gamma_gui::SLT_MouseReleasedRightRef()
+{
+    m_bMousePressedRightRef = false;
+}
+
+void gamma_gui::SLT_MouseReleasedRightComp()
+{
+    m_bMousePressedRightComp = false;
+}
+
+void gamma_gui::SLT_MouseReleasedRightGamma3D()
+{
+    m_bMousePressedRightGamma3D = false;
+}
+
+void gamma_gui::SLT_MouseReleasedRightGamma2D()
+{
+    m_bMousePressedRightGamma2D = false;
+}
+
+
+void gamma_gui::UpdatePanCommon(qyklabel* qWnd)
+{
+    if (qWnd->m_pYK16Image == NULL)
+        return;
+
+    double dspWidth = qWnd->width();
+    double dspHeight = qWnd->height();
+
+    int dataWidth = qWnd->m_pYK16Image->m_iWidth;
+    int dataHeight = qWnd->m_pYK16Image->m_iHeight;
+    if (dataWidth*dataHeight == 0)
+        return;
+
+   // int dataX = qWnd->GetDataPtFromMousePos().x();
+   // int dataY = qWnd->GetDataPtFromMousePos().y();
+
+    ////Update offset information of dispImage
+    //GetOriginalDataPos (PanStart)
+    //offset should be 0.. only relative distance matters. offset is in realtime changing
+    QPoint ptDataPanStartRel = qWnd->View2DataExt(m_ptPanStart, dspWidth,
+        dspHeight, dataWidth, dataHeight, QPoint(0, 0), qWnd->m_pYK16Image->m_fZoom);
+
+    QPoint ptDataPanEndRel = qWnd->View2DataExt(QPoint(qWnd->x, qWnd->y), dspWidth,
+        dspHeight, dataWidth, dataHeight, QPoint(0, 0), qWnd->m_pYK16Image->m_fZoom);
+
+    //int dspOffsetX = pOverlapWnd->x - m_ptPanStart.x();
+    //int dspOffsetY = m_ptPanStart.y() - pOverlapWnd->y;
+
+    /*QPoint ptDataStart= pOverlapWnd->GetDataPtFromViewPt(m_ptPanStart.x(),  m_ptPanStart.y());
+    QPoint ptDataEnd= pOverlapWnd->GetDataPtFromViewPt(pOverlapWnd->x, pOverlapWnd->y);*/
+
+    int curOffsetX = ptDataPanEndRel.x() - ptDataPanStartRel.x();
+    int curOffsetY = ptDataPanEndRel.y() - ptDataPanStartRel.y();
+
+    int prevOffsetX = m_ptOriginalDataOffset.x();
+    int prevOffsetY = m_ptOriginalDataOffset.y();
+
+    //double fZoom = qWnd->m_pYK16Image->m_fZoom;
+    qWnd->m_pYK16Image->SetOffset(prevOffsetX - curOffsetX, prevOffsetY - curOffsetY);
+
+    //SLT_DrawAll();
+}
+
+void gamma_gui::SLT_UpdatePanSettingRef() //Mouse Move
+{
+    if (!m_bMousePressedRightRef)
+        return;
+
+    if (ui.labelReferDose->m_pYK16Image == NULL || 
+        ui.labelCompDose->m_pYK16Image == NULL ||
+        ui.labelGammaMap3D->m_pYK16Image == NULL)
+        return;
+
+    UpdatePanCommon(ui.labelReferDose);
+    //Sync offset
+    int offsetX = ui.labelReferDose->m_pYK16Image->m_iOffsetX;
+    int offsetY = ui.labelReferDose->m_pYK16Image->m_iOffsetY;
+
+    ui.labelCompDose->m_pYK16Image->SetOffset(offsetX, offsetY);
+    ui.labelGammaMap3D->m_pYK16Image->SetOffset(offsetX, offsetY);
+
+    if (ui.labelGammaMap2D->m_pYK16Image != NULL)
+        ui.labelGammaMap2D->m_pYK16Image->SetOffset(offsetX, offsetY);    
+
+    SLT_DrawAll();
+}
+
+
+void gamma_gui::SLT_UpdatePanSettingComp()
+{
+    if (!m_bMousePressedRightComp)
+        return;
+
+    if (ui.labelReferDose->m_pYK16Image == NULL ||
+        ui.labelCompDose->m_pYK16Image == NULL ||
+        ui.labelGammaMap3D->m_pYK16Image == NULL)
+        return;
+
+    UpdatePanCommon(ui.labelCompDose);
+    //Sync offset
+    int offsetX = ui.labelCompDose->m_pYK16Image->m_iOffsetX;
+    int offsetY = ui.labelCompDose->m_pYK16Image->m_iOffsetY;
+
+    ui.labelReferDose->m_pYK16Image->SetOffset(offsetX, offsetY);
+    ui.labelGammaMap3D->m_pYK16Image->SetOffset(offsetX, offsetY);
+
+    if (ui.labelGammaMap2D->m_pYK16Image != NULL)
+        ui.labelGammaMap2D->m_pYK16Image->SetOffset(offsetX, offsetY);
+
+    SLT_DrawAll();
+}
+
+void gamma_gui::SLT_UpdatePanSettingGamma3D()
+{
+    if (!m_bMousePressedRightGamma3D)
+        return;
+
+    if (ui.labelReferDose->m_pYK16Image == NULL ||
+        ui.labelCompDose->m_pYK16Image == NULL ||
+        ui.labelGammaMap3D->m_pYK16Image == NULL)
+        return;
+
+    UpdatePanCommon(ui.labelGammaMap3D);
+    //Sync offset
+    int offsetX = ui.labelGammaMap3D->m_pYK16Image->m_iOffsetX;
+    int offsetY = ui.labelGammaMap3D->m_pYK16Image->m_iOffsetY;
+
+    ui.labelReferDose->m_pYK16Image->SetOffset(offsetX, offsetY);
+    ui.labelCompDose->m_pYK16Image->SetOffset(offsetX, offsetY);
+
+    if (ui.labelGammaMap2D->m_pYK16Image != NULL)
+        ui.labelGammaMap2D->m_pYK16Image->SetOffset(offsetX, offsetY);
+
+    SLT_DrawAll();
+
+}
+
+void gamma_gui::SLT_UpdatePanSettingGamma2D()
+{
+    return;
+}
+
+void gamma_gui::SLTM_RenameRDFilesByDICOMInfo()
+{
+    QStringList files = QFileDialog::getOpenFileNames(this, "Select one or more files to open",
+        m_strPathInputDir, "DICOM-RD files (*.dcm)");
+
+    int cnt = files.size();
+    if (cnt <= 0)
+        return;
+
+    QString strMsg = "Original file names will be gone. Backup is strongly recommended. Continue?";
+
+    QMessageBox msgBox;
+    msgBox.setText(strMsg);
+    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+
+    int res = msgBox.exec();
+
+    if (res == QMessageBox::Yes)
+    {
+
+    }
+        RenameFileByDCMInfo(files);
+}
+
+
+void gamma_gui::RenameFileByDCMInfo(QStringList& filenameList)
+{
+    int size = filenameList.size();
+
+    QString crntFilePath;
+    //Rt_study rt_study;
+
+    
+  
+    for (int a = 0; a < size; a++)
+    {
+        crntFilePath = filenameList.at(a);
+
+        //1) contructor
+        Dcmtk_loader dss(crntFilePath.toLocal8Bit().constData());
+        //2) parse directory: this will link dicome seriese to proper one (e.g. ds_dose)
+        dss.parse_directory();       
+
+        Dcmtk_series *pDcmSeries = dss.d_ptr->ds_rtdose; 
+
+        if (pDcmSeries == NULL)
+            continue;        
+       
+        //Pt name: 0010, 0010
+        QString strPtId = QString(pDcmSeries->get_cstr(DcmTagKey(0x0010, 0x0020)));
+        QString strRDType = QString(pDcmSeries->get_cstr(DcmTagKey(0x3004, 0x000A)));
+        //QString strFractionGroup = QString(pDcmSeries->get_cstr(DcmTagKey(0x300C, 0x0022)));
+        //QString strBeamNumberTmp = QString(pDcmSeries->get_cstr(DcmTagKey(0x300C, 0x0006)));
+
+        //long int iFractionGroup = 0;
+        //long int iBeamNumber = 0;
+
+        DcmSequenceOfItems *seqRefPlan = 0;        
+
+        bool rc = pDcmSeries->get_sequence(DcmTagKey(0x300c, 0x0002), seqRefPlan);
+        //rc = pDcmSeries->get_sequence(DcmTagKey(0x300C, 0x0020), seqFractionGroup);
+
+        long int iValBeamNumber = 0;
+        long int iValFractionGroupNumber = 0;
+        
+        if (rc)
+        {
+            //DcmSequenceOfItems *seqFractionGroup = 0;
+            int iNumOfRefPlanSeq = (int)(seqRefPlan->card());
+
+            for (int i = 0; i < iNumOfRefPlanSeq; i++)
+            {
+                OFCondition orc;
+
+               // const char *strVal = 0;
+
+                DcmItem *itemRefPlan = seqRefPlan->getItem(i);
+
+                //orc = item->findAndGetString(DcmTagKey(0x0008, 0x1150), strVal);//it works!
+                /*orc = item->findAndGetLongInt(DcmTagKey(0x300C, 0x0022), iVal);*/
+                /*if (!orc.good()){
+                    continue;
+                    }*/
+                DcmSequenceOfItems *seqFractionGroup = 0;
+                //rc = pDcmSeries->get_sequence(DcmTagKey(0x300c, 0x0020), seqFractionGroup);//ReferencedFractionGroupSequence                
+                orc = itemRefPlan->findAndGetSequence(DCM_ReferencedFractionGroupSequence, seqFractionGroup);//ReferencedFractionGroupSequence                
+
+                if (orc.good())
+                {
+                    int iNumOfFractionGroup = seqFractionGroup->card();
+
+                    DcmItem *itemFractionGroup = 0;
+
+                    for (int j = 0; j < iNumOfFractionGroup; j++)
+                    {
+                        itemFractionGroup = seqFractionGroup->getItem(j);
+                        DcmSequenceOfItems *seqRefBeam = 0;
+
+                        orc = itemFractionGroup->findAndGetLongInt(DCM_ReferencedFractionGroupNumber, iValFractionGroupNumber);
+
+                        //cout << "Group Number changed = " << iValFractionGroupNumber << endl;
+
+                        if (!orc.good())
+                            cout << "error! refFraction group number is not found" << endl;
+
+                        orc = itemFractionGroup->findAndGetSequence(DCM_ReferencedBeamSequence, seqRefBeam);//ReferencedFractionGroupSequence                                                              
+
+                        if (!orc.good())
+                            continue;
+
+                        int iNumOfRefBeam = seqRefBeam->card();
+
+                        for (int k = 0; k < iNumOfRefBeam; k++)
+                        {
+                            DcmItem *itemBeam = 0;
+                            itemBeam = seqRefBeam->getItem(k);
+
+                            //orc = itemBeam->findAndGetLongInt(DcmTagKey(0x300C, 0x0006), iValBeamNumber);
+                            orc = itemBeam->findAndGetLongInt(DCM_ReferencedBeamNumber, iValBeamNumber);
+
+                            //cout << "iValBeamNumber changed = " << iValBeamNumber << endl;
+                        }
+                    }
+                }
+            }                //iVal
+        }
+
+
+        //long int iFractionGroup = 0;
+        //long int iBeamNumber = 0;
+
+        //cout << "iFractionGroup " << iValFractionGroupNumber << endl;
+        //cout << "iBeamNumber " << iValBeamNumber << endl;
+
+        QString strFractionGroupNumber;
+        strFractionGroupNumber = strFractionGroupNumber.sprintf("%02d", (int)iValFractionGroupNumber);
+
+        QString strBeamNumber;
+        strBeamNumber = strBeamNumber.sprintf("%03d", (int)iValBeamNumber);
+
+        QFileInfo fileInfo = QFileInfo(crntFilePath);
+        QDir dir = fileInfo.absoluteDir();
+
+        QString newBaseName = strPtId + "_" + strRDType + "_" + strFractionGroupNumber + "_" + strBeamNumber;
+        //QString extStr = fileInfo.completeSuffix();
+
+        QString newFileName = newBaseName.append(".").append("dcm");
+        QString newPath = dir.absolutePath() + "/" + newFileName;
+
+        //cout << newPath.toLocal8Bit().constData() << endl;
+
+        //extract former part
+        QFile::rename(crntFilePath, newPath);
+    }// end of for,
+    
+    cout << "In total "<< size << " files were successfully renamed" << endl;
+
+}
+
+void gamma_gui::SaveCurrentGammaWorkSpace(QString& strPathGammaWorkSpace)
+{
+    ofstream fout;
+    fout.open(strPathGammaWorkSpace.toLocal8Bit().constData());
+
+    fout << "#GAMMA_WORK_SPACE_FILE V1.0#" << endl;
+
+    int cntItem1 = m_strlistPath_RD_Read_Ref.size();
+    int cntItem2 = m_strlistPath_RD_Read_Comp.size();
+    int cntItem3 = m_strlistFileBaseName_Ref.size();
+    int cntItem4 = m_strlistFileBaseName_Comp.size();
+
+    //int cntItem5 = m_strlistBatchReport.size();
+
+    int cntItem5 = m_strlistPath_Output_Gammamap.size();
+    int cntItem6 = m_strlistPath_Output_Failure.size();
+    int cntItem7 = m_strlistPath_Output_Report.size();
+    int cntItem8 = m_vRefDose.size();
+
+    if (cntItem1 < 1 ||
+        cntItem1 != cntItem2 ||
+        cntItem1 != cntItem3 ||
+        cntItem1 != cntItem4 ||
+        cntItem1 != cntItem5 ||
+        cntItem1 != cntItem6 ||
+        cntItem1 != cntItem7 ||
+        cntItem1 != cntItem8
+        )
+    {
+        cout << "Error! number of item doesn't match" << endl;
+        return;
+    }
+    
+    for (int i = 0; i < cntItem1; i++)
+    {
+        fout << endl;
+        fout << "ITEM_ID" << "\t" << i << endl;
+        fout << "RD_REF" << "\t" << m_strlistPath_RD_Read_Ref.at(i).toLocal8Bit().constData() << endl;
+        fout << "RD_COMP" << "\t" << m_strlistPath_RD_Read_Comp.at(i).toLocal8Bit().constData() << endl;
+        fout << "GAMMA_MAP" << "\t" << m_strlistPath_Output_Gammamap.at(i).toLocal8Bit().constData() << endl;
+        fout << "BASE_REF" << "\t" << m_strlistFileBaseName_Ref.at(i).toLocal8Bit().constData() << endl;
+        fout << "BASE_COMP" << "\t" << m_strlistFileBaseName_Comp.at(i).toLocal8Bit().constData() << endl;
+        fout << "GAMMA_FAILURE" << "\t" << m_strlistPath_Output_Failure.at(i).toLocal8Bit().constData() << endl;
+        fout << "GAMMA_REPORT" << "\t" << m_strlistPath_Output_Report.at(i).toLocal8Bit().constData() << endl;
+        fout << "REF_DOSE[Gy]" << "\t" << m_vRefDose.at(i) << endl;
+        fout << endl;
+    }
+
+    int numOfLinesInSimpleReport = m_strlistBatchReport.count();
+
+    fout << "#BATCH_SIMPLE_REPORT" << endl;
+    for (int i = 0; i < numOfLinesInSimpleReport; i++)
+    {
+        fout << m_strlistBatchReport.at(i).toLocal8Bit().constData() << endl;
+    }
+
+    fout.close();
+}
+
+
+bool gamma_gui::LoadGammaWorkSpace(QString& strPathGammaWorkSpace)
+{
+    ifstream fin;
+    fin.open(strPathGammaWorkSpace.toLocal8Bit().constData());
+
+    if (fin.fail())
+    {
+        cout << "Error occurred in file reading!" << endl;
+        return false;
+    }
+
+    //clear all the data
+    m_strlistPath_RD_Read_Ref.clear();
+    m_strlistPath_RD_Read_Comp.clear();
+
+    m_strlistPath_Output_Gammamap.clear();
+
+    m_strlistFileBaseName_Ref.clear();
+    m_strlistFileBaseName_Comp.clear();
+    
+    m_strlistPath_Output_Failure.clear();
+    m_strlistPath_Output_Report.clear();
+    m_vRefDose.clear();
+
+    m_strlistBatchReport.clear();
+
+    ////This will be also done in loading to mem
+    //m_vRefDoseImages.clear();
+    //m_vCompDoseImages.clear();
+    //m_vGammaMapImages.clear();
+
+    char str[MAX_LINE_LENGTH];
+
+    while (!fin.eof())
+    {
+        memset(str, 0, MAX_LINE_LENGTH);
+        fin.getline(str, MAX_LINE_LENGTH);
+        QString tmpStr = QString(str);
+        QString trimmed;
+        if (tmpStr.contains("ITEM_ID")) //go into subloop
+        {
+            while (!fin.eof())
+            {
+                memset(str, 0, MAX_LINE_LENGTH);
+                fin.getline(str, MAX_LINE_LENGTH);
+                tmpStr = QString(str);
+
+                trimmed = tmpStr.trimmed(); //if any blank line found
+                if (trimmed.length() < 1)
+                    break;
+                
+                //parsing
+                QStringList strListTmp = tmpStr.split("\t");
+
+                if (tmpStr.contains("RD_REF") && strListTmp.count() == 2) //go into subloop                
+                    m_strlistPath_RD_Read_Ref.push_back(strListTmp.at(1));                
+                else if (tmpStr.contains("RD_COMP") && strListTmp.count() == 2) //go into subloop                
+                    m_strlistPath_RD_Read_Comp.push_back(strListTmp.at(1));
+                else if (tmpStr.contains("GAMMA_MAP") && strListTmp.count() == 2) //go into subloop                
+                    m_strlistPath_Output_Gammamap.push_back(strListTmp.at(1));
+                else if (tmpStr.contains("BASE_REF") && strListTmp.count() == 2) //go into subloop                
+                    m_strlistFileBaseName_Ref.push_back(strListTmp.at(1));
+                else if (tmpStr.contains("BASE_COMP") && strListTmp.count() == 2) //go into subloop                
+                    m_strlistFileBaseName_Comp.push_back(strListTmp.at(1));
+                else if (tmpStr.contains("GAMMA_FAILURE") && strListTmp.count() == 2) //go into subloop                
+                    m_strlistPath_Output_Failure.push_back(strListTmp.at(1));
+                else if (tmpStr.contains("GAMMA_REPORT") && strListTmp.count() == 2) //go into subloop                
+                    m_strlistPath_Output_Report.push_back(strListTmp.at(1));
+                else if (tmpStr.contains("REF_DOSE[Gy]") && strListTmp.count() == 2) //go into subloop                
+                    m_vRefDose.push_back(strListTmp.at(1).toFloat());
+            }
+        }//end of ITEM
+
+        if (tmpStr.contains("BATCH_SIMPLE_REPORT"))
+        {
+            while (!fin.eof())
+            {
+                memset(str, 0, MAX_LINE_LENGTH);
+                fin.getline(str, MAX_LINE_LENGTH);
+                tmpStr = QString(str);
+
+                trimmed = tmpStr.trimmed(); //if any blank line found
+                if (trimmed.length() < 1)
+                    continue;
+
+                m_strlistBatchReport.push_back(tmpStr);
+            }
+        }
+    }
+  
+
+    //ui.plainTextEdit_RD_Comp->clear();
+    //m_strlistPath_RD_Comp.clear();
+    //m_strlistFileBaseName_Comp.clear();
+
+    //m_strlistPath_RD_Comp = tmpList;
+
+    //for (int i = 0; i < iFileCnt; i++)
+    //{
+    //    ui.plainTextEdit_RD_Comp->appendPlainText(m_strlistPath_RD_Comp.at(i)); //just for display
+
+    //    QFileInfo tmpInfo = QFileInfo(m_strlistPath_RD_Comp.at(i));
+
+    //    m_strlistFileBaseName_Comp.push_back(tmpInfo.completeBaseName());
+    //}
+
+
+    //cout << "File names are successfully loaded" << endl;
+    fin.close();
+
+    return true;
+}
+
+void gamma_gui::SLTM_LoadSavedWorkSpace()
+{
+    QString strPath = QFileDialog::getOpenFileName(this, "Select a gamma workspace file to open", m_strPathDirWorkDir, "AccuGamma workspace file (*.gws)");
+
+    if (strPath.length() <= 1)
+        return;
+
+    if (!LoadGammaWorkSpace(strPath)) //Only reading text files to fill up the vectors, without any auditing
+    {
+        QUTIL::ShowErrorMessage("Error! failed in loading workspace file");
+        return;
+    }       
+
+    QFileInfo fileInfo(strPath);
+    QDir dir = fileInfo.absoluteDir();
+    SetWorkDir(dir.absolutePath());
+
+    bool bOK = true;
+
+    QString strWorkDirPath = dir.absolutePath();
+
+    //1) Update the previous fileList and convert them according to current wo
+    
+    //rkspace
+    //Get current workspace directoroy
+
+    //QString ReplaceUpperDirOnly(QString& strOriginalPath, QString& strCurrDirPath, QString& strDelim);
+
+    int cntItem0 = m_strlistPath_RD_Read_Ref.count();
+    int cntItem1 = m_strlistPath_RD_Read_Comp.count();
+    int cntItem2 = m_strlistPath_Output_Gammamap.count();
+    int cntItem3 = m_strlistPath_Output_Report.count();
+
+    int cntItem4 = m_strlistPath_Output_Failure.count(); //not mandatory
+
+
+    if (cntItem0 < 1 ||
+        cntItem0 != cntItem1 ||
+        cntItem0 != cntItem2 ||
+        cntItem0 != cntItem3
+        )
+    {
+        QUTIL::ShowErrorMessage("Error! Number of read files are not matching each other!");
+        cout << "RD_ref= " << cntItem0 << endl;
+        cout << "RD_comp= " << cntItem1 << endl;
+        cout << "Gamma= " << cntItem2 << endl;
+        cout << "TxtReport= " << cntItem3 << endl;        
+        cout << "Failure= " << cntItem4 << endl;
+        bOK = false;
+    }
+
+    int cnt = cntItem0;
+
+    if (!bOK)
+    {
+        SetWorkDir(QString(""));//roll back
+        return;
+    }
+
+    QString strDelimRef = "DoseRef_";
+    QString strDelimComp = "DoseComp_";
+    QString strDelimGamma = "Analysis_";
+    QString strDelimText = "Analysis_";
+    QString strDelimFail = "Analysis_";
+
+    QString originalPath;
+    QString newPath;
+    //Update file Path
+    for (int i = 0; i < cnt; i++)
+    {
+        originalPath = m_strlistPath_RD_Read_Ref.at(i);
+        newPath = ReplaceUpperDirOnly(originalPath, strWorkDirPath, strDelimRef);
+        m_strlistPath_RD_Read_Ref.replace(i, newPath);
+
+        originalPath = m_strlistPath_RD_Read_Comp.at(i);
+        newPath = ReplaceUpperDirOnly(originalPath, strWorkDirPath, strDelimComp);
+        m_strlistPath_RD_Read_Comp.replace(i, newPath);
+
+        originalPath = m_strlistPath_Output_Gammamap.at(i);
+        newPath = ReplaceUpperDirOnly(originalPath, strWorkDirPath, strDelimGamma);
+        m_strlistPath_Output_Gammamap.replace(i, newPath);
+
+        originalPath = m_strlistPath_Output_Report.at(i);
+        newPath = ReplaceUpperDirOnly(originalPath, strWorkDirPath, strDelimText);
+        m_strlistPath_Output_Report.replace(i, newPath);
+
+        originalPath = m_strlistPath_Output_Failure.at(i);
+        newPath = ReplaceUpperDirOnly(originalPath, strWorkDirPath, strDelimFail);
+        m_strlistPath_Output_Failure.replace(i, newPath);
+    }
+
+    SLT_LoadResults();
+}
+
+QString gamma_gui::ReplaceUpperDirOnly(QString& strOriginalPath, QString& strCurrDirPath, QString& strDelim)
+{
+    QStringList strListPath = strOriginalPath.split("/");
+    //if (strListPath.count() < 1)
+      //  strListPath = strOriginalPath.split("/");
+    int cnt = strListPath.count();
+    
+    int idx = 0;
+
+    int cntSecondary = 0;
+    for (int i = 0; i < cnt; i++)
+    {
+        if (strListPath.at(i).contains(strDelim))
+        {
+            idx = i;
+            cntSecondary++;
+        }            
+    }
+
+    if (cntSecondary > 1)
+    {
+        cout << "ERROR! strDelim should be shown once" << endl;
+        return strOriginalPath;
+    }     
+
+    QString strLower;
+    for (int i = idx; i < cnt; i++)
+    {
+        strLower = strLower + "/" + strListPath.at(i);
+    }
+
+    QString newPath = strCurrDirPath + strLower;
+    return newPath;
+}
+
+void gamma_gui::SLT_SetWorkDir()
+{
+    //default: current one m_strPathDirWorkDir
+    QString dirPath = QFileDialog::getExistingDirectory(this, tr("Open Gamma Work Directory"),
+        m_strPathDirWorkDir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
+
+    QDir dirIMAGES = QDir(dirPath);
+    if (!dirIMAGES.exists())
+        return;
+
+    SetWorkDir(dirPath);
+}
+
+void gamma_gui::SetWorkDir(const QString& strPath)
+{
+    m_strPathDirWorkDir = strPath;
+    ui.lineEdit_WorkDir->setText(m_strPathDirWorkDir);
+}
+
+void gamma_gui::SLTM_ExportBatchReport()
+{
+    QString filePath = QFileDialog::getSaveFileName(this, "Save batch report file", m_strPathDirWorkDir, "report (*.txt)", 0, 0);
+
+    if (filePath.length() < 1)
+        return;
+
+
+    QFileInfo fInfo(filePath);
+
+    if (fInfo.suffix() != "txt" && fInfo.suffix() != "TXT")
+    {
+        cout << "Saving filter didn't work. Saving this file as txt." << endl;
+        filePath = filePath + ".txt";
+    }
+
+    SaveBatchGamma3DSimpleReport(filePath);
+}
+
+void gamma_gui::SLTM_LoadProtonDoseSetFile()
+{   
+    QStringList tmpList = QFileDialog::getOpenFileNames(this, "Select proton dose set file to open", m_strPathDirWorkDir, "Proton dose set file (*.set)");
+
+    int iFileCnt = tmpList.size();
+    if (iFileCnt < 1)
+        return;
+
+    vector<ProtonSetFileMGH> vProtonSetHeader;
+
+    for (int i = 0; i < iFileCnt; i++)
+    {
+        QString curTextSetFilePath = tmpList.at(i);
+        
+        ProtonSetFileMGH tmpProtonSetHeader;
+        //Read text and fill the class with as it is.
+        if (!ReadProtonDoseSet(curTextSetFilePath, tmpProtonSetHeader)) //Only reading text files to fill up the vectors, without any auditing
+        {
+            //do nothing            
+        }
+
+        cout << "FilePathRef= " << tmpProtonSetHeader.strPathRefDose.toLocal8Bit().constData() << endl;
+        cout << "FilePathComp= " << tmpProtonSetHeader.strPathCompDose.toLocal8Bit().constData() << endl;
+
+        QFileInfo fInfoRef(tmpProtonSetHeader.strPathRefDose);
+        QFileInfo fInfoComp(tmpProtonSetHeader.strPathCompDose);
+
+        if (fInfoRef.exists() && fInfoComp.exists()) //both files should exists.
+        {
+            vProtonSetHeader.push_back(tmpProtonSetHeader);
+        }        
+    }
+
+    if (vProtonSetHeader.empty())
+    {
+        cout << "Error. No file was found" << endl;
+        return;
+    }
+
+
+    //Ref clear
+    ui.plainTextEdit_RD_Ref->clear();
+    m_strlistPath_RD_Original_Ref.clear();
+    m_strlistFileBaseName_Ref.clear();
+
+    //Comp Clear
+    ui.plainTextEdit_RD_Comp->clear();    
+    m_strlistPath_RD_Original_Comp.clear();    
+    m_strlistFileBaseName_Comp.clear();
+
+    //output clear
+    m_strlistPath_Output_Gammamap.clear();    
+    m_strlistPath_Output_Failure.clear();
+    m_strlistPath_Output_Report.clear();
+    m_strlistBatchReport.clear();
+
+    //Ref dose clear;
+    m_vRefDose.clear();   
+
+    //Conversion
+    vector<ProtonSetFileMGH>::iterator it;
+    for (it = vProtonSetHeader.begin(); it != vProtonSetHeader.end(); ++it)
+    {
+        ProtonSetFileMGH tempDoseSet = (*it);
+
+        QString strPathMhaRef = ConvertMGHProtonDoseToMha(tempDoseSet.strPathRefDose, tempDoseSet.fDim, tempDoseSet.fOrigin, tempDoseSet.fSpacing);
+        QString strPathMhaComp = ConvertMGHProtonDoseToMha(tempDoseSet.strPathCompDose, tempDoseSet.fDim, tempDoseSet.fOrigin, tempDoseSet.fSpacing);
+
+        QFileInfo fInfoMhaRef(strPathMhaRef);
+        QFileInfo fInfoMhaComp(strPathMhaComp);
+
+        if (fInfoMhaRef.exists())
+        {            
+            m_strlistPath_RD_Original_Ref.push_back(strPathMhaRef);            
+            m_strlistFileBaseName_Ref.push_back(fInfoMhaRef.completeBaseName());
+            ui.plainTextEdit_RD_Ref->appendPlainText(strPathMhaRef); //just for display            
+        }        
+
+        if (fInfoMhaComp.exists())
+        {
+            m_strlistPath_RD_Original_Comp.push_back(strPathMhaComp);            
+            m_strlistFileBaseName_Comp.push_back(fInfoMhaComp.completeBaseName());
+            ui.plainTextEdit_RD_Comp->appendPlainText(strPathMhaComp); //just for display            
+        }
+    }          
+
+    if (m_strlistPath_RD_Original_Ref.size() > 0)
+    {
+        QFileInfo finfo(m_strlistPath_RD_Original_Ref.at(0));
+        QDir crntDir = finfo.absoluteDir();
+        m_strPathInputDir = crntDir.absolutePath();
+
+        SetWorkDir(m_strPathInputDir); //optional
+
+        //Change the plane to Frontal
+        ui.radioButtonFrontal->setChecked(true);
+    }     
+}
+
+bool gamma_gui::ReadProtonDoseSet(QString& strPathProtonDoseSet, ProtonSetFileMGH& protonSetInfo) //strPathProtonDoseSet text file
+{
+    //protonSetInfo    
+
+    /*protonSetInfo.fDim = { 1.0, 1.0, 1.0 };
+    protonSetInfo.fOrigin = { 0.0, 0.0, 0.0 };
+    protonSetInfo.fSpacing = { 1.0, 1.0, 1.0 };*/
+
+    protonSetInfo.fDim.x = 1.0;
+    protonSetInfo.fDim.y = 1.0;
+    protonSetInfo.fDim.z = 1.0;
+
+    protonSetInfo.fOrigin.x = 0.0;
+    protonSetInfo.fOrigin.y = 0.0;
+    protonSetInfo.fOrigin.z = 0.0;
+
+    protonSetInfo.fSpacing.x = 1.0;
+    protonSetInfo.fSpacing.y = 1.0;
+    protonSetInfo.fSpacing.z = 1.0;
+
+    protonSetInfo.strCTDir = "";
+    protonSetInfo.strPathRefDose = "";
+    protonSetInfo.strPathCompDose = "";
+
+
+    QFileInfo fInfoBase(strPathProtonDoseSet);
+
+    ifstream fin;
+    fin.open(strPathProtonDoseSet.toLocal8Bit().constData());
+
+    if (fin.fail())
+    {
+        cout << "Error occurred in file reading!" << endl;
+        return false;
+    } 
+
+    char str[MAX_LINE_LENGTH];
+
+    while (!fin.eof())
+    {
+        memset(str, 0, MAX_LINE_LENGTH);
+        fin.getline(str, MAX_LINE_LENGTH);
+
+        QString tmpStrLine = QString(str);
+        QStringList strListData = tmpStrLine.split(" \"");
+
+        QString strHeader, strData;
+        if (strListData.count() == 2)
+        {
+            strHeader = strListData.at(0);
+            strData = strListData.at(1);
+            strData = strData.remove("\"");
+        }        
+        strData = strData.trimmed(); //very important for Linux system!!!!!
+
+
+
+        if (strHeader.contains("calc-vol"))
+        {           
+
+            QStringList strlistVolInfo = strData.split(",");
+            if (strlistVolInfo.count() == 9) //should be
+            {
+                //First 3 digits: size in mm. X (Lat), Y(SI), Z(AntPost), IEC
+                VEC3D fLength, fOrigin, fDim, fSpacing;
+
+                fLength.x = strlistVolInfo.at(0).toDouble();
+                fLength.y = strlistVolInfo.at(1).toDouble();
+                fLength.z = strlistVolInfo.at(2).toDouble();
+
+                fOrigin.x = strlistVolInfo.at(3).toDouble();
+                fOrigin.y = strlistVolInfo.at(4).toDouble();
+                fOrigin.z = strlistVolInfo.at(5).toDouble();
+
+                fDim.x = strlistVolInfo.at(6).toDouble();
+                fDim.y = strlistVolInfo.at(7).toDouble();
+                fDim.z = strlistVolInfo.at(8).toDouble();
+
+                if (fDim.x != 0 && fDim.y != 0 && fDim.z != 0)
+                {
+                    fSpacing.x = fLength.x / fDim.x;
+                    fSpacing.y = fLength.y / fDim.y;
+                    fSpacing.z = fLength.z / fDim.z;
+                }
+                else
+                {
+                    fSpacing.x = 1.0;
+                    fSpacing.y = 1.0;
+                    fSpacing.z = 1.0;                    
+                }
+
+                protonSetInfo.fDim = fDim;
+                protonSetInfo.fOrigin = fOrigin;
+                protonSetInfo.fSpacing = fSpacing;
+            }
+        }
+        else if (strHeader.contains("ct-dir"))
+        {
+            protonSetInfo.strCTDir = strData;
+        }
+        else if (strHeader.trimmed() == "dose")
+        {            
+            strData = strData + ".MC";                        
+            protonSetInfo.strPathCompDose = fInfoBase.absolutePath() + "/" +strData;
+        }
+        else if (strHeader.trimmed() == "ref-dose")
+        {
+            strData = strData + ".orig";            
+            protonSetInfo.strPathRefDose = fInfoBase.absolutePath() + "/" + strData;
+        }
+    }
+    fin.close();
+
+    return true;
+}
+
+QString gamma_gui::ConvertMGHProtonDoseToMha(QString& strPathBinary, VEC3D& fDim, VEC3D& fOrigin, VEC3D& fSpacing)
+{
+    QString strResult = "";
+    ///Create a mha file
+
+    QFileInfo fInfo(strPathBinary);
+
+    if (!fInfo.exists())
+    {
+        cout << "Cannot find the binary file: " << strPathBinary.toLocal8Bit().constData() << endl;
+        return strResult;
+    }
+
+    QFile binFile(strPathBinary);
+    binFile.open(QIODevice::ReadOnly);
+    QDataStream in(&binFile);    
+    in.setByteOrder(QDataStream::BigEndian);
+
+    //qint32 voxData;
+    quint32 voxData;
+
+    //Coordinate conversion: IEC --> DICOM --> not possible!
+    //fDim = IEC
+    int width_DCM = (int)fDim.x; //
+    int height_DCM = (int)fDim.z; //
+    int length_DCM = (int)fDim.y; //
+
+    //Create a float itk image 3D
+    FloatImageType::Pointer spItkFloat = FloatImageType::New();
+
+    FloatImageType::SizeType size;
+    size[0] = width_DCM;
+    size[1] = height_DCM;
+    size[2] = length_DCM;
+
+    FloatImageType::IndexType idxStart;
+    idxStart[0] = 0;
+    idxStart[1] = 0;
+    idxStart[2] = 0;
+
+    FloatImageType::SpacingType spacing;    
+    spacing[0] = fSpacing.x;
+    spacing[1] = fSpacing.z;
+    spacing[2] = fSpacing.y;    
+
+    FloatImageType::PointType origin;
+    origin[0] = fOrigin.x;
+    origin[1] = -fOrigin.z;
+    origin[2] = fOrigin.y;
+
+    FloatImageType::RegionType region;
+    region.SetSize(size);
+    region.SetIndex(idxStart);
+
+    spItkFloat->SetRegions(region);
+    spItkFloat->SetSpacing(spacing);
+    spItkFloat->SetOrigin(origin);
+    spItkFloat->Allocate();
+
+    /*ofstream fout;
+    fout.open("D:/testDoseOut.txt");*/
+
+    //itk::ImageRegionIterator<FloatImageType> it(spItkFloat, spItkFloat->GetLargestPossibleRegion());
+    itk::ImageSliceIteratorWithIndex<FloatImageType> it(spItkFloat, spItkFloat->GetLargestPossibleRegion());
+    it.SetFirstDirection(0); //dicom x
+    it.SetSecondDirection(2); //dicom z. -2 doesn't work
+
+    it.GoToBegin();          
+    
+    //for (int k = 3; k < 4; k++)
+    float doseGyVal = 0.0;
+    for (int k = 0; k < height_DCM && !it.IsAtEnd(); k++)
+    {
+        for (int i = 0; i < length_DCM && !it.IsAtEndOfSlice(); i++) //coronal plane
+        {
+            for (int j = 0; j < width_DCM && !it.IsAtEndOfLine(); j++)
+            {
+                //converted mha file: Gy, float //current file: cGy, uint
+                in >> voxData;
+                doseGyVal = voxData / 100.0; //cGy to Gy
+                it.Set(doseGyVal);
+                ++it;                                            
+                //    fout << voxData << ",";
+            }
+            it.NextLine();
+          //  fout << endl;
+        }
+        it.NextSlice();
+        //fout << "Plane=" << k << endl;
+    }
+    binFile.close();
+
+    typedef itk::FlipImageFilter< FloatImageType >  FilterType;
+    FilterType::Pointer flipFilter = FilterType::New();
+    typedef FilterType::FlipAxesArrayType FlipAxesArrayType;
+
+    FlipAxesArrayType arrFlipAxes;
+    arrFlipAxes[0] = 0;
+    arrFlipAxes[1] = 0;
+    arrFlipAxes[2] = 1;//SI flip
+
+    flipFilter->SetFlipAxes(arrFlipAxes);
+    flipFilter->SetInput(spItkFloat); //plan CT, USHORT image
+    flipFilter->Update();
+
+    spItkFloat = flipFilter->GetOutput();
+    spItkFloat->SetOrigin(origin); //origin was reverted after the flipping
+    
+
+    QString strDirPath = fInfo.absolutePath();
+    QString strFileName = fInfo.fileName() + ".mha";
+    strResult = strDirPath + "/" + strFileName;
+
+    typedef itk::ImageFileWriter<FloatImageType> WriterType;
+    WriterType::Pointer writer = WriterType::New();
+
+    writer->SetFileName(strResult.toLocal8Bit().constData());
+    writer->SetUseCompression(true); //not exist in original code (rtkfdk)	
+    writer->SetInput(spItkFloat);
+    writer->Update();
+
+    //cout << "Writing image file was succeeded: " << strPath.toLocal8Bit().constData() << endl;
+    //fout.close();
+
+    QFileInfo fInfoOut(strResult);
+
+    if (!fInfoOut.exists())
+    {
+        strResult = "";
+        cout << "Error!File conversion failed" << endl;
+    }
+    else
+        cout << "Successfully converted to a mha file:" << strResult.toLocal8Bit().constData() <<endl;
+     
+    
+    
+    return strResult;
+}
diff --git a/src/plastimatch/standalone/gamma_gui.h b/src/plastimatch/standalone/gamma_gui.h
new file mode 100644
index 0000000..21d0306
--- /dev/null
+++ b/src/plastimatch/standalone/gamma_gui.h
@@ -0,0 +1,200 @@
+#ifndef GAMMA_GUI_H
+#define GAMMA_GUI_H
+
+#include <QtGui/QMainWindow>
+#include "ui_gamma_gui.h"
+#include <QStringList>
+#include <vector>
+#include "yk_config.h"
+
+//#include "itkImageFileReader.h"
+//#include "itkImageFileWriter.h"
+#include "itk_image_type.h"
+
+//#include "YK16GrayImage.h"
+//#include <fstream>
+
+using namespace std;
+
+class Gamma_parms;
+
+class DlgGammaView;
+class YK16GrayImage;
+class qyklabel;
+class QStandardItemModel;
+
+
+class gamma_gui : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    gamma_gui(QWidget *parent = 0, Qt::WFlags flags = 0);
+    ~gamma_gui();     
+    
+    QString GammaMain(Gamma_parms* parms, float& refDoseGy, const QString& strPathBkupRef = QString(""), const QString& strPathBkupComp = QString(""));
+
+    void Load_FilesToMem();//all ref, comp, gamma map should be prepared    
+    void UpdateProbePos(qyklabel* qlabel);    
+
+    void UpdateTable(vector<QPointF>& vData1, vector<QPointF>& vData2, vector<QPointF>& vData3,
+    float fNorm1, float fNorm2, float fNorm3, float fMag1, float fMag2, float fMag3);    
+
+    void WhenMousePressedRight(qyklabel* pWnd);
+    void UpdatePanCommon(qyklabel* qWnd);
+
+    void RenameFileByDCMInfo(QStringList& filenameList);
+
+    void SaveCurrentGammaWorkSpace(QString& strPathGammaWorkSpace);
+
+    bool LoadGammaWorkSpace(QString& strPathGammaWorkSpace);
+    bool ReadProtonDoseSet(QString& strPathProtonDoseSet, ProtonSetFileMGH& protonSetInfo);
+
+    void SaveBatchGamma3DSimpleReport(QString& strFilePath);
+
+    void SetWorkDir(const QString& strPath);
+
+    QString ReplaceUpperDirOnly(QString& strOriginalPath, QString& strCurrDirPath, QString& strDelim);
+
+    QString ConvertMGHProtonDoseToMha(QString& strPathBinnary, VEC3D& fDim, VEC3D& fOrigin, VEC3D& fSpacing);
+
+    public slots:        
+        void SLT_Load_RD_Ref();
+        void SLT_Load_RD_Comp();        
+        void SLT_LoadResults();
+
+        void SLT_RunBatchGamma();
+        void SLT_ProfileView();
+
+        void SLT_DrawDoseImages();// refer to probe positions, selected 3D file (spPointer), plane direction
+        void SLT_DrawGammaMap3D();
+        void SLT_DrawGammaMap2D();
+        void SLT_UpdateComboContents();        
+
+        void SLT_WhenSelectCombo();
+        void SLT_DrawAll();
+        void SLT_DrawTable();
+        void SLT_DrawChart();
+
+        void SLT_WhenChangePlane();//restore zoom and pan then draw all
+
+        void SLT_UpdateReportTxt();
+
+        void SLT_UpdateProbePosRef();
+        void SLT_UpdateProbePosComp();
+        void SLT_UpdateProbePosGamma2D();
+        void SLT_UpdateProbePosGamma3D();
+        void SLT_CopyTableToClipboard();
+        //void SLT_DrawGraph(); 
+        void SLT_DrawGraph(bool bInitMinMax = false);
+        void SLT_RunGamma2D();
+
+        void SLT_GoCenterPosRef();
+        void SLT_GoCenterPosComp();
+        void SLT_NormCompFromRefNorm();        
+
+        void SaveDoseIBAGenericTXTFromItk(QString strFilePath, FloatImage2DType::Pointer& spFloatDose);
+
+        void SLT_MouseWheelUpdateRef();
+        void SLT_MouseWheelUpdateComp();
+        void SLT_MouseWheelUpdateGamma2D();
+        void SLT_MouseWheelUpdateGamma3D();
+
+        void SLT_RestoreZoomPan();
+
+        void SLT_MouseMoveUpdateRef();
+        void SLT_MouseMoveUpdateComp();
+        void SLT_MouseMoveUpdateGamma2D();
+        void SLT_MouseMoveUpdateGamma3D();
+
+        void SLT_MousePressedRightRef();
+        void SLT_MousePressedRightComp();
+        void SLT_MousePressedRightGamma3D();
+        void SLT_MousePressedRightGamma2D();     
+
+        void SLT_MouseReleasedRightRef();
+        void SLT_MouseReleasedRightComp();
+        void SLT_MouseReleasedRightGamma3D();
+        void SLT_MouseReleasedRightGamma2D();
+
+        void SLT_UpdatePanSettingRef();
+        void SLT_UpdatePanSettingComp();
+        void SLT_UpdatePanSettingGamma3D();
+        void SLT_UpdatePanSettingGamma2D();
+
+        void SLTM_RenameRDFilesByDICOMInfo();
+
+        void SLTM_LoadSavedWorkSpace();
+        void SLTM_SaveBatchModeSimpleReport();
+
+        void SLT_SetWorkDir();
+
+        void SLTM_ExportBatchReport();
+
+        void SLTM_LoadProtonDoseSetFile();       
+
+public:    
+    QStringList m_strlistPath_RD_Original_Ref; //RD files, before the conversion
+    QStringList m_strlistPath_RD_Original_Comp;
+
+    QStringList m_strlistPath_RD_Read_Ref; //mha files, after the conversion
+    QStringList m_strlistPath_RD_Read_Comp;
+
+    QStringList m_strlistFileBaseName_Ref;
+    QStringList m_strlistFileBaseName_Comp;
+    
+    QStringList m_strlistBatchReport;
+    
+    QStringList m_strlistPath_Output_Gammamap;
+    QStringList m_strlistPath_Output_Failure;
+    QStringList m_strlistPath_Output_Report;
+
+    vector<float> m_vRefDose;
+
+    //DlgGammaView* m_pView;    
+
+    vector<FloatImageType::Pointer> m_vRefDoseImages;
+    vector<FloatImageType::Pointer> m_vCompDoseImages;
+    vector<FloatImageType::Pointer> m_vGammaMapImages;
+
+    //checkBox_low_mem
+    FloatImageType::Pointer m_spDummyLowMemImg;
+
+    FloatImage2DType::Pointer m_spCurRef2D;
+    FloatImage2DType::Pointer m_spCurComp2D;
+    FloatImage2DType::Pointer m_spGamma2DResult; //Read from output of 2D gamma
+
+    /*FloatImageType::Pointer m_spRefDoseImages;
+    FloatImageType::Pointer m_spCompDoseImages;
+    FloatImageType::Pointer m_spGammaMapImages;*/
+
+    YK16GrayImage* m_pCurImageRef;
+    YK16GrayImage* m_pCurImageComp;
+    YK16GrayImage* m_pCurImageGamma3D;
+    YK16GrayImage* m_pCurImageGamma2D;
+    QStandardItemModel *m_pTableModel;
+
+    vector<VEC3D> m_vColormapDose;
+    vector<VEC3D> m_vColormapGamma;
+
+    bool m_bGamma2DIsDone;
+
+    bool m_bMousePressedRightRef;
+    bool m_bMousePressedRightComp;
+    bool m_bMousePressedRightGamma3D;
+    bool m_bMousePressedRightGamma2D;
+
+    QPoint m_ptPanStart;
+    QPoint m_ptOriginalDataOffset;
+
+    QString m_strPathDirWorkDir;//this is for output
+
+    QString m_strPathInputDir;//this is for input DCM. initialized when Load Ref files or Load Comp files
+
+    int m_iLastLoadedIndex;
+
+private:
+    Ui::gamma_guiClass ui;
+};
+
+#endif // gamma_gui_H
diff --git a/src/plastimatch/standalone/gamma_gui.ui b/src/plastimatch/standalone/gamma_gui.ui
new file mode 100644
index 0000000..a6b93dc
--- /dev/null
+++ b/src/plastimatch/standalone/gamma_gui.ui
@@ -0,0 +1,1628 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>gamma_guiClass</class>
+ <widget class="QMainWindow" name="gamma_guiClass">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1225</width>
+    <height>857</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Plastimatch EasyGamma3D (bug report to: ykpark at mgh.harvard.edu)</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <widget class="QPushButton" name="pushButton_Open_RD_Ref">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>10</y>
+      <width>141</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Load Reference</string>
+    </property>
+   </widget>
+   <widget class="QPlainTextEdit" name="plainTextEdit_RD_Ref">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>50</y>
+      <width>311</width>
+      <height>151</height>
+     </rect>
+    </property>
+    <property name="readOnly">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QPlainTextEdit" name="plainTextEdit_RD_Comp">
+    <property name="geometry">
+     <rect>
+      <x>330</x>
+      <y>50</y>
+      <width>311</width>
+      <height>151</height>
+     </rect>
+    </property>
+    <property name="readOnly">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QGroupBox" name="groupBox">
+    <property name="geometry">
+     <rect>
+      <x>670</x>
+      <y>20</y>
+      <width>501</width>
+      <height>141</height>
+     </rect>
+    </property>
+    <property name="title">
+     <string>Gamma parameters</string>
+    </property>
+    <widget class="QLineEdit" name="lineEdit_inhereResample">
+     <property name="geometry">
+      <rect>
+       <x>150</x>
+       <y>30</y>
+       <width>31</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>1</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_6">
+     <property name="geometry">
+      <rect>
+       <x>190</x>
+       <y>30</y>
+       <width>31</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>mm</string>
+     </property>
+    </widget>
+    <widget class="QCheckBox" name="checkBox_inhereResample">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>30</y>
+       <width>141</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Inherent resample</string>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+    </widget>
+    <widget class="QCheckBox" name="checkBox_Interp_search">
+     <property name="geometry">
+      <rect>
+       <x>230</x>
+       <y>30</y>
+       <width>131</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Interp-search</string>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="radioButton_localGamma">
+     <property name="geometry">
+      <rect>
+       <x>370</x>
+       <y>30</y>
+       <width>111</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Local gamma</string>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="radioButton_globalGamma">
+     <property name="geometry">
+      <rect>
+       <x>370</x>
+       <y>50</y>
+       <width>111</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Global gamma</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+    <widget class="QLineEdit" name="lineEdit_refDoseInGy">
+     <property name="geometry">
+      <rect>
+       <x>90</x>
+       <y>60</y>
+       <width>41</width>
+       <height>21</height>
+      </rect>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_7">
+     <property name="geometry">
+      <rect>
+       <x>140</x>
+       <y>60</y>
+       <width>121</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>(blank = use dmax)</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_8">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>60</y>
+       <width>81</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Ref. dose[Gy]</string>
+     </property>
+    </widget>
+    <widget class="QLineEdit" name="lineEdit_dta_tol">
+     <property name="geometry">
+      <rect>
+       <x>70</x>
+       <y>90</y>
+       <width>41</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>3.0</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_9">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>90</y>
+       <width>61</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>DTA [mm]</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_10">
+     <property name="geometry">
+      <rect>
+       <x>120</x>
+       <y>90</y>
+       <width>81</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Dose diff [%]</string>
+     </property>
+    </widget>
+    <widget class="QLineEdit" name="lineEdit_dose_tol">
+     <property name="geometry">
+      <rect>
+       <x>200</x>
+       <y>90</y>
+       <width>41</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>3.0</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_11">
+     <property name="geometry">
+      <rect>
+       <x>250</x>
+       <y>90</y>
+       <width>91</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Cut-off dose[%]</string>
+     </property>
+    </widget>
+    <widget class="QLineEdit" name="lineEdit_cutoff_dose">
+     <property name="geometry">
+      <rect>
+       <x>340</x>
+       <y>90</y>
+       <width>41</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>10.0</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="pushButton_RunBatchGamma">
+     <property name="geometry">
+      <rect>
+       <x>390</x>
+       <y>80</y>
+       <width>111</width>
+       <height>31</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Run 3D Gamma</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QPushButton" name="pushButton_Open_RD_Comp">
+    <property name="geometry">
+     <rect>
+      <x>330</x>
+      <y>10</y>
+      <width>141</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Load Compare</string>
+    </property>
+   </widget>
+   <widget class="QGroupBox" name="groupBox_2">
+    <property name="geometry">
+     <rect>
+      <x>670</x>
+      <y>180</y>
+      <width>501</width>
+      <height>81</height>
+     </rect>
+    </property>
+    <property name="title">
+     <string>Analysis 3D</string>
+    </property>
+    <widget class="QLineEdit" name="lineEdit_ProbePosX">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>40</y>
+       <width>41</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>0</string>
+     </property>
+    </widget>
+    <widget class="QLineEdit" name="lineEdit_ProbePosY">
+     <property name="geometry">
+      <rect>
+       <x>70</x>
+       <y>40</y>
+       <width>41</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>0</string>
+     </property>
+    </widget>
+    <widget class="QLineEdit" name="lineEdit_ProbePosZ">
+     <property name="geometry">
+      <rect>
+       <x>130</x>
+       <y>40</y>
+       <width>41</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>0</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_12">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>20</y>
+       <width>51</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>DCM_X</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_13">
+     <property name="geometry">
+      <rect>
+       <x>70</x>
+       <y>20</y>
+       <width>51</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>DCM_Y</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_14">
+     <property name="geometry">
+      <rect>
+       <x>130</x>
+       <y>20</y>
+       <width>51</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>DCM_Z</string>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="radioButtonAxial">
+     <property name="geometry">
+      <rect>
+       <x>290</x>
+       <y>50</y>
+       <width>61</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="font">
+      <font>
+       <pointsize>8</pointsize>
+      </font>
+     </property>
+     <property name="text">
+      <string>Axial</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="radioButtonSagittal">
+     <property name="geometry">
+      <rect>
+       <x>360</x>
+       <y>50</y>
+       <width>71</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="font">
+      <font>
+       <pointsize>8</pointsize>
+      </font>
+     </property>
+     <property name="text">
+      <string>Sagittal</string>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="radioButtonFrontal">
+     <property name="geometry">
+      <rect>
+       <x>430</x>
+       <y>50</y>
+       <width>71</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Frontal</string>
+     </property>
+    </widget>
+    <widget class="QComboBox" name="comboBoxCompareFile">
+     <property name="geometry">
+      <rect>
+       <x>310</x>
+       <y>20</y>
+       <width>181</width>
+       <height>22</height>
+      </rect>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_15">
+     <property name="geometry">
+      <rect>
+       <x>270</x>
+       <y>20</y>
+       <width>41</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>File</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="pushButtonGoForcedProbe">
+     <property name="geometry">
+      <rect>
+       <x>180</x>
+       <y>30</y>
+       <width>71</width>
+       <height>31</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Go position</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="qyklabel" name="labelCompDose">
+    <property name="geometry">
+     <rect>
+      <x>270</x>
+      <y>270</y>
+      <width>256</width>
+      <height>256</height>
+     </rect>
+    </property>
+    <property name="frameShape">
+     <enum>QFrame::Box</enum>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="qyklabel" name="labelGammaMap3D">
+    <property name="geometry">
+     <rect>
+      <x>270</x>
+      <y>530</y>
+      <width>256</width>
+      <height>256</height>
+     </rect>
+    </property>
+    <property name="frameShape">
+     <enum>QFrame::Box</enum>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_16">
+    <property name="geometry">
+     <rect>
+      <x>90</x>
+      <y>250</y>
+      <width>101</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Ref. dose</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_17">
+    <property name="geometry">
+     <rect>
+      <x>350</x>
+      <y>250</y>
+      <width>121</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Compared dose</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_18">
+    <property name="geometry">
+     <rect>
+      <x>350</x>
+      <y>790</y>
+      <width>121</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>3D Gamma map</string>
+    </property>
+   </widget>
+   <widget class="qyklabel" name="labelGammaMap2D">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>530</y>
+      <width>256</width>
+      <height>256</height>
+     </rect>
+    </property>
+    <property name="frameShape">
+     <enum>QFrame::Box</enum>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_19">
+    <property name="geometry">
+     <rect>
+      <x>100</x>
+      <y>790</y>
+      <width>111</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>2D Gamma map</string>
+    </property>
+   </widget>
+   <widget class="QSlider" name="sliderNormRef">
+    <property name="geometry">
+     <rect>
+      <x>540</x>
+      <y>300</y>
+      <width>31</width>
+      <height>191</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>10000</number>
+    </property>
+    <property name="singleStep">
+     <number>10</number>
+    </property>
+    <property name="pageStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>200</number>
+    </property>
+    <property name="orientation">
+     <enum>Qt::Vertical</enum>
+    </property>
+   </widget>
+   <widget class="QSlider" name="sliderNormComp">
+    <property name="geometry">
+     <rect>
+      <x>600</x>
+      <y>300</y>
+      <width>31</width>
+      <height>191</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>10000</number>
+    </property>
+    <property name="singleStep">
+     <number>10</number>
+    </property>
+    <property name="pageStep">
+     <number>100</number>
+    </property>
+    <property name="value">
+     <number>200</number>
+    </property>
+    <property name="sliderPosition">
+     <number>200</number>
+    </property>
+    <property name="orientation">
+     <enum>Qt::Vertical</enum>
+    </property>
+   </widget>
+   <widget class="QCustomPlot" name="customPlotProfile" native="true">
+    <property name="geometry">
+     <rect>
+      <x>590</x>
+      <y>540</y>
+      <width>381</width>
+      <height>231</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditYMax">
+    <property name="geometry">
+     <rect>
+      <x>540</x>
+      <y>540</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>130</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditYMin">
+    <property name="geometry">
+     <rect>
+      <x>540</x>
+      <y>750</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>0</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditXMin">
+    <property name="geometry">
+     <rect>
+      <x>600</x>
+      <y>780</y>
+      <width>51</width>
+      <height>20</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditXMax">
+    <property name="geometry">
+     <rect>
+      <x>920</x>
+      <y>780</y>
+      <width>51</width>
+      <height>20</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QTableView" name="tableViewProfile">
+    <property name="geometry">
+     <rect>
+      <x>980</x>
+      <y>520</y>
+      <width>201</width>
+      <height>281</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonCopyTableToClipboard">
+    <property name="geometry">
+     <rect>
+      <x>980</x>
+      <y>480</y>
+      <width>201</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>CopyToClipboard</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_20">
+    <property name="geometry">
+     <rect>
+      <x>530</x>
+      <y>250</y>
+      <width>131</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Norm. dose (cGy)</string>
+    </property>
+   </widget>
+   <widget class="qyklabel" name="labelReferDose">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>270</y>
+      <width>256</width>
+      <height>256</height>
+     </rect>
+    </property>
+    <property name="frameShape">
+     <enum>QFrame::Box</enum>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QSpinBox" name="spinBoxNormRef">
+    <property name="geometry">
+     <rect>
+      <x>530</x>
+      <y>270</y>
+      <width>51</width>
+      <height>22</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>10000</number>
+    </property>
+    <property name="value">
+     <number>200</number>
+    </property>
+   </widget>
+   <widget class="QSpinBox" name="spinBoxNormComp">
+    <property name="geometry">
+     <rect>
+      <x>590</x>
+      <y>270</y>
+      <width>51</width>
+      <height>22</height>
+     </rect>
+    </property>
+    <property name="maximum">
+     <number>10000</number>
+    </property>
+    <property name="value">
+     <number>200</number>
+    </property>
+   </widget>
+   <widget class="QGroupBox" name="radioButtonVer">
+    <property name="geometry">
+     <rect>
+      <x>750</x>
+      <y>480</y>
+      <width>221</width>
+      <height>51</height>
+     </rect>
+    </property>
+    <property name="title">
+     <string>Profile direction</string>
+    </property>
+    <widget class="QRadioButton" name="radioButtonHor">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>20</y>
+       <width>111</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Horizontal</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="radioButtonVert">
+     <property name="geometry">
+      <rect>
+       <x>140</x>
+       <y>20</y>
+       <width>71</width>
+       <height>17</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>Vertical</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxAutoAdjust">
+    <property name="enabled">
+     <bool>true</bool>
+    </property>
+    <property name="geometry">
+     <rect>
+      <x>680</x>
+      <y>780</y>
+      <width>101</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Auto Adjust</string>
+    </property>
+    <property name="checked">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButton_Run2DGamma">
+    <property name="geometry">
+     <rect>
+      <x>900</x>
+      <y>260</y>
+      <width>111</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Run 2D Gamma</string>
+    </property>
+   </widget>
+   <widget class="QPlainTextEdit" name="plainTextEditGammaResult">
+    <property name="geometry">
+     <rect>
+      <x>650</x>
+      <y>300</y>
+      <width>271</width>
+      <height>171</height>
+     </rect>
+    </property>
+    <property name="readOnly">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_21">
+    <property name="geometry">
+     <rect>
+      <x>730</x>
+      <y>270</y>
+      <width>121</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>3D Gamma report</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_22">
+    <property name="geometry">
+     <rect>
+      <x>1030</x>
+      <y>270</y>
+      <width>121</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>2D Gamma report</string>
+    </property>
+   </widget>
+   <widget class="QPlainTextEdit" name="plainTextEditGammaResult2D">
+    <property name="geometry">
+     <rect>
+      <x>920</x>
+      <y>300</y>
+      <width>271</width>
+      <height>171</height>
+     </rect>
+    </property>
+    <property name="readOnly">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBoxNormalized">
+    <property name="geometry">
+     <rect>
+      <x>800</x>
+      <y>780</y>
+      <width>101</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Normalized</string>
+    </property>
+    <property name="checked">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEditNormComp">
+    <property name="geometry">
+     <rect>
+      <x>580</x>
+      <y>500</y>
+      <width>41</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>1.00</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_23">
+    <property name="geometry">
+     <rect>
+      <x>530</x>
+      <y>500</y>
+      <width>51</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>NormF</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonNormApply">
+    <property name="geometry">
+     <rect>
+      <x>630</x>
+      <y>501</y>
+      <width>61</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>apply</string>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBox_ScrollZoom">
+    <property name="geometry">
+     <rect>
+      <x>230</x>
+      <y>240</y>
+      <width>101</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>scroll_zoom</string>
+    </property>
+    <property name="checked">
+     <bool>false</bool>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_24">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>210</y>
+      <width>101</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Save directory</string>
+    </property>
+   </widget>
+   <widget class="QLineEdit" name="lineEdit_WorkDir">
+    <property name="geometry">
+     <rect>
+      <x>180</x>
+      <y>210</y>
+      <width>461</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="pushButtonSetWorkDir">
+    <property name="geometry">
+     <rect>
+      <x>130</x>
+      <y>210</y>
+      <width>41</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>set</string>
+    </property>
+   </widget>
+   <widget class="QCheckBox" name="checkBox_low_mem">
+    <property name="geometry">
+     <rect>
+      <x>500</x>
+      <y>20</y>
+      <width>151</width>
+      <height>17</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>low memory option</string>
+    </property>
+    <property name="checked">
+     <bool>false</bool>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QMenuBar" name="menuBar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1225</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionLoad_saved_results"/>
+    <addaction name="actionLoad_proton_dose_file_set"/>
+    <addaction name="actionExport_simple_batch_report"/>
+   </widget>
+   <widget class="QMenu" name="menuView">
+    <property name="title">
+     <string>View</string>
+    </property>
+   </widget>
+   <widget class="QMenu" name="menuTool">
+    <property name="title">
+     <string>Tool</string>
+    </property>
+    <addaction name="actionRename_RD_files"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuView"/>
+   <addaction name="menuTool"/>
+  </widget>
+  <widget class="QToolBar" name="mainToolBar">
+   <attribute name="toolBarArea">
+    <enum>TopToolBarArea</enum>
+   </attribute>
+   <attribute name="toolBarBreak">
+    <bool>false</bool>
+   </attribute>
+  </widget>
+  <widget class="QStatusBar" name="statusBar"/>
+  <action name="actionProfile_View">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Profile View</string>
+   </property>
+  </action>
+  <action name="actionRename_RD_files">
+   <property name="text">
+    <string>Rename RD files</string>
+   </property>
+  </action>
+  <action name="actionLoad_saved_results">
+   <property name="text">
+    <string>Import workspace file</string>
+   </property>
+  </action>
+  <action name="actionExport_simple_batch_report">
+   <property name="text">
+    <string>Export simple batch report</string>
+   </property>
+  </action>
+  <action name="actionLoad_proton_dose_file_set">
+   <property name="text">
+    <string>Import proton dose file (*.set)</string>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+  <customwidget>
+   <class>QCustomPlot</class>
+   <extends>QWidget</extends>
+   <header>qcustomplot.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>qyklabel</class>
+   <extends>QLabel</extends>
+   <header>qyklabel.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>pushButton_Open_RD_Comp</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_Load_RD_Comp()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>385</x>
+     <y>73</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>544</x>
+     <y>85</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_Open_RD_Ref</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_Load_RD_Ref()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>84</x>
+     <y>73</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>155</x>
+     <y>93</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_RunBatchGamma</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_RunBatchGamma()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>1060</x>
+     <y>163</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>121</x>
+     <y>418</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionProfile_View</sender>
+   <signal>triggered()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_ProfileView()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>597</x>
+     <y>428</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderNormRef</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>spinBoxNormRef</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>557</x>
+     <y>352</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>574</x>
+     <y>313</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>spinBoxNormRef</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>sliderNormRef</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>543</x>
+     <y>311</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>543</x>
+     <y>369</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>spinBoxNormComp</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>sliderNormComp</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>634</x>
+     <y>318</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>626</x>
+     <y>348</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderNormComp</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>spinBoxNormComp</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>613</x>
+     <y>372</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>602</x>
+     <y>308</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderNormRef</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawAll()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>555</x>
+     <y>508</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>563</x>
+     <y>553</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sliderNormComp</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawAll()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>626</x>
+     <y>501</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>655</x>
+     <y>530</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonGoForcedProbe</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawAll()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>876</x>
+     <y>273</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>690</x>
+     <y>408</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>radioButtonAxial</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_WhenChangePlane()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>991</x>
+     <y>279</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>913</x>
+     <y>567</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>radioButtonSagittal</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_WhenChangePlane()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>1072</x>
+     <y>279</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1075</x>
+     <y>508</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>radioButtonFrontal</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_WhenChangePlane()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>1146</x>
+     <y>279</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1075</x>
+     <y>404</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonCopyTableToClipboard</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_CopyTableToClipboard()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>1020</x>
+     <y>519</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>960</x>
+     <y>563</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>checkBoxAutoAdjust</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawGraph(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>735</x>
+     <y>829</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>875</x>
+     <y>823</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>lineEditYMax</sender>
+   <signal>textEdited(QString)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawGraph()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>552</x>
+     <y>582</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>601</x>
+     <y>618</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>lineEditYMin</sender>
+   <signal>textEdited(QString)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawGraph()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>568</x>
+     <y>802</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>618</x>
+     <y>766</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>lineEditXMin</sender>
+   <signal>textEdited(QString)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawGraph()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>637</x>
+     <y>832</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>725</x>
+     <y>823</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>lineEditXMax</sender>
+   <signal>textEdited(QString)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawGraph()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>964</x>
+     <y>827</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>977</x>
+     <y>812</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>radioButtonHor</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawAll()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>818</x>
+     <y>549</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>934</x>
+     <y>520</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>radioButtonVert</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawAll()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>908</x>
+     <y>549</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>946</x>
+     <y>536</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>comboBoxCompareFile</sender>
+   <signal>currentIndexChanged(int)</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_WhenSelectCombo()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>1152</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1075</x>
+     <y>331</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_Run2DGamma</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_RunGamma2D()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>990</x>
+     <y>311</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>911</x>
+     <y>303</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>checkBoxNormalized</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_DrawAll()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>837</x>
+     <y>820</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>906</x>
+     <y>832</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonNormApply</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_NormCompFromRefNorm()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>669</x>
+     <y>543</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>692</x>
+     <y>548</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionRename_RD_files</sender>
+   <signal>triggered()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLTM_RenameRDFilesByDICOMInfo()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>612</x>
+     <y>428</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionLoad_saved_results</sender>
+   <signal>triggered()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLTM_LoadSavedWorkSpace()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>612</x>
+     <y>428</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonSetWorkDir</sender>
+   <signal>released()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLT_SetWorkDir()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>142</x>
+     <y>253</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>161</x>
+     <y>275</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionExport_simple_batch_report</sender>
+   <signal>triggered()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLTM_ExportBatchReport()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>612</x>
+     <y>428</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionLoad_proton_dose_file_set</sender>
+   <signal>triggered()</signal>
+   <receiver>gamma_guiClass</receiver>
+   <slot>SLTM_LoadProtonDoseSetFile()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>612</x>
+     <y>428</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+ <slots>
+  <slot>SLT_Load_RD_Ref()</slot>
+  <slot>SLT_Load_RD_Comp()</slot>
+  <slot>SLT_RunBatchGamma()</slot>
+  <slot>SLT_ProfileView()</slot>
+  <slot>SLT_LoadResults()</slot>
+  <slot>SLT_DrawAll()</slot>
+  <slot>SLT_UpdateReportTxt()</slot>
+  <slot>SLT_CopyTableToClipboard()</slot>
+  <slot>SLT_DrawGraph(bool)</slot>
+  <slot>SLT_DrawGraph()</slot>
+  <slot>SLT_RunGamma2D()</slot>
+  <slot>SLT_NormCompFromRefNorm()</slot>
+  <slot>SLT_WhenSelectCombo()</slot>
+  <slot>SLT_WhenChangePlane()</slot>
+  <slot>SLTM_RenameRDFilesByDICOMInfo()</slot>
+  <slot>SLTM_LoadSavedWorkSpace()</slot>
+  <slot>SLT_SetWorkDir()</slot>
+  <slot>SLTM_ExportBatchReport()</slot>
+  <slot>SLTM_LoadProtonDoseSetFile()</slot>
+ </slots>
+</ui>
diff --git a/src/plastimatch/standalone/nki2mha_converter_main.cpp b/src/plastimatch/standalone/gamma_gui_main.cpp
similarity index 66%
copy from src/plastimatch/standalone/nki2mha_converter_main.cpp
copy to src/plastimatch/standalone/gamma_gui_main.cpp
index 2e20b2c..ff4954d 100644
--- a/src/plastimatch/standalone/nki2mha_converter_main.cpp
+++ b/src/plastimatch/standalone/gamma_gui_main.cpp
@@ -1,10 +1,10 @@
-#include "nki2mha_converter.h"
-#include <QtGui/QApplication>
-
-int main(int argc, char *argv[])
-{
-	QApplication a(argc, argv);
-	nki2mha_converter w;
-	w.show();
-	return a.exec();
-}
+#include "gamma_gui.h"
+#include <QtGui/QApplication>
+
+int main(int argc, char *argv[])
+{
+	QApplication a(argc, argv);
+        gamma_gui w;
+	w.show();
+	return a.exec();
+}
diff --git a/src/plastimatch/standalone/hnd_to_pfm.cxx b/src/plastimatch/standalone/hnd_to_pfm.cxx
index 0de43d6..b14012c 100755
--- a/src/plastimatch/standalone/hnd_to_pfm.cxx
+++ b/src/plastimatch/standalone/hnd_to_pfm.cxx
@@ -40,7 +40,7 @@ main (int argc, char* argv[])
     }
 
     /* Write image and header */
-    proj_image_save (proj, pfm_fn, mat_fn);
+    proj->save (pfm_fn, mat_fn);
 
     delete proj;
     return 0;
diff --git a/src/plastimatch/standalone/nki2mha_converter.cpp b/src/plastimatch/standalone/nki2mha_converter.cpp
index 022f006..8bf7fb1 100644
--- a/src/plastimatch/standalone/nki2mha_converter.cpp
+++ b/src/plastimatch/standalone/nki2mha_converter.cpp
@@ -1,4 +1,4 @@
-#include "nki2mha_converter.h"
+#include "nki2mha_converter.h"
 #include <QString>
 #include <QFileDialog>
 #include <QListView>
@@ -90,7 +90,7 @@ QString nki2mha_converter::CorrectSingle_NKI2MHA(const char* filePath)
     QString extName = "mha";
 
     QString newFileName = baseName.append(endFix).append(".").append(extName);
-    QString newPath = dir.absolutePath() + "\\" + newFileName;	
+    QString newPath = dir.absolutePath() + "/" + newFileName;
 
     write_mha(newPath.toLocal8Bit().constData(), v);	
 
@@ -249,7 +249,7 @@ QString nki2mha_converter::CorrectSingle_NKI2DCM( const char* filePath )
     QString baseName = srcFileInfo.completeBaseName();	
 
     baseName.append(endFix);	
-    QString newDirPath = dir.absolutePath() + "\\" + baseName;	
+    QString newDirPath = dir.absolutePath() + "/" + baseName;	
 
     QDir dirNew(newDirPath);
     if (!dirNew.exists()){
@@ -283,11 +283,11 @@ QString nki2mha_converter::CorrectSingle_NKI2DCM( const char* filePath, QString
   QString baseName = srcFileInfo.completeBaseName();	
 
   //baseName.append(endFix);	
-  //QString newDirPath = dir.absolutePath() + "\\" + baseName;	
+  
 
 
   //baseName.append(endFix);	
-  //QString newDirPath = dir.absolutePath() + "\\" + baseName;
+  
   QString dirName;
 
   if (patientID.length() > 1)
@@ -295,7 +295,7 @@ QString nki2mha_converter::CorrectSingle_NKI2DCM( const char* filePath, QString
   else
 	dirName = baseName;    
 
-  QString newDirPath = dir.absolutePath() + "\\" + dirName;
+  QString newDirPath = dir.absolutePath() + "/" + dirName;
 
   QDir dirNew(newDirPath);
   if (!dirNew.exists()){
@@ -333,7 +333,7 @@ QString nki2mha_converter::CorrectSingle_MHA2DCM( const char* filePath)
     QString baseName = srcFileInfo.completeBaseName();	
 
     baseName.append(endFix);	
-    QString newDirPath = dir.absolutePath() + "\\" + baseName;	
+    QString newDirPath = dir.absolutePath() + "/" + baseName;	
 
     QDir dirNew(newDirPath);
     if (!dirNew.exists()){
@@ -371,7 +371,7 @@ QString nki2mha_converter::CorrectSingle_MHA2DCM( const char* filePath, QString
   QString baseName = srcFileInfo.completeBaseName();	
 
   //baseName.append(endFix);	
-  //QString newDirPath = dir.absolutePath() + "\\" + baseName;
+  //QString newDirPath = dir.absolutePath() + "/" + baseName;
   QString dirName;
 
   if (patientID.length() > 1)
@@ -379,7 +379,7 @@ QString nki2mha_converter::CorrectSingle_MHA2DCM( const char* filePath, QString
   else
 	dirName = baseName;    
   
-  QString newDirPath = dir.absolutePath() + "\\" + dirName;
+  QString newDirPath = dir.absolutePath() + "/" + dirName;
 
 
   QDir dirNew(newDirPath);
@@ -417,7 +417,7 @@ QString nki2mha_converter::CorrectSingle_NKI2RAW( const char* filePath )
     QString baseName = srcFileInfo.completeBaseName();	
 
     baseName.append(endFix);	
-    QString newDirPath = dir.absolutePath() + "\\" + baseName;	
+    QString newDirPath = dir.absolutePath() + "/" + baseName;	
 
     QDir dirNew(newDirPath);
     if (!dirNew.exists()){
@@ -436,7 +436,7 @@ QString nki2mha_converter::CorrectSingle_NKI2RAW( const char* filePath )
     if ( v->pix_size != 2)//USHORT or short only
     {		
         cout << "not supported file format. only USHORT 16 bit is compatible" << endl;
-        return false;
+        return "";
     }
 
     int img2DSize = imgWidth*imgHeight;	
diff --git a/src/plastimatch/standalone/nki2mha_converter.h b/src/plastimatch/standalone/nki2mha_converter.h
index 1eadd06..c8526a7 100644
--- a/src/plastimatch/standalone/nki2mha_converter.h
+++ b/src/plastimatch/standalone/nki2mha_converter.h
@@ -1,57 +1,57 @@
-#ifndef NKI2MHA_CONVERTER_H
-#define NKI2MHA_CONVERTER_H
-//#ifndef nki2mha_converter_H
-//#define nki2mha_converter_H
-
-#include <QtGui/QMainWindow>
-#include "ui_nki2mha_converter.h"
-#include <QStringList>
-#include <vector>
-
-using namespace std;
-
-class nki2mha_converter : public QMainWindow
-{
-    Q_OBJECT
-
-public:
-    nki2mha_converter(QWidget *parent = 0, Qt::WFlags flags = 0);
-    ~nki2mha_converter();
-    QString CorrectSingle_NKI2MHA(const char* filePath);
-    QString CorrectSingle_NKI2DCM(const char* filePath);
-	QString CorrectSingle_NKI2DCM( const char* filePath, QString patientID, QString patientName);
-
-    QString CorrectSingle_NKI2RAW( const char* filePath );
-
-    QString CorrectSingle_MHA2DCM(const char* filePath );
-	QString CorrectSingle_MHA2DCM( const char* filePath, QString patientID, QString patientName);
-    //void LoadBadPixelMap(const char* filePath);
-    //void BadPixReplacement(YK16GrayImage* targetImg);
-    //void SaveBadPixelMap(vector<BADPIXELMAP>& vBadPixels);
-
-    public slots:
-        //void SLT_OpenOffsetFile();
-        //void SLT_OpenGainFile();
-        //void SLT_OpenBadpixelFile();
-        void SLT_OpenMultipleRaw();
-        void SLT_Correct_NKI2MHA(); //NKI to MHA
-        void SLT_Correct_NKI2DCM(); //NKI to MHA
-        void SLT_Correct_NKI2RAW(); //NKI to RAW: signed short!
-
-public:
-    //YK16GrayImage* m_pImgOffset;
-    //YK16GrayImage* m_pImgGain;
-    //Badpixmap;
-
-    //vector<BADPIXELMAP> m_vPixelReplMap;	
-    //vector<YK16GrayImage*> m_vpRawImg;
-    QStringList m_strlistPath;
-
-
-
-
-private:
-    Ui::nki2mha_converterClass ui;
-};
-
-#endif // nki2mha_converter_H
+#ifndef NKI2MHA_CONVERTER_H
+#define NKI2MHA_CONVERTER_H
+//#ifndef nki2mha_converter_H
+//#define nki2mha_converter_H
+
+#include <QtGui/QMainWindow>
+#include "ui_nki2mha_converter.h"
+#include <QStringList>
+#include <vector>
+
+using namespace std;
+
+class nki2mha_converter : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    nki2mha_converter(QWidget *parent = 0, Qt::WFlags flags = 0);
+    ~nki2mha_converter();
+    QString CorrectSingle_NKI2MHA(const char* filePath);
+    QString CorrectSingle_NKI2DCM(const char* filePath);
+	QString CorrectSingle_NKI2DCM( const char* filePath, QString patientID, QString patientName);
+
+    QString CorrectSingle_NKI2RAW( const char* filePath );
+
+    QString CorrectSingle_MHA2DCM(const char* filePath );
+	QString CorrectSingle_MHA2DCM( const char* filePath, QString patientID, QString patientName);
+    //void LoadBadPixelMap(const char* filePath);
+    //void BadPixReplacement(YK16GrayImage* targetImg);
+    //void SaveBadPixelMap(vector<BADPIXELMAP>& vBadPixels);
+
+    public slots:
+        //void SLT_OpenOffsetFile();
+        //void SLT_OpenGainFile();
+        //void SLT_OpenBadpixelFile();
+        void SLT_OpenMultipleRaw();
+        void SLT_Correct_NKI2MHA(); //NKI to MHA
+        void SLT_Correct_NKI2DCM(); //NKI to MHA
+        void SLT_Correct_NKI2RAW(); //NKI to RAW: signed short!
+
+public:
+    //YK16GrayImage* m_pImgOffset;
+    //YK16GrayImage* m_pImgGain;
+    //Badpixmap;
+
+    //vector<BADPIXELMAP> m_vPixelReplMap;	
+    //vector<YK16GrayImage*> m_vpRawImg;
+    QStringList m_strlistPath;
+
+
+
+
+private:
+    Ui::nki2mha_converterClass ui;
+};
+
+#endif // nki2mha_converter_H
diff --git a/src/plastimatch/standalone/nki2mha_converter.ui b/src/plastimatch/standalone/nki2mha_converter.ui
index 5a8d4ff..1b7c584 100644
--- a/src/plastimatch/standalone/nki2mha_converter.ui
+++ b/src/plastimatch/standalone/nki2mha_converter.ui
@@ -7,209 +7,178 @@
     <x>0</x>
     <y>0</y>
     <width>689</width>
-    <height>467</height>
+    <height>652</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Platimatch NKI /MHA file converter (bug report to: ykpark at mgh.harvard.edu)</string>
   </property>
   <widget class="QWidget" name="centralWidget">
-   <widget class="QLabel" name="label_4">
-    <property name="geometry">
-     <rect>
-      <x>130</x>
-      <y>20</y>
-      <width>201</width>
-      <height>16</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>Files to be converted (*.SCAN, *.mha)</string>
-    </property>
-   </widget>
-   <widget class="QPushButton" name="pushButton_OpenRaw">
-    <property name="geometry">
-     <rect>
-      <x>30</x>
-      <y>20</y>
-      <width>81</width>
-      <height>21</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>Load</string>
-    </property>
-   </widget>
-   <widget class="QPushButton" name="pushButton_ToMHA">
-    <property name="geometry">
-     <rect>
-      <x>20</x>
-      <y>210</y>
-      <width>71</width>
-      <height>31</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>ToMHA</string>
-    </property>
-   </widget>
-   <widget class="QPlainTextEdit" name="plainTextEdit_Raw">
-    <property name="geometry">
-     <rect>
-      <x>30</x>
-      <y>50</y>
-      <width>631</width>
-      <height>131</height>
-     </rect>
-    </property>
-   </widget>
-   <widget class="QPlainTextEdit" name="plainTextEdit_Corrected">
-    <property name="geometry">
-     <rect>
-      <x>30</x>
-      <y>280</y>
-      <width>631</width>
-      <height>131</height>
-     </rect>
-    </property>
-   </widget>
-   <widget class="QLabel" name="label_5">
-    <property name="geometry">
-     <rect>
-      <x>30</x>
-      <y>260</y>
-      <width>281</width>
-      <height>16</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>Files generated by conversion</string>
-    </property>
-   </widget>
-   <widget class="QPushButton" name="pushButton_ToRAW">
-    <property name="geometry">
-     <rect>
-      <x>100</x>
-      <y>210</y>
-      <width>71</width>
-      <height>31</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>ToRAW</string>
-    </property>
-   </widget>
-   <widget class="QGroupBox" name="groupBox">
-    <property name="geometry">
-     <rect>
-      <x>180</x>
-      <y>190</y>
-      <width>481</width>
-      <height>80</height>
-     </rect>
-    </property>
-    <property name="title">
-     <string>DICOM conversion</string>
-    </property>
-    <widget class="QPushButton" name="pushButton__ToDCM">
-     <property name="geometry">
-      <rect>
-       <x>10</x>
-       <y>20</y>
-       <width>61</width>
-       <height>31</height>
-      </rect>
-     </property>
-     <property name="text">
-      <string>ToDCM</string>
-     </property>
-    </widget>
-    <widget class="QLabel" name="label_7">
-     <property name="geometry">
-      <rect>
-       <x>100</x>
-       <y>10</y>
-       <width>61</width>
-       <height>16</height>
-      </rect>
-     </property>
-     <property name="text">
-      <string>Patient ID</string>
-     </property>
-    </widget>
-    <widget class="QLineEdit" name="lineEditPatientID">
-     <property name="geometry">
-      <rect>
-       <x>160</x>
-       <y>10</y>
-       <width>101</width>
-       <height>21</height>
-      </rect>
-     </property>
-    </widget>
-    <widget class="QLabel" name="label_6">
-     <property name="geometry">
-      <rect>
-       <x>290</x>
-       <y>10</y>
-       <width>71</width>
-       <height>16</height>
-      </rect>
-     </property>
-     <property name="text">
-      <string>Patient Name</string>
-     </property>
-    </widget>
-    <widget class="QLineEdit" name="lineEditPatientName">
-     <property name="geometry">
-      <rect>
-       <x>370</x>
-       <y>10</y>
-       <width>81</width>
-       <height>21</height>
-      </rect>
-     </property>
-    </widget>
-    <widget class="QLabel" name="label_8">
-     <property name="geometry">
-      <rect>
-       <x>90</x>
-       <y>40</y>
-       <width>331</width>
-       <height>20</height>
-      </rect>
-     </property>
-     <property name="text">
-      <string># if patient ID is left blank, file name will be regarded as ID/Name</string>
-     </property>
-    </widget>
-    <widget class="QLabel" name="label_9">
-     <property name="geometry">
-      <rect>
-       <x>90</x>
-       <y>60</y>
-       <width>361</width>
-       <height>20</height>
-      </rect>
-     </property>
-     <property name="text">
-      <string># For multiplie conversion, 1-based endfix will be used (~1,2,3..) for ID</string>
-     </property>
-    </widget>
-   </widget>
-   <widget class="QLabel" name="label_10">
-    <property name="geometry">
-     <rect>
-      <x>340</x>
-      <y>20</y>
-      <width>301</width>
-      <height>16</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>#Warning! Program may crash if a single file size is too big!</string>
-    </property>
-   </widget>
+   <layout class="QVBoxLayout" name="verticalLayout_6">
+    <item>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QPushButton" name="pushButton_OpenRaw">
+        <property name="text">
+         <string>Load</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <layout class="QHBoxLayout" name="horizontalLayout_5">
+      <item>
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Files to be converted (*.SCAN, *.mha)</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_10">
+        <property name="text">
+         <string>#Warning! Program may crash if a single file size is too big!</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <layout class="QHBoxLayout" name="horizontalLayout_3">
+      <item>
+       <widget class="QPlainTextEdit" name="plainTextEdit_Raw"/>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <layout class="QHBoxLayout" name="horizontalLayout_4">
+      <item>
+       <widget class="QPushButton" name="pushButton_ToMHA">
+        <property name="text">
+         <string>ToMHA</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="pushButton_ToRAW">
+        <property name="text">
+         <string>ToRAW</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <widget class="QGroupBox" name="groupBox">
+        <property name="title">
+         <string>DICOM conversion</string>
+        </property>
+        <layout class="QVBoxLayout" name="verticalLayout_5">
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_7">
+           <item>
+            <widget class="QPushButton" name="pushButton__ToDCM">
+             <property name="text">
+              <string>ToDCM</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="label_7">
+             <property name="text">
+              <string>Patient ID</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="lineEditPatientID"/>
+           </item>
+           <item>
+            <widget class="QLabel" name="label_6">
+             <property name="text">
+              <string>Patient Name</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="lineEditPatientName"/>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <layout class="QVBoxLayout" name="verticalLayout_2">
+           <item>
+            <widget class="QLabel" name="label_8">
+             <property name="text">
+              <string># if patient ID is left blank, file name will be regarded as ID/Name</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="label_9">
+             <property name="text">
+              <string># For multiplie conversion, 1-based endfix will be used (~1,2,3..) for ID</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <layout class="QVBoxLayout" name="verticalLayout_4">
+      <item>
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Files generated by conversion</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPlainTextEdit" name="plainTextEdit_Corrected"/>
+      </item>
+     </layout>
+    </item>
+   </layout>
+   <zorder>horizontalLayoutWidget</zorder>
+   <zorder>horizontalLayoutWidget_3</zorder>
+   <zorder>plainTextEdit_Raw</zorder>
+   <zorder>horizontalLayoutWidget_4</zorder>
+   <zorder>verticalLayoutWidget_2</zorder>
+   <zorder>verticalLayoutWidget_3</zorder>
+   <zorder>horizontalLayoutWidget_5</zorder>
   </widget>
   <widget class="QMenuBar" name="menuBar">
    <property name="geometry">
diff --git a/src/plastimatch/standalone/nki2mha_converter_main.cpp b/src/plastimatch/standalone/nki2mha_converter_main.cpp
index 2e20b2c..95f2f53 100644
--- a/src/plastimatch/standalone/nki2mha_converter_main.cpp
+++ b/src/plastimatch/standalone/nki2mha_converter_main.cpp
@@ -1,10 +1,10 @@
-#include "nki2mha_converter.h"
-#include <QtGui/QApplication>
-
-int main(int argc, char *argv[])
-{
-	QApplication a(argc, argv);
-	nki2mha_converter w;
-	w.show();
-	return a.exec();
-}
+#include "nki2mha_converter.h"
+#include <QtGui/QApplication>
+
+int main(int argc, char *argv[])
+{
+	QApplication a(argc, argv);
+	nki2mha_converter w;
+	w.show();
+	return a.exec();
+}
diff --git a/src/plastimatch/standalone/qcustomplot.cpp b/src/plastimatch/standalone/qcustomplot.cpp
new file mode 100644
index 0000000..b11adb2
--- /dev/null
+++ b/src/plastimatch/standalone/qcustomplot.cpp
@@ -0,0 +1,15034 @@
+/***************************************************************************
+**                                                                        **
+**  QCustomPlot, a simple to use, modern plotting widget for Qt           **
+**  Copyright (C) 2012 Emanuel Eichhammer                                 **
+**                                                                        **
+**  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/.   **
+**                                                                        **
+****************************************************************************
+**           Author: Emanuel Eichhammer                                   **
+**  Website/Contact: http://www.WorksLikeClockwork.com/                   **
+**             Date: 09.06.12                                             **
+****************************************************************************/
+
+/*! \mainpage %QCustomPlot Documentation
+ 
+  Below is a brief overview of and guide to the classes and their relations. If you are new to
+  QCustomPlot and just want to start using it, it's recommended to look at the examples/tutorials
+  at
+ 
+  http://www.WorksLikeClockWork.com/index.php/components/qt-plotting-widget
+ 
+  This documentation is especially helpful when you're familiar with the basic concept of how to use
+  %QCustomPlot and you wish to learn more about specific functionality.
+ 
+  \section simpleoverview Simplified Class Overview
+  
+  \image latex ClassesOverviewSimplified.png "" width=1.2\textwidth
+  \image html ClassesOverviewSimplified.png
+  <center>Simplified diagram of most important classes, view the \ref classoverview "Class Overview" to see a full overview.</center>
+  
+  The central widget which displays the plottables and axes on its surface is QCustomPlot. Usually,
+  you don't create the axes yourself, but you use the ones already inside every QCustomPlot
+  instance (xAxis, yAxis, xAxis2, yAxis2).
+
+  \section plottables Plottables
+  
+  \a Plottables are classes that display any kind of data inside the QCustomPlot. They all derive
+  from QCPAbstractPlottable. For example, the QCPGraph class is a plottable that displays a graph
+  inside the plot with different line styles, scatter styles, filling etc.
+  
+  Since plotting graphs is such a dominant use case, QCustomPlot has a special interface for working
+  with QCPGraph plottables, that makes it very easy to handle them:\n
+  You create a new graph with QCustomPlot::addGraph and access them with QCustomPlot::graph.
+  
+  For all other plottables, you need to use the normal plottable interface:\n
+  First, you create an instance of the plottable you want, e.g.
+  \code
+  QCPCurve *newCurve = new QCPCurve(customPlot->xAxis, customPlot->yAxis);\endcode
+  add it to the customPlot with QCustomPlot::addPlottable:
+  \code
+  customPlot->addPlottable(newCurve);\endcode
+  and then modify the properties of the newly created plottable via <tt>newCurve</tt>.
+  
+  Plottables (including graphs) can be retrieved via QCustomPlot::plottable. Since the return type
+  of that function is the abstract base class of all plottables, QCPAbstractPlottable, you will
+  probably want to qobject_cast (or dynamic_cast) the returned pointer to the respective plottable
+  subclass. (As usual, if the cast returns zero, the plottable wasn't of that specific subclass.)
+  
+  All further interfacing with plottables (e.g how to set data) is specific to the plottable type.
+  See the documentations of the subclasses: QCPGraph, QCPCurve, QCPBars, QCPStatisticalBox.
+
+  \section axes Controlling the Axes
+  
+  As mentioned, QCustomPlot has four axes by default: \a xAxis (bottom), \a yAxis (left), \a xAxis2
+  (top), \a yAxis2 (right).
+  
+  Their range is handled by the simple QCPRange class. You can set the range with the
+  QCPAxis::setRange function. By default, the axes represent a linear scale. To set a logarithmic
+  scale, set QCPAxis::setScaleType to QCPAxis::stLogarithmic. The logarithm base can be set freely
+  with QCPAxis::setScaleLogBase.
+  
+  By default, an axis automatically creates and labels ticks in a sensible manner, i.e. with a tick
+  interval that's pleasing to the viewer. See the following functions for tick manipulation:\n
+  QCPAxis::setTicks, QCPAxis::setAutoTicks, QCPAxis::setAutoTickCount, QCPAxis::setAutoTickStep,
+  QCPAxis::setTickLabels, QCPAxis::setTickLabelType, QCPAxis::setTickLabelRotation,
+  QCPAxis::setTickStep, QCPAxis::setTickLength,...
+  
+  Each axis can be given an axis label (e.g. "Voltage [mV]") with QCPAxis::setLabel.
+  
+  The distance of an axis backbone to the respective QCustomPlot widget border is called its margin.
+  Normally, the margins are calculated automatically. To change this, set QCustomPlot::setAutoMargin
+  to false and set the margins manually with QCustomPlot::setMargin.
+  
+  \section legend Plot Legend
+  
+  Every QCustomPlot owns a QCPLegend (as \a legend). That's a small window inside the plot which
+  lists the plottables with an icon of the plottable line/symbol and a description. The Description
+  is retrieved from the plottable name (QCPAbstractPlottable::setName). Plottables can be added and
+  removed from the legend via \ref QCPAbstractPlottable::addToLegend and \ref
+  QCPAbstractPlottable::removeFromLegend. By default, adding a plottable to QCustomPlot
+  automatically adds it to the legend, too. This behaviour can be modified with the
+  QCustomPlot::setAutoAddPlottableToLegend property.
+  
+  The QCPLegend provides an interface to access, add and remove legend items directly, too. See
+  QCPLegend::item, QCPLegend::itemWithPlottable, QCPLegend::addItem, QCPLegend::removeItem for
+  example.
+  
+  \section userinteraction User Interactions
+  
+  QCustomPlot currently supports dragging axis ranges with the mouse (\ref
+  QCustomPlot::setRangeDrag), zooming axis ranges with the mouse wheel (\ref
+  QCustomPlot::setRangeZoom) and a complete selection mechanism of most objects.
+  
+  The availability of these interactions is controlled with \ref QCustomPlot::setInteractions. For
+  details about the interaction system, see the documentation there.
+  
+  Further, QCustomPlot always emits corresponding signals, when objects are clicked or
+  doubleClicked. See \ref QCustomPlot::plottableClick, \ref QCustomPlot::plottableDoubleClick
+  and \ref QCustomPlot::axisClick for example.
+  
+  \section items Items
+  
+  Apart from plottables there is another category of plot objects that are important: Items. The
+  base class of all items is QCPAbstractItem. An item sets itself apart from plottables in that
+  it's not necessarily bound to any axes. This means it may also be positioned in absolute pixel
+  coordinates or placed at a relative position on the axis rect. Further it usually doesn't
+  represent data directly but acts as decoration, emphasis, description etc.
+  
+  Multiple items can be arranged in a parent-child-hierarchy allowing for dynamical behaviour. For
+  example, you could place the head of an arrow at a certain plot coordinate, so it always points
+  to some important part of your data. The tail of the arrow can be fixed at a text label item
+  which always resides in the top center of the axis rect (independent of where the user drags the
+  axis ranges).
+  
+  For a more detailed introduction, see the QCPAbstractItem documentation, and from there the
+  documentations of the individual built-in items, to find out how to use them.
+  
+  \section performancetweaks Performance Tweaks
+  
+  Although QCustomPlot is quite fast, some features like semi-transparent fills and antialiasing
+  can cause a significant slow down. Here are some thoughts on how to increase performance. By far
+  the most time is spent in the drawing functions, specifically the drawing of graphs. For maximum
+  performance, consider the following (most recommended/effective measures first):
+  
+  \li use Qt 4.8.0 and up. Performance has doubled or tripled with respect to Qt 4.7.4. However they broke QPainter,
+  drawing pixel precise things, e.g. scatters, isn't possible with Qt 4.8.0/1. So it's a performance vs. plot
+  quality tradeoff when switching to Qt 4.8.
+  \li To increase responsiveness during dragging, consider setting \ref QCustomPlot::setNoAntialiasingOnDrag to true.
+  \li On X11 (linux), avoid the (slow) native drawing system, use raster by supplying
+  "-graphicssystem raster" as command line argument or calling QApplication::setGraphicsSystem("raster")
+  before creating the QApplication object.
+  \li On all operating systems, use OpenGL hardware acceleration by supplying "-graphicssystem
+  opengl" as command line argument or calling QApplication::setGraphicsSystem("opengl"). If OpenGL
+  is available, this will slightly decrease the quality of antialiasing, but extremely increase
+  performance especially with alpha (semi-transparent) fills, much antialiasing and a large
+  QCustomPlot drawing surface. Note however, that the maximum frame rate might be constrained by
+  the vertical sync frequency of your monitor (VSync can be disabled in the graphics card driver
+  configuration). So for simple plots (where the potential framerate is far above 60 frames per
+  second), OpenGL acceleration might achieve numerically lower frame rates than the other
+  graphics systems, because they are not capped at the VSync frequency.
+  \li Avoid any kind of alpha (transparency), especially in fills
+  \li Avoid any kind of antialiasing, especially in graph lines (see QCustomPlot::setNotAntialiasedElements)
+  \li Avoid repeatedly setting the complete data set with QCPGraph::setData. Use QCPGraph::addData instead, if most
+  data points stay unchanged, e.g. in a running measurement.
+  \li Set the \a copy parameter of the setData functions to false, so only pointers get
+  transferred. (Relevant only if preparing data maps with a large number of points, i.e. over 10000)
+*/
+
+/*! \page classoverview Class Overview
+  
+  \image latex ClassesOverview.png "Overview of all classes and their relations" width=1.2\textwidth
+  \image html ClassesOverview.png "Overview of all classes and their relations"
+  
+*/
+
+#include "qcustomplot.h"
+
+// ================================================================================
+// =================== QCPData
+// ================================================================================
+
+/*! \class QCPData
+  \brief Holds the data of one single data point for QCPGraph.
+  
+  The stored data is:
+  \li \a key: coordinate on the key axis of this data point
+  \li \a value: coordinate on the value axis of this data point
+  \li \a keyErrorMinus: negative error in the key dimension (for error bars)
+  \li \a keyErrorPlus: positive error in the key dimension (for error bars)
+  \li \a valueErrorMinus: negative error in the value dimension (for error bars)
+  \li \a valueErrorPlus: positive error in the value dimension (for error bars)
+  
+  \see QCPDataMap
+*/
+
+/*!
+  Constructs a data point with key, value and all errors set to zero.
+*/
+QCPData::QCPData() :
+  key(0),
+  value(0),
+  keyErrorPlus(0),
+  keyErrorMinus(0),
+  valueErrorPlus(0),
+  valueErrorMinus(0)
+{
+}
+
+/*!
+  Constructs a data point with the specified \a key and \a value. All errors are set to zero.
+*/
+QCPData::QCPData(double key, double value) :
+  key(key),
+  value(value),
+  keyErrorPlus(0),
+  keyErrorMinus(0),
+  valueErrorPlus(0),
+  valueErrorMinus(0)
+{
+}
+
+// ================================================================================
+// =================== QCPCurveData
+// ================================================================================
+
+/*! \class QCPCurveData
+  \brief Holds the data of one single data point for QCPCurve.
+  
+  The stored data is:
+  \li \a t: the free parameter of the curve at this curve point (cp. the mathematical vector <em>(x(t), y(t))</em>)
+  \li \a key: coordinate on the key axis of this curve point
+  \li \a value: coordinate on the value axis of this curve point
+  
+  \see QCPCurveDataMap
+*/
+
+/*!
+  Constructs a curve data point with t, key and value set to zero.
+*/
+QCPCurveData::QCPCurveData() :
+  t(0),
+  key(0),
+  value(0)
+{
+}
+
+/*!
+  Constructs a curve data point with the specified \a t, \a key and \a value.
+*/
+QCPCurveData::QCPCurveData(double t, double key, double value) :
+  t(t),
+  key(key),
+  value(value)
+{
+}
+
+
+// ================================================================================
+// =================== QCPBarData
+// ================================================================================
+
+/*! \class QCPBarData
+  \brief Holds the data of one single data point (one bar) for QCPBars.
+  
+  The stored data is:
+  \li \a key: coordinate on the key axis of this bar
+  \li \a value: height coordinate on the value axis of this bar
+  
+  \see QCPBarDataaMap
+*/
+
+/*!
+  Constructs a bar data point with key and value set to zero.
+*/
+QCPBarData::QCPBarData() :
+  key(0),
+  value(0)
+{
+}
+
+/*!
+  Constructs a bar data point with the specified \a key and \a value.
+*/
+QCPBarData::QCPBarData(double key, double value) :
+  key(key),
+  value(value)
+{
+}
+
+// ================================================================================
+// =================== QCPGraph
+// ================================================================================
+
+/*! \class QCPGraph
+  \brief A plottable representing a graph in a plot.
+
+  Usually QCustomPlot creates it internally via QCustomPlot::addGraph and the resulting instance is
+  accessed via QCustomPlot::graph.
+
+  To plot data, assign it with the \ref setData or \ref addData functions.
+  
+  \section appearance Changing the appearance
+  
+  The appearance of the graph is mainly determined by the line style, scatter style, brush and pen
+  of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen).
+  
+  \subsection filling Filling under or between graphs
+  
+  QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to
+  the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill,
+  just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent.
+  
+  By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill
+  between this graph and another one, call \ref setChannelFillGraph with the other graph as
+  parameter.
+
+  \see QCustomPlot::addGraph, QCustomPlot::graph, QCPLegend::addGraph
+*/
+
+/*!
+  Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
+  axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
+  the same orientation. If either of these restrictions is violated, a corresponding message is
+  printed to the debug output (qDebug), the construction is not aborted, though.
+  
+  The constructed QCPGraph can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
+  then takes ownership of the graph.
+  
+  To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.
+*/
+QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis)
+{
+  mData = new QCPDataMap;
+  
+  setPen(QPen(Qt::blue));
+  setErrorPen(QPen(Qt::black));
+  setBrush(Qt::NoBrush);
+  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
+  setSelectedBrush(Qt::NoBrush);
+  
+  setLineStyle(lsLine);
+  setScatterStyle(QCP::ssNone);
+  setScatterSize(6);
+  setErrorType(etNone);
+  setErrorBarSize(6);
+  setErrorBarSkipSymbol(true);
+  setChannelFillGraph(0);
+}
+
+QCPGraph::~QCPGraph()
+{
+  if (mParentPlot)
+  {
+    // if another graph has a channel fill towards this graph, set it to zero
+    for (int i=0; i<mParentPlot->graphCount(); ++i)
+    {
+      if (mParentPlot->graph(i)->channelFillGraph() == this)
+        mParentPlot->graph(i)->setChannelFillGraph(0);
+    }
+  }
+  delete mData;
+}
+
+/*!
+  Replaces the current data with the provided \a data.
+  
+  If \a copy is set to true, data points in \a data will only be copied. if false, the graph
+  takes ownership of the passed data and replaces the internal data pointer with it. This is
+  significantly faster than copying for large datasets.
+*/
+void QCPGraph::setData(QCPDataMap *data, bool copy)
+{
+  if (copy)
+  {
+    *mData = *data;
+  } else
+  {
+    delete mData;
+    mData = data;
+  }
+}
+
+/*! \overload
+  
+  Replaces the current data with the provided points in \a key and \a value pairs. The provided
+  vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+*/
+void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    mData->insertMulti(newData.key, newData);
+  }
+}
+
+/*!
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  symmetrical value error of the data points are set to the values in \a valueError.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+  
+  For asymmetrical errors (plus different from minus), see the overloaded version of this function.
+*/
+void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, valueError.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.valueErrorMinus = valueError[i];
+    newData.valueErrorPlus = valueError[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  \overload
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  negative value error of the data points are set to the values in \a valueErrorMinus, the positive
+  value error to \a valueErrorPlus.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+*/
+void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, valueErrorMinus.size());
+  n = qMin(n, valueErrorPlus.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.valueErrorMinus = valueErrorMinus[i];
+    newData.valueErrorPlus = valueErrorPlus[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  symmetrical key error of the data points are set to the values in \a keyError.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+  
+  For asymmetrical errors (plus different from minus), see the overloaded version of this function.
+*/
+void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, keyError.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.keyErrorMinus = keyError[i];
+    newData.keyErrorPlus = keyError[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  \overload
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  negative key error of the data points are set to the values in \a keyErrorMinus, the positive
+  key error to \a keyErrorPlus.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+*/
+void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, keyErrorMinus.size());
+  n = qMin(n, keyErrorPlus.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.keyErrorMinus = keyErrorMinus[i];
+    newData.keyErrorPlus = keyErrorPlus[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  symmetrical key and value errors of the data points are set to the values in \a keyError and \a valueError.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+  
+  For asymmetrical errors (plus different from minus), see the overloaded version of this function.
+*/
+void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, valueError.size());
+  n = qMin(n, keyError.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.keyErrorMinus = keyError[i];
+    newData.keyErrorPlus = keyError[i];
+    newData.valueErrorMinus = valueError[i];
+    newData.valueErrorPlus = valueError[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  \overload
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  negative key and value errors of the data points are set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive
+  key and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+*/
+void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, valueErrorMinus.size());
+  n = qMin(n, valueErrorPlus.size());
+  n = qMin(n, keyErrorMinus.size());
+  n = qMin(n, keyErrorPlus.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.keyErrorMinus = keyErrorMinus[i];
+    newData.keyErrorPlus = keyErrorPlus[i];
+    newData.valueErrorMinus = valueErrorMinus[i];
+    newData.valueErrorPlus = valueErrorPlus[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+
+/*!
+  Sets how the single data points are connected in the plot or how they are represented visually
+  apart from the scatter symbol. For scatter-only plots, set \a ls to \ref lsNone and \ref
+  setScatterStyle to the desired scatter style.
+  
+  \see setScatterStyle
+*/
+void QCPGraph::setLineStyle(LineStyle ls)
+{
+  mLineStyle = ls;
+}
+
+/*! 
+  Sets the visual appearance of single data points in the plot. If set to \ref QCP::ssNone, no scatter points
+  are drawn (e.g. for line-only-plots with appropriate line style).
+  
+  \see ScatterStyle, setLineStyle
+*/
+void QCPGraph::setScatterStyle(QCP::ScatterStyle ss)
+{
+  mScatterStyle = ss;
+}
+
+/*! 
+  This defines how big (in pixels) single scatters are drawn, if scatter style (\ref
+  setScatterStyle) isn't \ref QCP::ssNone, \ref QCP::ssDot or \ref QCP::ssPixmap. Floating point values are
+  allowed for fine grained control over optical appearance with antialiased painting.
+  
+  \see ScatterStyle
+*/
+void QCPGraph::setScatterSize(double size)
+{
+  mScatterSize = size;
+}
+
+/*! 
+  If the scatter style (\ref setScatterStyle) is set to ssPixmap, this function defines the QPixmap
+  that will be drawn centered on the data point coordinate.
+  
+  \see ScatterStyle
+*/
+void QCPGraph::setScatterPixmap(const QPixmap &pixmap)
+{
+  mScatterPixmap = pixmap;
+}
+
+/*!
+  Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data
+  point. If you set \a errorType to something other than \ref etNone, make sure to actually pass
+  error data via the specific setData functions along with the data points (e.g. \ref
+  setDataValueError, \ref setDataKeyError, \ref setDataBothError).
+
+  \see ErrorType
+*/
+void QCPGraph::setErrorType(ErrorType errorType)
+{
+  mErrorType = errorType;
+}
+
+/*!
+  Sets the pen with which the error bars will be drawn.
+  \see setErrorBarSize, setErrorType
+*/
+void QCPGraph::setErrorPen(const QPen &pen)
+{
+  mErrorPen = pen;
+}
+
+/*! 
+  Sets the width of the handles at both ends of an error bar in pixels.
+*/
+void QCPGraph::setErrorBarSize(double size)
+{
+  mErrorBarSize = size;
+}
+
+/*! 
+  If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but
+  leave some free space around the symbol.
+  
+  This feature uses the current scatter size (\ref setScatterSize) to determine the size of the
+  area to leave blank. So when drawing Pixmaps as scatter points (\ref QCP::ssPixmap), the scatter size
+  must be set manually to a value corresponding to the size of the Pixmap, if the error bars should
+  leave gaps to its boundaries.
+*/
+void QCPGraph::setErrorBarSkipSymbol(bool enabled)
+{
+  mErrorBarSkipSymbol = enabled;
+}
+
+/*! 
+  Sets the target graph for filling the area between this graph and \a targetGraph with the current
+  brush (\ref setBrush).
+  
+  When \a targetGraph is set to 0, a normal graph fill will be produced. This means, when the brush
+  is not Qt::NoBrush or fully transparent, a fill all the way to the zero-value-line parallel to
+  the key axis of this graph will be drawn. To disable any filling, set the brush to Qt::NoBrush.
+  \see setBrush
+*/
+void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
+{
+  // prevent setting channel target to this graph itself:
+  if (targetGraph == this)
+  {
+    qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
+    mChannelFillGraph = 0;
+    return;
+  }
+  // prevent setting channel target to a graph not in the plot:
+  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
+    mChannelFillGraph = 0;
+    return;
+  }
+  
+  mChannelFillGraph = targetGraph;
+}
+
+/*!
+  Adds the provided data points in \a dataMap to the current data.
+  \see removeData
+*/
+void QCPGraph::addData(const QCPDataMap &dataMap)
+{
+  mData->unite(dataMap);
+}
+
+/*! \overload
+  Adds the provided single data point in \a data to the current data.
+  \see removeData
+*/
+void QCPGraph::addData(const QCPData &data)
+{
+  mData->insertMulti(data.key, data);
+}
+
+/*! \overload
+  Adds the provided single data point as \a key and \a value pair to the current data.
+  \see removeData
+*/
+void QCPGraph::addData(double key, double value)
+{
+  QCPData newData;
+  newData.key = key;
+  newData.value = value;
+  mData->insertMulti(newData.key, newData);
+}
+
+/*! \overload
+  Adds the provided data points as \a key and \a value pairs to the current data.
+  \see removeData
+*/
+void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
+{
+  int n = qMin(keys.size(), values.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = keys[i];
+    newData.value = values[i];
+    mData->insertMulti(newData.key, newData);
+  }
+}
+
+/*!
+  Removes all data points with keys smaller than \a key.
+  \see addData, clearData
+*/
+void QCPGraph::removeDataBefore(double key)
+{
+  QCPDataMap::iterator it = mData->begin();
+  while (it != mData->end() && it.key() < key)
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with keys greater than \a key.
+  \see addData, clearData
+*/
+void QCPGraph::removeDataAfter(double key)
+{
+  if (mData->isEmpty()) return;
+  QCPDataMap::iterator it = mData->upperBound(key);
+  while (it != mData->end())
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with keys between \a fromKey and \a toKey.
+  if \a fromKey is greater or equal to \a toKey, the function does nothing. To remove
+  a single data point with known key, use \ref removeData(double key).
+  
+  \see addData, clearData
+*/
+void QCPGraph::removeData(double fromKey, double toKey)
+{
+  if (fromKey >= toKey || mData->isEmpty()) return;
+  QCPDataMap::iterator it = mData->upperBound(fromKey);
+  QCPDataMap::iterator itEnd = mData->upperBound(toKey);
+  while (it != itEnd)
+    it = mData->erase(it);
+}
+
+/*! \overload
+  
+  Removes a single data point at \a key. If the position is not known with absolute precision,
+  consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval around
+  the suspected position, depeding on the precision with which the key is known.
+
+  \see addData, clearData
+*/
+void QCPGraph::removeData(double key)
+{
+  mData->remove(key);
+}
+
+/*!
+  Removes all data points.
+  \see removeData, removeDataAfter, removeDataBefore
+*/
+void QCPGraph::clearData()
+{
+  mData->clear();
+}
+
+/* inherits documentation from base class */
+double QCPGraph::selectTest(const QPointF &pos) const
+{
+  if (mData->isEmpty() || !mVisible)
+    return -1;
+  
+  return pointDistance(pos);
+}
+
+/*! \overload
+  
+  Allows to define whether error bars are taken into consideration when determining the new axis
+  range.
+*/
+void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
+{
+  rescaleKeyAxis(onlyEnlarge, includeErrorBars);
+  rescaleValueAxis(onlyEnlarge, includeErrorBars);
+}
+
+/*! \overload
+  
+  Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken into consideration
+  when determining the new axis range.
+*/
+void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
+{
+  // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
+  // that getKeyRange is passed the includeErrorBars value.
+  if (mData->isEmpty()) return;
+
+  SignDomain signDomain = sdBoth;
+  if (mKeyAxis->scaleType() == QCPAxis::stLogarithmic)
+    signDomain = (mKeyAxis->range().upper < 0 ? sdNegative : sdPositive);
+  
+  bool validRange;
+  QCPRange newRange = getKeyRange(validRange, signDomain, includeErrorBars);
+  
+  if (validRange)
+  {
+    if (onlyEnlarge)
+    {
+      if (mKeyAxis->range().lower < newRange.lower)
+        newRange.lower = mKeyAxis->range().lower;
+      if (mKeyAxis->range().upper > newRange.upper)
+        newRange.upper = mKeyAxis->range().upper;
+    }
+    mKeyAxis->setRange(newRange);
+  }
+}
+
+/*! \overload
+  
+  Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken into consideration
+  when determining the new axis range.
+*/
+void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
+{
+  // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
+  // is that getValueRange is passed the includeErrorBars value.
+  if (mData->isEmpty()) return;
+
+  SignDomain signDomain = sdBoth;
+  if (mValueAxis->scaleType() == QCPAxis::stLogarithmic)
+    signDomain = (mValueAxis->range().upper < 0 ? sdNegative : sdPositive);
+  
+  bool validRange;
+  QCPRange newRange = getValueRange(validRange, signDomain, includeErrorBars);
+  
+  if (validRange)
+  {
+    if (onlyEnlarge)
+    {
+      if (mValueAxis->range().lower < newRange.lower)
+        newRange.lower = mValueAxis->range().lower;
+      if (mValueAxis->range().upper > newRange.upper)
+        newRange.upper = mValueAxis->range().upper;
+    }
+    mValueAxis->setRange(newRange);
+  }
+}
+
+/* inherits documentation from base class */
+void QCPGraph::draw(QCPPainter *painter)
+{
+  if (mKeyAxis->range().size() <= 0 || mData->isEmpty()) return;
+  if (mLineStyle == lsNone && mScatterStyle == QCP::ssNone) return;
+  
+  // allocate line and (if necessary) point vectors:
+  QVector<QPointF> *lineData = new QVector<QPointF>;
+  QVector<QCPData> *pointData = 0;
+  if (mScatterStyle != QCP::ssNone)
+    pointData = new QVector<QCPData>;
+  
+  // fill vectors with data appropriate to plot style:
+  getPlotData(lineData, pointData);
+
+  // draw fill of graph:
+  drawFill(painter, lineData);
+  
+  // draw line:
+  if (mLineStyle == lsImpulse)
+    drawImpulsePlot(painter, lineData);
+  else if (mLineStyle != lsNone)
+    drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
+  
+  // draw scatters:
+  if (pointData)
+    drawScatterPlot(painter, pointData);
+  
+  // free allocated line and point vectors:
+  delete lineData;
+  if (pointData)
+    delete pointData;
+}
+
+/* inherits documentation from base class */
+void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRect &rect) const
+{
+  // draw fill:
+  if (mBrush.style() != Qt::NoBrush)
+  {
+    applyFillAntialiasingHint(painter);
+    painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
+  }
+  // draw line vertically centered:
+  if (mLineStyle != lsNone)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
+  }
+  // draw scatter symbol:
+  if (mScatterStyle != QCP::ssNone)
+  {
+    if (mScatterStyle == QCP::ssPixmap && (mScatterPixmap.size().width() > rect.width() || mScatterPixmap.size().height() > rect.height()))
+    {
+      // handle pixmap scatters that are larger than legend icon rect separately.
+      // We resize them and draw them manually, instead of calling drawScatter:
+      QSize newSize = mScatterPixmap.size();
+      newSize.scale(rect.size(), Qt::KeepAspectRatio);
+      QRect targetRect;
+      targetRect.setSize(newSize);
+      targetRect.moveCenter(rect.center());
+      bool smoothBackup = painter->testRenderHint(QPainter::SmoothPixmapTransform);
+      painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
+      painter->drawPixmap(targetRect, mScatterPixmap);
+      painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
+    } else
+    {
+      applyScattersAntialiasingHint(painter);
+      painter->setPen(mPen);
+      painter->drawScatter(QRectF(rect).center().x(), QRectF(rect).center().y(), mScatterSize, mScatterStyle);
+    }
+  }
+}
+
+/*! 
+  \internal
+  This function branches out to the line style specific "get(...)PlotData" functions, according to the
+  line style of the graph.
+  \param lineData will be filled with raw points that will be drawn with the according draw functions, e.g. \ref drawLinePlot and \ref drawImpulsePlot.
+  These aren't necessarily the original data points, since for step plots for example, additional points are needed for drawing lines that make up steps.
+  If the line style of the graph is \ref lsNone, the \a lineData vector will be left untouched.
+  \param pointData will be filled with the original data points so \ref drawScatterPlot can draw the scatter symbols accordingly. If no scatters need to be
+  drawn, i.e. scatter style is \ref QCP::ssNone, pass 0 as \a pointData, and this step will be skipped.
+  
+  \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData, getStepCenterPlotData, getImpulsePlotData
+*/
+void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
+{
+  switch(mLineStyle)
+  {
+    case lsNone: getScatterPlotData(pointData); break;
+    case lsLine: getLinePlotData(lineData, pointData); break;
+    case lsStepLeft: getStepLeftPlotData(lineData, pointData); break;
+    case lsStepRight: getStepRightPlotData(lineData, pointData); break;
+    case lsStepCenter: getStepCenterPlotData(lineData, pointData); break;
+    case lsImpulse: getImpulsePlotData(lineData, pointData); break;
+  }
+}
+
+/*! 
+  \internal
+  If line style is \ref lsNone and scatter style is not \ref QCP::ssNone, this function serves at providing the
+  visible data points in \a pointData, so the \ref drawScatterPlot function can draw the scatter points
+  accordingly.
+  
+  If line style is not \ref lsNone, this function is not called and the data for the scatter points
+  are (if needed) calculated inside the corresponding other "get(...)PlotData" functions.
+  \see drawScatterPlot
+*/
+void QCPGraph::getScatterPlotData(QVector<QCPData> *pointData) const
+{
+  if (!pointData) return;
+  
+  // get visible data range:
+  QCPDataMap::const_iterator lower, upper;
+  int dataCount;
+  getVisibleDataBounds(lower, upper, dataCount);
+  // prepare vectors:
+  if (pointData)
+    pointData->resize(dataCount);
+
+  // position data points:
+  QCPDataMap::const_iterator it = lower;
+  QCPDataMap::const_iterator upperEnd = upper+1;
+  int i = 0;
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    while (it != upperEnd)
+    {
+      (*pointData)[i] = it.value();
+      ++i;
+      ++it;
+    }
+  } else // key axis is horizontal
+  {
+    while (it != upperEnd)
+    {
+      (*pointData)[i] = it.value();
+      ++i;
+      ++it;
+    }
+  }
+}
+
+/*! 
+  \internal
+  Places the raw data points needed for a normal linearly connected plot in \a lineData.
+
+  As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
+  points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
+  filling the vector.
+  \see drawLinePlot
+*/
+void QCPGraph::getLinePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
+{
+  // get visible data range:
+  QCPDataMap::const_iterator lower, upper;
+  int dataCount;
+  getVisibleDataBounds(lower, upper, dataCount);
+  // prepare vectors:
+  if (lineData)
+  { 
+    // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
+    lineData->reserve(dataCount+2);
+    lineData->resize(dataCount);
+  }
+  if (pointData)
+    pointData->resize(dataCount);
+
+  // position data points:
+  QCPDataMap::const_iterator it = lower;
+  QCPDataMap::const_iterator upperEnd = upper+1;
+  int i = 0;
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    while (it != upperEnd)
+    {
+      if (pointData)
+        (*pointData)[i] = it.value();
+      (*lineData)[i].setX(mValueAxis->coordToPixel(it.value().value));
+      (*lineData)[i].setY(mKeyAxis->coordToPixel(it.key()));
+      ++i;
+      ++it;
+    }
+  } else // key axis is horizontal
+  {
+    while (it != upperEnd)
+    {
+      if (pointData)
+        (*pointData)[i] = it.value();
+      (*lineData)[i].setX(mKeyAxis->coordToPixel(it.key()));
+      (*lineData)[i].setY(mValueAxis->coordToPixel(it.value().value));
+      ++i;
+      ++it;
+    }
+  }
+}
+
+/*! 
+  \internal
+  Places the raw data points needed for a step plot with left oriented steps in \a lineData.
+
+  As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
+  points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
+  filling the vector.
+  \see drawLinePlot
+*/
+void QCPGraph::getStepLeftPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
+{
+  // get visible data range:
+  QCPDataMap::const_iterator lower, upper;
+  int dataCount;
+  getVisibleDataBounds(lower, upper, dataCount);
+  // prepare vectors:
+  if (lineData)
+  {
+    // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
+    // multiplied by 2 because step plot needs two polyline points per one actual data point
+    lineData->reserve(dataCount*2+2);
+    lineData->resize(dataCount*2);
+  }
+  if (pointData)
+    pointData->resize(dataCount);
+  
+  // position data points:
+  QCPDataMap::const_iterator it = lower;
+  QCPDataMap::const_iterator upperEnd = upper+1;
+  int i = 0;
+  int ipoint = 0;
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    double lastValue = mValueAxis->coordToPixel(it.value().value);
+    double key;
+    while (it != upperEnd)
+    {
+      if (pointData)
+      {
+        (*pointData)[ipoint] = it.value();
+        ++ipoint;
+      }
+      key = mKeyAxis->coordToPixel(it.key());
+      (*lineData)[i].setX(lastValue);
+      (*lineData)[i].setY(key);
+      ++i;
+      lastValue = mValueAxis->coordToPixel(it.value().value);
+      (*lineData)[i].setX(lastValue);
+      (*lineData)[i].setY(key);
+      ++i;
+      ++it;
+    }
+  } else // key axis is horizontal
+  {
+    double lastValue = mValueAxis->coordToPixel(it.value().value);
+    double key;
+    while (it != upperEnd)
+    {
+      if (pointData)
+      {
+        (*pointData)[ipoint] = it.value();
+        ++ipoint;
+      }
+      key = mKeyAxis->coordToPixel(it.key());
+      (*lineData)[i].setX(key);
+      (*lineData)[i].setY(lastValue);
+      ++i;
+      lastValue = mValueAxis->coordToPixel(it.value().value);
+      (*lineData)[i].setX(key);
+      (*lineData)[i].setY(lastValue);
+      ++i;
+      ++it;
+    }
+  }
+}
+
+/*! 
+  \internal
+  Places the raw data points needed for a step plot with right oriented steps in \a lineData.
+
+  As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
+  points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
+  filling the vector.
+  \see drawLinePlot
+*/
+void QCPGraph::getStepRightPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
+{
+  // get visible data range:
+  QCPDataMap::const_iterator lower, upper;
+  int dataCount;
+  getVisibleDataBounds(lower, upper, dataCount);
+  // prepare vectors:
+  if (lineData)
+  {
+    // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
+    // multiplied by 2 because step plot needs two polyline points per one actual data point
+    lineData->reserve(dataCount*2+2);
+    lineData->resize(dataCount*2);
+  }
+  if (pointData)
+    pointData->resize(dataCount);
+  
+  // position points:
+  QCPDataMap::const_iterator it = lower;
+  QCPDataMap::const_iterator upperEnd = upper+1;
+  int i = 0;
+  int ipoint = 0;
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    double lastKey = mKeyAxis->coordToPixel(it.key());
+    double value;
+    while (it != upperEnd)
+    {
+      if (pointData)
+      {
+        (*pointData)[ipoint] = it.value();
+        ++ipoint;
+      }
+      value = mValueAxis->coordToPixel(it.value().value);
+      (*lineData)[i].setX(value);
+      (*lineData)[i].setY(lastKey);
+      ++i;
+      lastKey = mKeyAxis->coordToPixel(it.key());
+      (*lineData)[i].setX(value);
+      (*lineData)[i].setY(lastKey);
+      ++i;
+      ++it;
+    }
+  } else // key axis is horizontal
+  {
+    double lastKey = mKeyAxis->coordToPixel(it.key());
+    double value;
+    while (it != upperEnd)
+    {
+      if (pointData)
+      {
+        (*pointData)[ipoint] = it.value();
+        ++ipoint;
+      }
+      value = mValueAxis->coordToPixel(it.value().value);
+      (*lineData)[i].setX(lastKey);
+      (*lineData)[i].setY(value);
+      ++i;
+      lastKey = mKeyAxis->coordToPixel(it.key());
+      (*lineData)[i].setX(lastKey);
+      (*lineData)[i].setY(value);
+      ++i;
+      ++it;
+    }
+  }
+}
+
+/*! 
+  \internal
+  Places the raw data points needed for a step plot with centered steps in \a lineData.
+
+  As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
+  points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
+  filling the vector.
+  \see drawLinePlot
+*/
+void QCPGraph::getStepCenterPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
+{
+  // get visible data range:
+  QCPDataMap::const_iterator lower, upper;
+  int dataCount;
+  getVisibleDataBounds(lower, upper, dataCount);
+  // prepare vectors:
+  if (lineData)
+  {
+    // added 2 to reserve memory for lower/upper fill base points that might be needed for base fill
+    // multiplied by 2 because step plot needs two polyline points per one actual data point
+    lineData->reserve(dataCount*2+2);
+    lineData->resize(dataCount*2);
+  }
+  if (pointData)
+    pointData->resize(dataCount);
+  
+  // position points:
+  QCPDataMap::const_iterator it = lower;
+  QCPDataMap::const_iterator upperEnd = upper+1;
+  int i = 0;
+  int ipoint = 0;
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    double lastKey = mKeyAxis->coordToPixel(it.key());
+    double lastValue = mValueAxis->coordToPixel(it.value().value);
+    double key;
+    if (pointData)
+    {
+      (*pointData)[ipoint] = it.value();
+      ++ipoint;
+    }
+    (*lineData)[i].setX(lastValue);
+    (*lineData)[i].setY(lastKey);
+    ++it;
+    ++i;
+    while (it != upperEnd)
+    {
+      if (pointData)
+      {
+        (*pointData)[ipoint] = it.value();
+        ++ipoint;
+      }
+      key = (mKeyAxis->coordToPixel(it.key())-lastKey)*0.5 + lastKey;
+      (*lineData)[i].setX(lastValue);
+      (*lineData)[i].setY(key);
+      ++i;
+      lastValue = mValueAxis->coordToPixel(it.value().value);
+      lastKey = mKeyAxis->coordToPixel(it.key());
+      (*lineData)[i].setX(lastValue);
+      (*lineData)[i].setY(key);
+      ++it;
+      ++i;
+    }
+    (*lineData)[i].setX(lastValue);
+    (*lineData)[i].setY(lastKey);
+  } else // key axis is horizontal
+  {
+    double lastKey = mKeyAxis->coordToPixel(it.key());
+    double lastValue = mValueAxis->coordToPixel(it.value().value);
+    double key;
+    if (pointData)
+    {
+      (*pointData)[ipoint] = it.value();
+      ++ipoint;
+    }
+    (*lineData)[i].setX(lastKey);
+    (*lineData)[i].setY(lastValue);
+    ++it;
+    ++i;
+    while (it != upperEnd)
+    {
+      if (pointData)
+      {
+        (*pointData)[ipoint] = it.value();
+        ++ipoint;
+      }
+      key = (mKeyAxis->coordToPixel(it.key())-lastKey)*0.5 + lastKey;
+      (*lineData)[i].setX(key);
+      (*lineData)[i].setY(lastValue);
+      ++i;
+      lastValue = mValueAxis->coordToPixel(it.value().value);
+      lastKey = mKeyAxis->coordToPixel(it.key());
+      (*lineData)[i].setX(key);
+      (*lineData)[i].setY(lastValue);
+      ++it;
+      ++i;
+    }
+    (*lineData)[i].setX(lastKey);
+    (*lineData)[i].setY(lastValue);
+  }
+}
+
+/*! 
+  \internal
+  Places the raw data points needed for an impulse plot in \a lineData.
+
+  As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
+  points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
+  filling the vector.
+  \see drawImpulsePlot
+*/
+void QCPGraph::getImpulsePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
+{
+  // get visible data range:
+  QCPDataMap::const_iterator lower, upper;
+  int dataCount;
+  getVisibleDataBounds(lower, upper, dataCount);
+  // prepare vectors:
+  if (lineData)
+  {
+    // no need to reserve 2 extra points, because there is no fill for impulse plot
+    lineData->resize(dataCount*2);
+  }
+  if (pointData)
+    pointData->resize(dataCount);
+  
+  // position data points:
+  QCPDataMap::const_iterator it = lower;
+  QCPDataMap::const_iterator upperEnd = upper+1;
+  int i = 0;
+  int ipoint = 0;
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    double zeroPointX = mValueAxis->coordToPixel(0);
+    double key;
+    while (it != upperEnd)
+    {
+      if (pointData)
+      {
+        (*pointData)[ipoint] = it.value();
+        ++ipoint;
+      }
+      key = mKeyAxis->coordToPixel(it.key());
+      (*lineData)[i].setX(zeroPointX);
+      (*lineData)[i].setY(key);
+      ++i;
+      (*lineData)[i].setX(mValueAxis->coordToPixel(it.value().value));
+      (*lineData)[i].setY(key);
+      ++i;
+      ++it;
+    }
+  } else // key axis is horizontal
+  {
+    double zeroPointY = mValueAxis->coordToPixel(0);
+    double key;
+    while (it != upperEnd)
+    {
+      if (pointData)
+      {
+        (*pointData)[ipoint] = it.value();
+        ++ipoint;
+      }
+      key = mKeyAxis->coordToPixel(it.key());
+      (*lineData)[i].setX(key);
+      (*lineData)[i].setY(zeroPointY);
+      ++i;
+      (*lineData)[i].setX(key);
+      (*lineData)[i].setY(mValueAxis->coordToPixel(it.value().value));
+      ++i;
+      ++it;
+    }
+  }
+}
+
+/*! 
+  \internal
+  Draws the fill of the graph with the specified brush. If the fill is a normal "base" fill, i.e.
+  under the graph toward the zero-value-line, only the \a lineData is required (and two extra points
+  at the zero-value-line, which are added by \ref addFillBasePoints and removed by \ref removeFillBasePoints
+  after the fill drawing is done).
+  
+  If the fill is a channel fill between this graph and another graph (mChannelFillGraph), the more complex
+  polygon is calculated with the \ref getChannelFillPolygon function.
+  \see drawLinePlot
+*/
+void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
+{
+  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
+  if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
+  
+  applyFillAntialiasingHint(painter);
+  if (!mChannelFillGraph)
+  {
+    // draw base fill under graph, fill goes all the way to the zero-value-line:
+    addFillBasePoints(lineData);
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(mainBrush());
+    painter->drawPolygon(QPolygonF(*lineData));
+    removeFillBasePoints(lineData);
+  } else
+  {
+    // draw channel fill between this graph and mChannelFillGraph:
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(mainBrush());
+    painter->drawPolygon(getChannelFillPolygon(lineData));
+  }
+}
+
+/*! \internal
+  
+  Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
+  the line style and are always drawn if scatter style is not \ref QCP::ssNone. Hence, the \a pointData vector
+  is outputted by all "get(...)PlotData" functions, together with the (line style dependent) line data.
+  \see drawLinePlot, drawImpulsePlot
+*/
+void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *pointData) const
+{
+  // draw error bars:
+  if (mErrorType != etNone)
+  {
+    applyErrorBarsAntialiasingHint(painter);
+    painter->setPen(mErrorPen);
+    if (mKeyAxis->orientation() == Qt::Vertical)
+    {
+      for (int i=0; i<pointData->size(); ++i)
+        drawError(painter, mValueAxis->coordToPixel(pointData->at(i).value), mKeyAxis->coordToPixel(pointData->at(i).key), pointData->at(i));
+    } else
+    {
+      for (int i=0; i<pointData->size(); ++i)
+        drawError(painter, mKeyAxis->coordToPixel(pointData->at(i).key), mValueAxis->coordToPixel(pointData->at(i).value), pointData->at(i));
+    }
+  }
+  
+  // draw scatter point symbols:
+  applyScattersAntialiasingHint(painter);
+  painter->setPen(mainPen());
+  painter->setBrush(mainBrush());
+  painter->setScatterPixmap(mScatterPixmap);
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    for (int i=0; i<pointData->size(); ++i)
+      painter->drawScatter(mValueAxis->coordToPixel(pointData->at(i).value), mKeyAxis->coordToPixel(pointData->at(i).key), mScatterSize, mScatterStyle);
+  } else
+  {
+    for (int i=0; i<pointData->size(); ++i)
+      painter->drawScatter(mKeyAxis->coordToPixel(pointData->at(i).key), mValueAxis->coordToPixel(pointData->at(i).value), mScatterSize, mScatterStyle);
+  }
+}
+
+/*! 
+  \internal
+  Draws line graphs from the provided data. It connects all points in \a lineData, which
+  was created by one of the "get(...)PlotData" functions for line styles that require simple line
+  connections between the point vector they create. These are for example \ref getLinePlotData, \ref
+  getStepLeftPlotData, \ref getStepRightPlotData and \ref getStepCenterPlotData.
+  \see drawScatterPlot, drawImpulsePlot
+*/
+void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
+{
+  // draw line of graph:
+  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mainPen());
+    painter->setBrush(Qt::NoBrush);
+    
+    /* Draws polyline in batches, currently not used:
+    int p = 0;
+    while (p < lineData->size())
+    {
+      int batch = qMin(25, lineData->size()-p);
+      if (p != 0)
+      {
+        ++batch;
+        --p; // to draw the connection lines between two batches
+      }
+      painter->drawPolyline(lineData->constData()+p, batch);
+      p += batch;
+    }
+    */
+    
+    // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
+    if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
+        painter->pen().style() == Qt::SolidLine &&
+        !painter->pdfExportMode())
+    {
+      for (int i=1; i<lineData->size(); ++i)
+        painter->drawLine(lineData->at(i-1), lineData->at(i));
+    } else
+    {  
+      painter->drawPolyline(QPolygonF(*lineData));
+    }
+  }
+}
+
+/*! 
+  \internal
+  Draws impulses graphs from the provided data, i.e. it connects all line pairs in \a lineData, which was
+  created by \ref getImpulsePlotData.
+  \see drawScatterPlot, drawLinePlot
+*/
+void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
+{
+  // draw impulses:
+  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
+  {
+    applyDefaultAntialiasingHint(painter);
+    QPen pen = mainPen();
+    pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
+    painter->setPen(pen);
+    painter->setBrush(Qt::NoBrush);
+    painter->drawLines(*lineData);
+  }
+}
+
+/*! 
+  \internal
+  called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
+  point. \a x and \a y pixel positions of the data point are passed since they are already known in
+  pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
+  data is therefore only used for the errors, not key and value.
+*/
+void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
+{
+  double a, b; // positions of error bar bounds in pixels
+  double barWidthHalf = mErrorBarSize*0.5;
+  double skipSymbolMargin = mScatterSize*1.25; // pixels left blank per side, when mErrorBarSkipSymbol is true
+
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    // draw key error vertically and value error horizontally
+    if (mErrorType == etKey || mErrorType == etBoth)
+    {
+      a = mKeyAxis->coordToPixel(data.key-data.keyErrorMinus);
+      b = mKeyAxis->coordToPixel(data.key+data.keyErrorPlus);
+      if (mKeyAxis->rangeReversed())
+        qSwap(a,b);
+      // draw spine:
+      if (mErrorBarSkipSymbol)
+      {
+        if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
+          painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
+        if (y-b > skipSymbolMargin) 
+          painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
+      } else
+        painter->drawLine(QLineF(x, a, x, b));
+      // draw handles:
+      painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
+      painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
+    }
+    if (mErrorType == etValue || mErrorType == etBoth)
+    {
+      a = mValueAxis->coordToPixel(data.value-data.valueErrorMinus);
+      b = mValueAxis->coordToPixel(data.value+data.valueErrorPlus);
+      if (mValueAxis->rangeReversed())
+        qSwap(a,b);
+      // draw spine:
+      if (mErrorBarSkipSymbol)
+      {
+        if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
+          painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
+        if (b-x > skipSymbolMargin)
+          painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
+      } else
+        painter->drawLine(QLineF(a, y, b, y));
+      // draw handles:
+      painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
+      painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
+    }
+  } else // mKeyAxis->orientation() is Qt::Horizontal
+  {
+    // draw value error vertically and key error horizontally
+    if (mErrorType == etKey || mErrorType == etBoth)
+    {
+      a = mKeyAxis->coordToPixel(data.key-data.keyErrorMinus);
+      b = mKeyAxis->coordToPixel(data.key+data.keyErrorPlus);
+      if (mKeyAxis->rangeReversed())
+        qSwap(a,b);
+      // draw spine:
+      if (mErrorBarSkipSymbol)
+      {
+        if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
+          painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
+        if (b-x > skipSymbolMargin)
+          painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
+      } else
+        painter->drawLine(QLineF(a, y, b, y));
+      // draw handles:
+      painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
+      painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
+    }
+    if (mErrorType == etValue || mErrorType == etBoth)
+    {
+      a = mValueAxis->coordToPixel(data.value-data.valueErrorMinus);
+      b = mValueAxis->coordToPixel(data.value+data.valueErrorPlus);
+      if (mValueAxis->rangeReversed())
+        qSwap(a,b);
+      // draw spine:
+      if (mErrorBarSkipSymbol)
+      {
+        if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
+          painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
+        if (y-b > skipSymbolMargin)
+          painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
+      } else
+        painter->drawLine(QLineF(x, a, x, b));
+      // draw handles:
+      painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
+      painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
+    }
+  }
+}
+
+/*! 
+  \internal
+  called by the specific plot data generating functions "get(...)PlotData" to determine
+  which data range is visible, so only that needs to be processed.
+  
+  \param[out] lower returns an iterator to the lowest data point that needs to be taken into account
+  when plotting. Note that in order to get a clean plot all the way to the edge of the axes, \a lower
+  may still be outside the visible range.
+  \param[out] upper returns an iterator to the highest data point. Same as before, \a upper may also
+  lie outside of the visible range.
+  \param[out] count number of data points that need plotting, i.e. points between \a lower and \a upper,
+  including them. This is useful for allocating the array of QPointFs in the specific drawing functions.
+*/
+void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper, int &count) const
+{
+  // get visible data range as QMap iterators
+  QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis->range().lower);
+  QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis->range().upper)-1;
+  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
+  bool highoutlier = ubound+1 != mData->constEnd(); // indicates whether there exist points above axis range
+  lower = (lowoutlier ? lbound-1 : lbound); // data pointrange that will be actually drawn
+  upper = (highoutlier ? ubound+1 : ubound); // data pointrange that will be actually drawn
+  
+  // count number of points in range lower to upper (including them), so we can allocate array for them in draw functions:
+  QCPDataMap::const_iterator it = lower;
+  count = 1;
+  while (it != upper)
+  {
+    ++it;
+    ++count;
+  }
+}
+
+/*! 
+  \internal
+  The line data vector generated by e.g. getLinePlotData contains only the line
+  that connects the data points. If the graph needs to be filled, two additional points
+  need to be added at the value-zero-line in the lower and upper key positions, the graph
+  reaches. This function calculates these points and adds them to the end of \a lineData.
+  Since the fill is typically drawn before the line stroke, these added points need to
+  be removed again after the fill is done, with the removeFillBasePoints function.
+  
+  The expanding of \a lineData by two points will not cause unnecessary memory reallocations,
+  because the data vector generation functions (getLinePlotData etc.) reserve two extra points
+  when they allocate memory for \a lineData.
+  \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint
+*/
+void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
+{
+  // append points that close the polygon fill at the key axis:
+  if (mKeyAxis->orientation() == Qt::Vertical)
+  {
+    *lineData << upperFillBasePoint(lineData->last().y());
+    *lineData << lowerFillBasePoint(lineData->first().y());
+  } else
+  {
+    *lineData << upperFillBasePoint(lineData->last().x());
+    *lineData << lowerFillBasePoint(lineData->first().x());
+  }
+}
+
+/*! 
+  \internal
+  removes the two points from \a lineData that were added by addFillBasePoints.
+  \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint
+*/
+void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
+{
+  lineData->remove(lineData->size()-2, 2);
+}
+
+/*! 
+  \internal
+  called by addFillBasePoints to conveniently assign the point which closes the fill
+  polygon on the lower side of the zero-value-line parallel to the key axis.
+  The logarithmic axis scale case is a bit special, since the zero-value-line in pixel coordinates
+  is in positive or negative infinity. So this case is handled separately by just closing the
+  fill polygon on the axis which lies in the direction towards the zero value.
+  
+  \param lowerKey pixel position of the lower key of the point. Depending on whether the key axis
+  is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned point,
+  respectively.
+  \see upperFillBasePoint, addFillBasePoints
+*/
+QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
+{
+  QPointF point;
+  if (mValueAxis->scaleType() == QCPAxis::stLinear)
+  {
+    if (mKeyAxis->axisType() == QCPAxis::atLeft)
+    {
+      point.setX(mValueAxis->coordToPixel(0));
+      point.setY(lowerKey);
+    } else if (mKeyAxis->axisType() == QCPAxis::atRight)
+    {
+      point.setX(mValueAxis->coordToPixel(0));
+      point.setY(lowerKey);
+    } else if (mKeyAxis->axisType() == QCPAxis::atTop)
+    {
+      point.setX(lowerKey);
+      point.setY(mValueAxis->coordToPixel(0));
+    } else if (mKeyAxis->axisType() == QCPAxis::atBottom)
+    {
+      point.setX(lowerKey);
+      point.setY(mValueAxis->coordToPixel(0));
+    }
+  } else // mValueAxis->mScaleType == QCPAxis::stLogarithmic
+  {
+    // In logarithmic scaling we can't just draw to value zero so we just fill all the way
+    // to the axis which is in the direction towards zero
+    if (mKeyAxis->orientation() == Qt::Vertical)
+    {
+      if ((mValueAxis->range().upper < 0 && !mValueAxis->rangeReversed()) ||
+          (mValueAxis->range().upper > 0 && mValueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
+        point.setX(mKeyAxis->axisRect().right());
+      else
+        point.setX(mKeyAxis->axisRect().left());
+      point.setY(lowerKey);
+    } else if (mKeyAxis->axisType() == QCPAxis::atTop || mKeyAxis->axisType() == QCPAxis::atBottom)
+    {
+      point.setX(lowerKey);
+      if ((mValueAxis->range().upper < 0 && !mValueAxis->rangeReversed()) ||
+          (mValueAxis->range().upper > 0 && mValueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
+        point.setY(mKeyAxis->axisRect().top());
+      else
+        point.setY(mKeyAxis->axisRect().bottom());
+    }
+  }
+  return point;
+}
+
+/*! 
+  \internal
+  called by addFillBasePoints to conveniently assign the point which closes the fill
+  polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis
+  scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or
+  negative infinity. So this case is handled separately by just closing the fill polygon on the
+  axis which lies in the direction towards the zero value.
+  
+  \param upperKey pixel position of the upper key of the point. Depending on whether the key axis
+  is horizontal or vertical, \a upperKey will end up as the x or y value of the returned point,
+  respectively.
+  \see lowerFillBasePoint, addFillBasePoints
+*/
+QPointF QCPGraph::upperFillBasePoint(double upperKey) const
+{
+  QPointF point;
+  if (mValueAxis->scaleType() == QCPAxis::stLinear)
+  {
+    if (mKeyAxis->axisType() == QCPAxis::atLeft)
+    {
+      point.setX(mValueAxis->coordToPixel(0));
+      point.setY(upperKey);
+    } else if (mKeyAxis->axisType() == QCPAxis::atRight)
+    {
+      point.setX(mValueAxis->coordToPixel(0));
+      point.setY(upperKey);
+    } else if (mKeyAxis->axisType() == QCPAxis::atTop)
+    {
+      point.setX(upperKey);
+      point.setY(mValueAxis->coordToPixel(0));
+    } else if (mKeyAxis->axisType() == QCPAxis::atBottom)
+    {
+      point.setX(upperKey);
+      point.setY(mValueAxis->coordToPixel(0));
+    }
+  } else // mValueAxis->mScaleType == QCPAxis::stLogarithmic
+  {
+    // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
+    // to the axis which is in the direction towards 0
+    if (mKeyAxis->orientation() == Qt::Vertical)
+    {
+      if ((mValueAxis->range().upper < 0 && !mValueAxis->rangeReversed()) ||
+          (mValueAxis->range().upper > 0 && mValueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
+        point.setX(mKeyAxis->axisRect().right());
+      else
+        point.setX(mKeyAxis->axisRect().left());
+      point.setY(upperKey);
+    } else if (mKeyAxis->axisType() == QCPAxis::atTop || mKeyAxis->axisType() == QCPAxis::atBottom)
+    {
+      point.setX(upperKey);
+      if ((mValueAxis->range().upper < 0 && !mValueAxis->rangeReversed()) ||
+          (mValueAxis->range().upper > 0 && mValueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
+        point.setY(mKeyAxis->axisRect().top());
+      else
+        point.setY(mKeyAxis->axisRect().bottom());
+    }
+  }
+  return point;
+}
+
+/*! \internal
+  
+  Generates the polygon needed for drawing channel fills between this graph (data passed via \a
+  lineData) and the graph specified by mChannelFillGraph (data generated by calling its \ref
+  getPlotData function). May return an empty polygon if the key ranges have no overlap or fill
+  target graph and this graph don't have same orientation (i.e. both key axes horizontal or both
+  key axes vertical). For increased performance (due to implicit sharing), keep the returned QPolygonF
+  const.
+*/
+const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
+{
+  if (mChannelFillGraph->mKeyAxis->orientation() != mKeyAxis->orientation())
+    return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
+  
+  if (lineData->isEmpty()) return QPolygonF();
+  QVector<QPointF> otherData;
+  mChannelFillGraph->getPlotData(&otherData, 0);
+  if (otherData.isEmpty()) return QPolygonF();
+  QVector<QPointF> thisData;
+  thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
+  for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector),  it squeezes internally, which ruins the performance tuning with reserve()
+    thisData << lineData->at(i);
+  
+  // pointers to be able to swap them, depending which data range needs cropping:
+  QVector<QPointF> *staticData = &thisData;
+  QVector<QPointF> *croppedData = &otherData;
+  
+  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
+  if (mKeyAxis->orientation() == Qt::Horizontal)
+  {
+    // x is key
+    // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
+    if (staticData->first().x() > staticData->last().x())
+    {
+      int size = staticData->size();
+      for (int i=0; i<size/2; ++i)
+        qSwap((*staticData)[i], (*staticData)[size-1-i]);
+    }
+    if (croppedData->first().x() > croppedData->last().x())
+    {
+      int size = croppedData->size();
+      for (int i=0; i<size/2; ++i)
+        qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
+    }
+    // crop lower bound:
+    if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
+      qSwap(staticData, croppedData);
+    int lowBound = findIndexBelowX(croppedData, staticData->first().x());
+    if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
+    croppedData->remove(0, lowBound);
+    // set lowest point of cropped data to fit exactly key position of first static data
+    // point via linear interpolation:
+    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
+    double slope;
+    if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
+      slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
+    else
+      slope = 0;
+    (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
+    (*croppedData)[0].setX(staticData->first().x());
+    
+    // crop upper bound:
+    if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
+      qSwap(staticData, croppedData);
+    int highBound = findIndexAboveX(croppedData, staticData->last().x());
+    if (highBound == -1) return QPolygonF(); // key ranges have no overlap
+    croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
+    // set highest point of cropped data to fit exactly key position of last static data
+    // point via linear interpolation:
+    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
+    int li = croppedData->size()-1; // last index
+    if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
+      slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
+    else
+      slope = 0;
+    (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
+    (*croppedData)[li].setX(staticData->last().x());
+  } else // mKeyAxis->orientation() == Qt::Vertical
+  {
+    // y is key
+    // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
+    // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
+    // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
+    if (staticData->first().y() < staticData->last().y())
+    {
+      int size = staticData->size();
+      for (int i=0; i<size/2; ++i)
+        qSwap((*staticData)[i], (*staticData)[size-1-i]);
+    }
+    if (croppedData->first().y() < croppedData->last().y())
+    {
+      int size = croppedData->size();
+      for (int i=0; i<size/2; ++i)
+        qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
+    }
+    // crop lower bound:
+    if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
+      qSwap(staticData, croppedData);
+    int lowBound = findIndexAboveY(croppedData, staticData->first().y());
+    if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
+    croppedData->remove(0, lowBound);
+    // set lowest point of cropped data to fit exactly key position of first static data
+    // point via linear interpolation:
+    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
+    double slope;
+    if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
+      slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
+    else
+      slope = 0;
+    (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
+    (*croppedData)[0].setY(staticData->first().y());
+    
+    // crop upper bound:
+    if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
+      qSwap(staticData, croppedData);
+    int highBound = findIndexBelowY(croppedData, staticData->last().y());
+    if (highBound == -1) return QPolygonF(); // key ranges have no overlap
+    croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
+    // set highest point of cropped data to fit exactly key position of last static data
+    // point via linear interpolation:
+    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
+    int li = croppedData->size()-1; // last index
+    if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
+      slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
+    else
+      slope = 0;
+    (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
+    (*croppedData)[li].setY(staticData->last().y());
+  }
+  
+  // return joined:
+  for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
+    thisData << otherData.at(i);
+  return QPolygonF(thisData);
+}
+
+/*! \internal
+  
+  Finds the smallest index of \a data, whose points x value is just above \a x.
+  Assumes x values in \a data points are ordered ascending, as is the case
+  when plotting with horizontal key axis.
+  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
+*/
+int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
+{
+  for (int i=data->size()-1; i>=0; --i)
+  {
+    if (data->at(i).x() < x)
+    {
+      if (i<data->size()-1)
+        return i+1;
+      else
+        return data->size()-1;
+    }
+  }
+  return -1;
+}
+
+/*! \internal
+  
+  Finds the greatest index of \a data, whose points x value is just below \a x.
+  Assumes x values in \a data points are ordered ascending, as is the case
+  when plotting with horizontal key axis.
+  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
+*/
+int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
+{
+  for (int i=0; i<data->size(); ++i)
+  {
+    if (data->at(i).x() > x)
+    {
+      if (i>0)
+        return i-1;
+      else
+        return 0;
+    }
+  }
+  return -1;
+}
+
+/*! \internal
+  
+  Finds the smallest index of \a data, whose points y value is just above \a y.
+  Assumes y values in \a data points are ordered descending, as is the case
+  when plotting with vertical key axis.
+  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
+*/
+int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
+{
+  for (int i=0; i<data->size(); ++i)
+  {
+    if (data->at(i).y() < y)
+    {
+      if (i>0)
+        return i-1;
+      else
+        return 0;
+    }
+  }
+  return -1;
+}
+
+/*! \internal 
+  
+  Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a
+  pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in
+  \ref selectTest.
+*/
+double QCPGraph::pointDistance(const QPointF &pixelPoint) const
+{
+  if (mData->isEmpty())
+  {
+    qDebug() << Q_FUNC_INFO << "requested point distance on graph" << mName << "without data";
+    return 500;
+  }
+  if (mData->size() == 1)
+  {
+    QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
+    return QVector2D(dataPoint-pixelPoint).length();
+  }
+  
+  if (mLineStyle == lsNone && mScatterStyle == QCP::ssNone)
+    return 500;
+  
+  // calculate minimum distances to graph representation:
+  if (mLineStyle == lsNone)
+  {
+    // no line displayed, only calculate distance to scatter points:
+    QVector<QCPData> *pointData = new QVector<QCPData>;
+    getScatterPlotData(pointData);
+    double minDistSqr = std::numeric_limits<double>::max();
+    QPointF ptA;
+    QPointF ptB = coordsToPixels(pointData->at(0).key, pointData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
+    for (int i=1; i<pointData->size(); ++i)
+    {
+      ptA = ptB;
+      ptB = coordsToPixels(pointData->at(i).key, pointData->at(i).value);
+      double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
+      if (currentDistSqr < minDistSqr)
+        minDistSqr = currentDistSqr;
+    }
+    delete pointData;
+    return sqrt(minDistSqr);
+  } else
+  {
+    // line displayed calculate distance to line segments:
+    QVector<QPointF> *lineData = new QVector<QPointF>;
+    getPlotData(lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
+    double minDistSqr = std::numeric_limits<double>::max();
+    if (mLineStyle == lsImpulse)
+    {
+      // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
+      for (int i=0; i<lineData->size()-1; i+=2) // iterate pairs
+      {
+        double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
+        if (currentDistSqr < minDistSqr)
+          minDistSqr = currentDistSqr;
+      }
+    } else 
+    {
+      // all other line plots (line and step) connect points directly:
+      for (int i=0; i<lineData->size()-1; ++i)
+      {
+        double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
+        if (currentDistSqr < minDistSqr)
+          minDistSqr = currentDistSqr;
+      }
+    }
+    delete lineData;
+    return sqrt(minDistSqr);
+  }
+}
+
+/*! \internal
+  
+  Finds the greatest index of \a data, whose points y value is just below \a y.
+  Assumes y values in \a data points are ordered descending, as is the case
+  when plotting with vertical key axis (since keys are ordered ascending).
+  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
+*/
+int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
+{
+  for (int i=data->size()-1; i>=0; --i)
+  {
+    if (data->at(i).y() > y)
+    {
+      if (i<data->size()-1)
+        return i+1;
+      else
+        return data->size()-1;
+    }
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain) const
+{
+  // just call the specialized version which takes an additional argument whether error bars
+  // should also be taken into consideration for range calculation. We set this to true here.
+  return getKeyRange(validRange, inSignDomain, true);
+}
+
+/* inherits documentation from base class */
+QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain) const
+{
+  // just call the specialized version which takes an additional argument whether error bars
+  // should also be taken into consideration for range calculation. We set this to true here.
+  return getValueRange(validRange, inSignDomain, true);
+}
+
+/*! \overload
+  Allows to specify whether the error bars should be included in the range calculation.
+  
+  \see getKeyRange(bool &validRange, SignDomain inSignDomain)
+*/
+QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+  
+  double current, currentErrorMinus, currentErrorPlus;
+  
+  if (inSignDomain == sdBoth) // range may be anywhere
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().key;
+      currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
+      currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
+      if (current-currentErrorMinus < range.lower || !haveLower)
+      {
+        range.lower = current-currentErrorMinus;
+        haveLower = true;
+      }
+      if (current+currentErrorPlus > range.upper || !haveUpper)
+      {
+        range.upper = current+currentErrorPlus;
+        haveUpper = true;
+      }
+      ++it;
+    }
+  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().key;
+      currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
+      currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
+      if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
+      {
+        range.lower = current-currentErrorMinus;
+        haveLower = true;
+      }
+      if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
+      {
+        range.upper = current+currentErrorPlus;
+        haveUpper = true;
+      }
+      if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
+      {
+        if ((current < range.lower || !haveLower) && current < 0)
+        {
+          range.lower = current;
+          haveLower = true;
+        }
+        if ((current > range.upper || !haveUpper) && current < 0)
+        {
+          range.upper = current;
+          haveUpper = true;
+        }
+      }
+      ++it;
+    }
+  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().key;
+      currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
+      currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
+      if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
+      {
+        range.lower = current-currentErrorMinus;
+        haveLower = true;
+      }
+      if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
+      {
+        range.upper = current+currentErrorPlus;
+        haveUpper = true;
+      }
+      if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
+      {
+        if ((current < range.lower || !haveLower) && current > 0)
+        {
+          range.lower = current;
+          haveLower = true;
+        }
+        if ((current > range.upper || !haveUpper) && current > 0)
+        {
+          range.upper = current;
+          haveUpper = true;
+        }
+      }
+      ++it;
+    }
+  }
+  
+  validRange = haveLower && haveUpper;
+  return range;
+}
+
+/*! \overload
+  Allows to specify whether the error bars should be included in the range calculation.
+  
+  \see getValueRange(bool &validRange, SignDomain inSignDomain)
+*/
+QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+  
+  double current, currentErrorMinus, currentErrorPlus;
+  
+  if (inSignDomain == sdBoth) // range may be anywhere
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().value;
+      currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
+      currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
+      if (current-currentErrorMinus < range.lower || !haveLower)
+      {
+        range.lower = current-currentErrorMinus;
+        haveLower = true;
+      }
+      if (current+currentErrorPlus > range.upper || !haveUpper)
+      {
+        range.upper = current+currentErrorPlus;
+        haveUpper = true;
+      }
+      ++it;
+    }
+  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().value;
+      currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
+      currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
+      if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
+      {
+        range.lower = current-currentErrorMinus;
+        haveLower = true;
+      }
+      if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
+      {
+        range.upper = current+currentErrorPlus;
+        haveUpper = true;
+      }
+      if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
+      {
+        if ((current < range.lower || !haveLower) && current < 0)
+        {
+          range.lower = current;
+          haveLower = true;
+        }
+        if ((current > range.upper || !haveUpper) && current < 0)
+        {
+          range.upper = current;
+          haveUpper = true;
+        }
+      }
+      ++it;
+    }
+  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().value;
+      currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
+      currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
+      if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
+      {
+        range.lower = current-currentErrorMinus;
+        haveLower = true;
+      }
+      if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
+      {
+        range.upper = current+currentErrorPlus;
+        haveUpper = true;
+      }
+      if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
+      {
+        if ((current < range.lower || !haveLower) && current > 0)
+        {
+          range.lower = current;
+          haveLower = true;
+        }
+        if ((current > range.upper || !haveUpper) && current > 0)
+        {
+          range.upper = current;
+          haveUpper = true;
+        }
+      }
+      ++it;
+    }
+  }
+  
+  validRange = haveLower && haveUpper;
+  return range;
+}
+
+
+// ================================================================================
+// =================== QCPRange
+// ================================================================================
+/*! \class QCPRange
+  \brief Represents the range an axis is encompassing.
+  
+  contains a \a lower and \a upper double value and provides convenience input, output and
+  modification functions.
+  
+  \see QCPAxis::setRange
+*/
+
+/*! 
+  Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
+  intervals would cause errors due to the 11-bit exponent of double precision numbers,
+  corresponding to a minimum magnitude of roughly 1e-308.
+  \see validRange, maxRange
+*/
+const double QCPRange::minRange = 1e-280;
+
+/*! 
+  Maximum values (negative and positive) the range will accept in range-changing functions.
+  Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
+  corresponding to a maximum magnitude of roughly 1e308.
+  Since the number of planck-volumes in the entire visible universe is only ~1e183, this should
+  be enough.
+  \see validRange, minRange
+*/
+const double QCPRange::maxRange = 1e250;
+
+/*! 
+  Constructs a range with \a lower and \a upper set to zero.
+*/
+QCPRange::QCPRange() :
+  lower(0),
+  upper(0)
+{
+}
+
+/*! \overload
+  Constructs a range with the specified \a lower and \a upper values.
+*/
+QCPRange::QCPRange(double lower, double upper) :
+  lower(lower),
+  upper(upper)
+{
+  normalize();
+}
+
+/*! 
+  Returns the size of the range, i.e. \a upper-\a lower
+*/
+double QCPRange::size() const
+{
+  return upper-lower;
+}
+
+/*! 
+  Returns the center of the range, i.e. (\a upper+\a lower)*0.5
+*/
+double QCPRange::center() const
+{
+  return (upper+lower)*0.5;
+}
+
+/*! 
+  Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values
+  are swapped.
+*/
+void QCPRange::normalize()
+{
+  if (lower > upper)
+    qSwap(lower, upper);
+}
+
+/*! 
+  Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
+  the range won't span the positive and negative sign domain, i.e. contain zero. Further
+  \a lower will always be numerically smaller (or equal) to \a upper.
+  
+  If the original range does span positive and negative sign domains or contains zero,
+  the returned range will try to approximate the original range as good as possible.
+  If the positive interval of the original range is wider than the negative interval, the
+  returned range will only contain the positive interval, with lower bound set to \a rangeFac or
+  \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval
+  is wider than the positive interval, this time by changing the \a upper bound.
+*/
+QCPRange QCPRange::sanitizedForLogScale() const
+{
+  double rangeFac = 1e-3;
+  QCPRange sanitizedRange(lower, upper);
+  sanitizedRange.normalize();
+  // can't have range spanning negative and positive values in log plot, so change range to fix it
+  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
+  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
+  {
+    // case lower is 0
+    if (rangeFac < sanitizedRange.upper*rangeFac)
+      sanitizedRange.lower = rangeFac;
+    else
+      sanitizedRange.lower = sanitizedRange.upper*rangeFac;
+  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
+  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
+  {
+    // case upper is 0
+    if (-rangeFac > sanitizedRange.lower*rangeFac)
+      sanitizedRange.upper = -rangeFac;
+    else
+      sanitizedRange.upper = sanitizedRange.lower*rangeFac;
+  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
+  {
+    // find out whether negative or positive interval is wider to decide which sign domain will be chosen
+    if (-sanitizedRange.lower > sanitizedRange.upper)
+    {
+      // negative is wider, do same as in case upper is 0
+      if (-rangeFac > sanitizedRange.lower*rangeFac)
+        sanitizedRange.upper = -rangeFac;
+      else
+        sanitizedRange.upper = sanitizedRange.lower*rangeFac;
+    } else
+    {
+      // positive is wider, do same as in case lower is 0
+      if (rangeFac < sanitizedRange.upper*rangeFac)
+        sanitizedRange.lower = rangeFac;
+      else
+        sanitizedRange.lower = sanitizedRange.upper*rangeFac;
+    }
+  }
+  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
+  return sanitizedRange;
+}
+
+/*! 
+  Returns a sanitized version of the range. Sanitized means for linear scales, that
+  \a lower will always be numerically smaller (or equal) to \a upper.
+*/
+QCPRange QCPRange::sanitizedForLinScale() const
+{
+  QCPRange sanitizedRange(lower, upper);
+  sanitizedRange.normalize();
+  return sanitizedRange;
+}
+
+/*! 
+  Returns true when \a value lies within or exactly on the borders of the range.
+*/
+bool QCPRange::contains(double value) const
+{
+  return value >= lower && value <= upper;
+}
+
+/*! 
+  Checks, whether the specified range is within valid bounds, which are defined
+  as QCPRange::maxRange and QCPRange::minRange.
+  A valid range means:
+  \li range bounds within -maxRange and maxRange
+  \li range size above minRange
+  \li range size below maxRange
+*/
+bool QCPRange::validRange(double lower, double upper)
+{
+  /*
+  return (lower > -maxRange &&
+          upper < maxRange &&
+          qAbs(lower-upper) > minRange &&
+          (lower < -minRange || lower > minRange) &&
+          (upper < -minRange || upper > minRange));
+          */
+  return (lower > -maxRange &&
+          upper < maxRange &&
+          qAbs(lower-upper) > minRange &&
+          qAbs(lower-upper) < maxRange);
+}
+
+/*! 
+  \overload
+  Checks, whether the specified range is within valid bounds, which are defined
+  as QCPRange::maxRange and QCPRange::minRange.
+  A valid range means:
+  \li range bounds within -maxRange and maxRange
+  \li range size above minRange
+  \li range size below maxRange
+*/
+bool QCPRange::validRange(const QCPRange &range)
+{
+  /*
+  return (range.lower > -maxRange &&
+          range.upper < maxRange &&
+          qAbs(range.lower-range.upper) > minRange &&
+          qAbs(range.lower-range.upper) < maxRange &&
+          (range.lower < -minRange || range.lower > minRange) &&
+          (range.upper < -minRange || range.upper > minRange));
+          */
+  return (range.lower > -maxRange &&
+          range.upper < maxRange &&
+          qAbs(range.lower-range.upper) > minRange &&
+          qAbs(range.lower-range.upper) < maxRange);
+}
+
+
+// ================================================================================
+// =================== QCPLegend
+// ================================================================================
+
+/*! \class QCPLegend
+  \brief Manages a legend inside a QCustomPlot.
+
+  Doesn't need to be instantiated externally, rather access QCustomPlot::legend
+*/
+
+/* start of documentation of signals */
+
+/*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
+
+  This signal is emitted when the selection state of this legend has changed.
+  
+  \see setSelected, setSelectable
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default
+  values. Under normal usage, QCPLegend needn't be instantiated outside of QCustomPlot.
+  Access QCustomPlot::legend to modify the legend (set to invisible by default, see \ref
+  setVisible).
+*/
+QCPLegend::QCPLegend(QCustomPlot *parentPlot) :
+  QCPLayerable(parentPlot)
+{
+  setAntialiased(false);
+  setPositionStyle(psTopRight);
+  setSize(100, 28);
+  setMinimumSize(100, 0);
+  setIconSize(32, 18);
+  setAutoSize(true);
+  
+  setMargin(12, 12, 12, 12);
+  setPadding(8, 8, 3, 3);
+  setItemSpacing(3);
+  setIconTextPadding(7);
+  
+  setSelectable(spLegendBox | spItems);
+  setSelected(spNone);
+  
+  setBorderPen(QPen(Qt::black));
+  setSelectedBorderPen(QPen(Qt::blue, 2));
+  setIconBorderPen(Qt::NoPen);
+  setSelectedIconBorderPen(QPen(Qt::blue, 2));
+  setBrush(Qt::white);
+  setSelectedBrush(Qt::white);
+  setFont(parentPlot->font());
+  setSelectedFont(parentPlot->font());
+  setTextColor(Qt::black);
+  setSelectedTextColor(Qt::blue);
+}
+
+QCPLegend::~QCPLegend()
+{
+  clearItems();
+}
+
+/*!
+  Sets the pen, the border of the entire legend is drawn with.
+*/
+void QCPLegend::setBorderPen(const QPen &pen)
+{
+  mBorderPen = pen;
+}
+
+/*!
+  Sets the brush of the legend background.
+*/
+void QCPLegend::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
+  use this font by default. However, a different font can be specified on a per-item-basis by
+  accessing the specific legend item.
+  
+  This function will also set \a font on all already existing legend items.
+  
+  \see QCPAbstractLegendItem::setFont
+*/
+void QCPLegend::setFont(const QFont &font)
+{
+  mFont = font;
+  for (int i=0; i<mItems.size(); ++i)
+    mItems.at(i)->setFont(mFont);
+}
+
+/*!
+  Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
+  will use this color by default. However, a different colors can be specified on a per-item-basis
+  by accessing the specific legend item.
+  
+  This function will also set \a color on all already existing legend items.
+  
+  \see QCPAbstractLegendItem::setTextColor
+*/
+void QCPLegend::setTextColor(const QColor &color)
+{
+  mTextColor = color;
+  for (int i=0; i<mItems.size(); ++i)
+    mItems.at(i)->setTextColor(color);
+}
+
+/*!
+  Sets the position style of the legend. If the \a legendPositionStyle is not \ref psManual, the
+  position is found automatically depending on the specific \a legendPositionStyle and the
+  legend margins. If \a legendPositionStyle is \ref psManual, the exact pixel position of the
+  legend must be specified via \ref setPosition. Margins have no effect in that case.
+  \see setMargin
+*/
+void QCPLegend::setPositionStyle(PositionStyle legendPositionStyle)
+{
+  mPositionStyle = legendPositionStyle;
+}
+
+/*!
+  Sets the exact pixel Position of the legend inside the QCustomPlot widget, if \ref
+  setPositionStyle is set to \ref psManual. Margins have no effect in that case.
+*/
+void QCPLegend::setPosition(const QPoint &pixelPosition)
+{
+  mPosition = pixelPosition;
+}
+
+/*!
+  Sets whether the size of the legend should be calculated automatically to fit all the content
+  (plus padding), or whether the size must be specified manually with \ref setSize.
+  
+  If the autoSize mechanism is enabled, the legend will have the smallest possible size to still
+  display all its content. For items with text wrapping (QCPPlottableLegendItem::setTextWrap) this
+  means, they would become very compressed, i.e. wrapped at every word. To prevent this, set a
+  reasonable \ref setMinimumSize width.
+*/
+void QCPLegend::setAutoSize(bool on)
+{
+  mAutoSize = on;
+}
+
+/*!
+  Sets the size of the legend. Setting the size manually with this function only has an effect, if
+  \ref setAutoSize is set to false.
+  
+  If you want to control the minimum size (or the text-wrapping width) while still leaving the
+  autoSize mechanism enabled, consider using \ref setMinimumSize.
+  
+  \see setAutoSize, setMinimumSize
+*/
+void QCPLegend::setSize(const QSize &size)
+{
+  mSize = size;
+}
+
+/*! \overload
+*/
+void QCPLegend::setSize(int width, int height)
+{
+  mSize = QSize(width, height);
+}
+
+/*!
+  Sets the minimum size of the legend when \ref setAutoSize is enabled.
+  
+  If text wrapping is enabled in the legend items (e.g. \ref QCPPlottableLegendItem::setTextWrap), this minimum \a size defines the width
+  at which the wrapping will occur. Note that the wrapping will happen only at word boundaries, so the actual size might
+  still be bigger than the \a size given here, but not smaller.
+  
+  If \ref setAutoSize is not enabled, the minimum \a size is ignored. Setting a smaller legend size with \ref setSize manually, is not prevented.
+  
+  \see setAutoSize, setSize, QCPPlottableLegendItem::setTextWrap
+*/
+void QCPLegend::setMinimumSize(const QSize &size)
+{
+  mMinimumSize = size;
+}
+
+/*! \overload
+*/
+void QCPLegend::setMinimumSize(int width, int height)
+{
+  mMinimumSize = QSize(width, height);
+}
+
+/*!
+  Sets the left padding of the legend. Padding is the space by what the legend box is made larger
+  than minimally needed for the content to fit. I.e. it's the space left blank on each side inside
+  the legend.
+*/
+void QCPLegend::setPaddingLeft(int padding)
+{
+  mPaddingLeft = padding;
+}
+
+/*!
+  Sets the right padding of the legend. Padding is the space by what the legend box is made larger
+  than minimally needed for the content to fit. I.e. it's the space left blank on each side inside
+  the legend.
+*/
+void QCPLegend::setPaddingRight(int padding)
+{
+  mPaddingRight = padding;
+}
+
+/*!
+  Sets the top padding of the legend. Padding is the space by what the legend box is made larger
+  than minimally needed for the content to fit. I.e. it's the space left blank on each side inside
+  the legend.
+*/
+void QCPLegend::setPaddingTop(int padding)
+{
+  mPaddingTop = padding;
+}
+
+/*!
+  Sets the bottom padding of the legend. Padding is the space by what the legend box is made larger
+  than minimally needed for the content to fit. I.e. it's the space left blank on each side inside
+  the legend.
+*/
+void QCPLegend::setPaddingBottom(int padding)
+{
+  mPaddingBottom = padding;
+}
+
+/*!
+  Sets the padding of the legend. Padding is the space by what the legend box is made larger than
+  minimally needed for the content to fit. I.e. it's the space left blank on each side inside the
+  legend.
+*/
+void QCPLegend::setPadding(int left, int right, int top, int bottom)
+{
+  mPaddingLeft = left;
+  mPaddingRight = right;
+  mPaddingTop = top;
+  mPaddingBottom = bottom;
+}
+
+/*!
+  Sets the left margin of the legend. Margins are the distances the legend will keep to the axis
+  rect, when \ref setPositionStyle is not \ref psManual.
+*/
+void QCPLegend::setMarginLeft(int margin)
+{
+  mMarginLeft = margin;
+}
+
+/*!
+  Sets the right margin of the legend. Margins are the distances the legend will keep to the axis
+  rect, when \ref setPositionStyle is not \ref psManual.
+*/
+void QCPLegend::setMarginRight(int margin)
+{
+  mMarginRight = margin;
+}
+
+/*!
+  Sets the top margin of the legend. Margins are the distances the legend will keep to the axis
+  rect, when \ref setPositionStyle is not \ref psManual.
+*/
+void QCPLegend::setMarginTop(int margin)
+{
+  mMarginTop = margin;
+}
+
+/*!
+  Sets the bottom margin of the legend. Margins are the distances the legend will keep to the axis
+  rect, when \ref setPositionStyle is not \ref psManual.
+*/
+void QCPLegend::setMarginBottom(int margin)
+{
+  mMarginBottom = margin;
+}
+
+/*!
+  Sets the margin of the legend. Margins are the distances the legend will keep to the axis rect,
+  when \ref setPositionStyle is not \ref psManual.
+*/
+void QCPLegend::setMargin(int left, int right, int top, int bottom)
+{
+  mMarginLeft = left;
+  mMarginRight = right;
+  mMarginTop = top;
+  mMarginBottom = bottom;
+}
+
+/*!
+  Sets the vertical space between two legend items in the legend.
+  
+  \see setIconTextPadding, setPadding
+*/
+void QCPLegend::setItemSpacing(int spacing)
+{
+  mItemSpacing = spacing;
+}
+
+/*!
+  Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
+  representation of the graph) will use this size by default.
+*/
+void QCPLegend::setIconSize(const QSize &size)
+{
+  mIconSize = size;
+}
+
+/*! \overload
+*/
+void QCPLegend::setIconSize(int width, int height)
+{
+  mIconSize.setWidth(width);
+  mIconSize.setHeight(height);
+}
+
+/*!
+  Sets the horizontal space in pixels between the legend icon and the text next to it.
+  Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
+  name of the graph) will use this space by default.
+  
+  \see setItemSpacing
+*/
+void QCPLegend::setIconTextPadding(int padding)
+{
+  mIconTextPadding = padding;
+}
+
+/*!
+  Sets the pen used to draw a border around each legend icon. Legend items that draw an
+  icon (e.g. a visual representation of the graph) will use this pen by default.
+  
+  If no border is wanted, set this to \a Qt::NoPen.
+*/
+void QCPLegend::setIconBorderPen(const QPen &pen)
+{
+  mIconBorderPen = pen;
+}
+
+/*!
+  Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains iSelectLegend.)
+  
+  However, even when \a selectable is set to a value not allowing the selection of a specific part,
+  it is still possible to set the selection of this part manually, by calling \ref setSelected
+  directly.
+  
+  \see SelectablePart, setSelected
+*/
+void QCPLegend::setSelectable(const SelectableParts &selectable)
+{
+  mSelectable = selectable;
+}
+
+/*!
+  Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
+  is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
+  doesn't contain \ref spItems, those items become deselected.
+  
+  The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
+  contains iSelectLegend. You only need to call this function when you wish to change the selection
+  state manually.
+  
+  This function can change the selection state of a part even when \ref setSelectable was set to a
+  value that actually excludes the part.
+  
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+  
+  Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
+  before, because there's no way to specify which exact items to newly select. Do this by calling
+  \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
+  
+  \see SelectablePart, setSelectable, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
+  setSelectedFont
+*/
+void QCPLegend::setSelected(const SelectableParts &selected)
+{
+  if (mSelected != selected)
+  {
+    if (!selected.testFlag(spItems) && mSelected.testFlag(spItems)) // some items are selected, but new selection state doesn't contain spItems, so deselect them
+    {
+      for (int i=0; i<mItems.size(); ++i)
+        mItems.at(i)->setSelected(false);
+      mSelected = selected;
+      // not necessary to emit selectionChanged here because this will have happened for the last setSelected(false) on mItems already, via updateSelectionState()
+    } else
+    {
+      mSelected = selected;
+      emit selectionChanged(mSelected);
+    }
+  }
+}
+
+/*!
+  When the legend box is selected, this pen is used to draw the border instead of the normal pen
+  set via \ref setBorderPen.
+
+  \see setSelected, setSelectable, setSelectedBrush
+*/
+void QCPLegend::setSelectedBorderPen(const QPen &pen)
+{
+  mSelectedBorderPen = pen;
+}
+
+/*!
+  Sets the pen legend items will use to draw their icon borders, when they are selected.
+
+  \see setSelected, setSelectable, setSelectedFont
+*/
+void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
+{
+  mSelectedIconBorderPen = pen;
+}
+
+/*!
+  When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
+  set via \ref setBrush.
+
+  \see setSelected, setSelectable, setSelectedBorderPen
+*/
+void QCPLegend::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/*!
+  Sets the default font that is used by legend items when they are selected.
+  
+  This function will also set \a font on all already existing legend items.
+
+  \see setFont, QCPAbstractLegendItem::setSelectedFont
+*/
+void QCPLegend::setSelectedFont(const QFont &font)
+{
+  mSelectedFont = font;
+  for (int i=0; i<mItems.size(); ++i)
+    mItems.at(i)->setSelectedFont(font);
+}
+
+/*!
+  Sets the default text color that is used by legend items when they are selected.
+  
+  This function will also set \a color on all already existing legend items.
+
+  \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
+*/
+void QCPLegend::setSelectedTextColor(const QColor &color)
+{
+  mSelectedTextColor = color;
+  for (int i=0; i<mItems.size(); ++i)
+    mItems.at(i)->setSelectedTextColor(color);
+}
+
+/*!
+  Returns the item with index \a i.
+  
+  \see itemCount
+*/
+QCPAbstractLegendItem *QCPLegend::item(int index) const
+{
+  if (index >= 0 && index < mItems.size())
+    return mItems[index];
+  else
+    return 0;
+}
+
+/*!
+  Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
+  If such an item isn't in the legend, returns 0.
+  
+  \see hasItemWithPlottable
+*/
+QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
+{
+  for (int i=0; i<mItems.size(); ++i)
+  {
+    if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(mItems.at(i)))
+    {
+      if (pli->plottable() == plottable)
+        return pli;
+    }
+  }
+  return 0;
+}
+
+/*!
+  Returns the number of items currently in the legend.
+  \see item
+*/
+int QCPLegend::itemCount() const
+{
+  return mItems.size();
+}
+
+/*!
+  Returns whether the legend contains \a item.
+*/
+bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
+{
+  return mItems.contains(item);
+}
+
+/*!
+  Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
+  If such an item isn't in the legend, returns false.
+  
+  \see itemWithPlottable
+*/
+bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
+{
+  return itemWithPlottable(plottable);
+}
+
+/*!
+  Adds \a item to the legend, if it's not present already.
+  
+  Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
+  
+  The legend takes ownership of the item.
+*/
+bool QCPLegend::addItem(QCPAbstractLegendItem *item)
+{
+  if (!mItems.contains(item))
+  {
+    mItems.append(item);
+    return true;
+  } else
+    return false;
+}
+
+/*!
+  Removes the item with index \a index from the legend.
+
+  Returns true, if successful.
+  
+  \see itemCount, clearItems
+*/
+bool QCPLegend::removeItem(int index)
+{
+  if (index >= 0 && index < mItems.size())
+  {
+    mItemBoundingBoxes.remove(mItems.at(index));
+    delete mItems.at(index);
+    mItems.removeAt(index);
+    return true;
+  } else
+    return false;
+}
+
+/*! \overload
+  
+  Removes \a item from the legend.
+
+  Returns true, if successful.
+  
+  \see clearItems
+*/
+bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
+{
+  return removeItem(mItems.indexOf(item));
+}
+
+/*!
+  Removes all items from the legend.
+*/
+void QCPLegend::clearItems()
+{
+  qDeleteAll(mItems);
+  mItems.clear();
+  mItemBoundingBoxes.clear();
+}
+
+
+/*!
+  Returns the legend items that are currently selected. If no items are selected,
+  the list is empty.
+  
+  \see QCPAbstractLegendItem::setSelected, setSelectable
+*/
+QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
+{
+  QList<QCPAbstractLegendItem*> result;
+  for (int i=0; i<mItems.size(); ++i)
+  {
+    if (mItems.at(i)->selected())
+      result.append(mItems.at(i));
+  }
+  return result;
+}
+
+/*!
+  If \ref setAutoSize is true, the size needed to fit all legend contents is calculated and applied.
+  Finally, the automatic positioning of the legend is performed, depending on the \ref
+  setPositionStyle setting.
+*/
+void  QCPLegend::reArrange()
+{
+  if (mAutoSize)
+  {
+    calculateAutoSize();
+  }
+  calculateAutoPosition();
+}
+
+/*!
+  Returns whether the point \a pos in pixels hits the legend rect.
+  
+  \see selectTestItem
+*/
+bool QCPLegend::selectTestLegend(const QPointF &pos) const
+{
+  return QRect(mPosition, mSize).contains(pos.toPoint());
+}
+
+/*!
+  When the point \a pos in pixels hits a legend item, the item is returned. If no item is hit, 0 is
+  returned.
+  
+  \see selectTestLegend
+*/
+QCPAbstractLegendItem *QCPLegend::selectTestItem(const QPoint pos) const
+{
+  QMap<QCPAbstractLegendItem*, QRect>::const_iterator it;
+  for (it = mItemBoundingBoxes.constBegin(); it != mItemBoundingBoxes.constEnd(); ++it)
+  {
+    if (it.value().contains(pos) && mItems.contains(it.key()))
+      return it.key();
+  }
+  return 0;
+}
+
+/*! \internal
+  
+  Updates the spItems part of the selection state of this legend by going through all child items
+  and checking their selected state.
+  
+  If no items are selected and the current selected state contains spItems, it is removed and the
+  \ref selectionChanged signal is emitted. If at least one item is selected and the current selection
+  state does not contain spItems, it is added and the signal is emitted, too.
+  
+  This function is called in the QCPAbstractLegendItem::setSelected functions to propagate their
+  change to the parent legend.
+*/
+void QCPLegend::updateSelectionState()
+{
+  bool hasSelections = false;
+  for (int i=0; i<mItems.size(); ++i)
+  {
+    if (mItems.at(i)->selected())
+    {
+      hasSelections = true;
+      break;
+    }
+  }
+  
+  // in the following we don't use setSelected because it would cause unnecessary
+  // logic looping through items if spItems isn't set in the new state. (look at setSelected and you'll understand)
+  if (hasSelections && !mSelected.testFlag(spItems))
+  {
+    mSelected |= spItems;
+    emit selectionChanged(mSelected);
+  } else if (!hasSelections && mSelected.testFlag(spItems))
+  {
+    mSelected &= ~spItems;
+    emit selectionChanged(mSelected);
+  }
+}
+
+/*! \internal
+  
+  Handles the selection \a event and returns true when the selection event hit any parts of the
+  legend. If the selection state of any parts of the legend was changed, the output parameter \a
+  modified is set to true.
+  
+  When \a additiveSelecton is true, any new selections become selected in addition to the recent
+  selections. The recent selections are not cleared. Further, clicking on one object multiple times
+  in additive selection mode, toggles the selection of that object on and off.
+  
+  To indicate that an event deselects the legend (i.e. the parts that are deselectable by the user,
+  see \ref setSelectable), pass 0 as \a event.
+*/
+bool QCPLegend::handleLegendSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
+{
+  modified = false;
+  bool selectionFound = false;
+  
+  if (event && selectTestLegend(event->pos())) // clicked inside legend somewhere
+  {
+    QCPAbstractLegendItem *ali = selectTestItem(event->pos());
+    if (selectable().testFlag(QCPLegend::spItems) && ali && ali->selectable()) // items shall be selectable and item ali was clicked 
+    {
+      selectionFound = true;
+      // deselect legend box:
+      if (!additiveSelection && selected().testFlag(QCPLegend::spLegendBox) && selectable().testFlag(QCPLegend::spLegendBox))
+        setSelected(selected() & ~QCPLegend::spLegendBox);
+      // first select clicked item:
+      if (!ali->selected() || additiveSelection) // if additive selection, we toggle selection on and off per click
+      {
+        modified = true;
+        ali->setSelected(!ali->selected());
+      }
+      // finally, deselect all other items (if we had deselected all first, the selectionChanged signal of QCPLegend might have been emitted twice):
+      if (!additiveSelection)
+      {
+        for (int i=0; i<itemCount(); ++i)
+        {
+          if (item(i) != ali && item(i)->selected() && item(i)->selectable())
+          {
+            modified = true;
+            item(i)->setSelected(false);
+          }
+        }
+      }
+    } else // no specific item clicked or items not selectable
+    {
+      // if items actually were selectable, this means none were clicked, deselect them:
+      if (selectable().testFlag(QCPLegend::spItems) && selected().testFlag(QCPLegend::spItems) && !additiveSelection)
+      {
+        for (int i=0; i<itemCount(); ++i)
+        {
+          if (item(i)->selectable())
+            item(i)->setSelected(false);
+        }
+        modified = true;
+      }
+      // if legend box is selectable, select it:
+      if (selectable().testFlag(QCPLegend::spLegendBox))
+      {
+        if (!selected().testFlag(QCPLegend::spLegendBox) || additiveSelection)
+        {
+          selectionFound = true;
+          setSelected(selected() ^ QCPLegend::spLegendBox); // xor because we always toggle
+          modified = true;
+        }
+      }
+    }
+  } else if (selected() != QCPLegend::spNone && selectable() != QCPLegend::spNone && !additiveSelection) // legend not clicked, deselect it if selectable allows that (and all child items)
+  {
+    // only deselect parts that are allowed to be changed by user according to selectable()
+    // deselect child items (and automatically removes spItems from selected state of legend, if last item gets deselected):
+    if (selectable().testFlag(spItems)) 
+    {
+      for (int i=0; i<itemCount(); ++i)
+      {
+        if (item(i)->selected() && item(i)->selectable())
+        {
+          item(i)->setSelected(false);
+          modified = true;
+        }
+      }
+    }
+    // only deselect parts that are allowed to be changed (are selectable). Don't forcibly remove
+    // spItems, because some selected items might not be selectable, i.e. allowed to be deselected
+    // by user interaction. If that's not the case, spItems will have been removed from selected()
+    // state in previous loop by individual setSelected(false) calls on the items anyway.
+    QCPLegend::SelectableParts newState = selected() & ~(selectable()&~spItems);
+    if (newState != selected())
+    {
+      setSelected(newState);
+      modified = true;
+    }
+  }
+  
+  return selectionFound;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing main legend elements.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+  
+  This function takes into account the local setting of the antialiasing flag as well as
+  the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased
+*/
+void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
+}
+
+/*! \internal
+  
+  Returns the pen used to paint the border of the legend, taking into account the selection state
+  of the legend box.
+*/
+QPen QCPLegend::getBorderPen() const
+{
+  return mSelected.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
+}
+
+/*! \internal
+  
+  Returns the brush used to paint the background of the legend, taking into account the selection
+  state of the legend box.
+*/
+QBrush QCPLegend::getBrush() const
+{
+  return mSelected.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
+}
+
+/*! \internal
+  
+  Draws the legend with the provided \a painter.
+*/
+void QCPLegend::draw(QCPPainter *painter)
+{
+  painter->setBrush(getBrush());
+  painter->setPen(getBorderPen());
+  // draw background rect:
+  painter->drawRect(QRect(mPosition, mSize));
+  // draw legend items:
+  painter->setClipRect(QRect(mPosition, mSize).adjusted(1, 1, 0, 0));
+  painter->setPen(QPen());
+  painter->setBrush(Qt::NoBrush);
+  int currentTop = mPosition.y()+mPaddingTop;
+  for (int i=0; i<mItems.size(); ++i)
+  {
+    QSize itemSize = mItems.at(i)->size(QSize(mSize.width(), 0));
+    QRect itemRect = QRect(QPoint(mPosition.x()+mPaddingLeft, currentTop), itemSize);
+    mItemBoundingBoxes.insert(mItems.at(i), itemRect);
+    painter->save();
+    mItems.at(i)->applyAntialiasingHint(painter);
+    mItems.at(i)->draw(painter, itemRect);
+    painter->restore();
+    currentTop += itemSize.height()+mItemSpacing;
+  }
+}
+
+/*! \internal 
+  
+  Goes through similar steps as \ref draw and calculates the width and height needed to
+  fit all items and padding in the legend. The new calculated size is then applied to the mSize of
+  this legend.
+*/
+void QCPLegend::calculateAutoSize()
+{
+  int width = mMinimumSize.width()-mPaddingLeft-mPaddingRight; // start with minimum width and only expand from there
+  int currentTop;
+  bool repeat = true;
+  int repeatCount = 0;
+  while (repeat && repeatCount < 3) // repeat until we find self-consistent width (usually 2 runs)
+  {
+    repeat = false;
+    currentTop = mPaddingTop;
+    for (int i=0; i<mItems.size(); ++i)
+    {
+      QSize s = mItems.at(i)->size(QSize(width, 0));
+      currentTop += s.height();
+      if (i < mItems.size()-1) // vertical spacer for all but last item
+        currentTop += mItemSpacing;
+      if (width < s.width())
+      {
+        width = s.width();
+        repeat = true; // changed width, so need a new run with new width to let other items adapt their height to that new width
+      }
+    }
+    repeatCount++;
+  }
+  if (repeat)
+    qDebug() << Q_FUNC_INFO << "hit repeat limit for iterative width calculation";
+  currentTop += mPaddingBottom;
+  width += mPaddingLeft+mPaddingRight;
+  
+  mSize.setWidth(width);
+  if (currentTop > mMinimumSize.height())
+    mSize.setHeight(currentTop);
+  else
+    mSize.setHeight(mMinimumSize.height());
+}
+
+/*! \internal
+  
+  Sets the position dependant on the \ref setPositionStyle setting and the margins.
+*/
+void QCPLegend::calculateAutoPosition()
+{
+  switch (mPositionStyle)
+  {
+    case psTopLeft:
+      mPosition = mParentPlot->mAxisRect.topLeft() + QPoint(mMarginLeft, mMarginTop); break;
+    case psTop:
+      mPosition = mParentPlot->mAxisRect.topLeft() + QPoint(mParentPlot->mAxisRect.width()/2.0-mSize.width()/2.0, mMarginTop); break;
+    case psTopRight:
+      mPosition = mParentPlot->mAxisRect.topRight() + QPoint(-mMarginRight-mSize.width(), mMarginTop); break;
+    case psRight:
+      mPosition = mParentPlot->mAxisRect.topRight() + QPoint(-mMarginRight-mSize.width(), mParentPlot->mAxisRect.height()/2.0-mSize.height()/2.0); break;
+    case psBottomRight:
+      mPosition = mParentPlot->mAxisRect.bottomRight() + QPoint(-mMarginRight-mSize.width(), -mMarginBottom-mSize.height()); break;
+    case psBottom:
+      mPosition = mParentPlot->mAxisRect.bottomLeft() + QPoint(mParentPlot->mAxisRect.width()/2.0-mSize.width()/2.0, -mMarginBottom-mSize.height()); break;
+    case psBottomLeft:
+      mPosition = mParentPlot->mAxisRect.bottomLeft() + QPoint(mMarginLeft, -mMarginBottom-mSize.height()); break;
+    case psLeft:
+      mPosition = mParentPlot->mAxisRect.topLeft() + QPoint(mMarginLeft, mParentPlot->mAxisRect.height()/2.0-mSize.height()/2.0); break;
+    case psManual: break;
+  }
+}
+
+
+// ================================================================================
+// =================== QCPAxis
+// ================================================================================
+
+/*! \class QCPAxis
+  \brief Manages a single axis inside a QCustomPlot.
+
+  Usually doesn't need to be instantiated externally. Access %QCustomPlot's axes via
+  QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and
+  QCustomPlot::yAxis2 (right).
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn Qt::Orientation QCPAxis::orientation() const
+  
+  Returns the orientation of the axis. The axis orientation (horizontal or vertical) is deduced
+  from the axis type (left, top, right or bottom).
+*/
+
+/* end of documentation of inline functions */
+/* start of documentation of signals */
+
+/*! \fn void QCPAxis::ticksRequest()
+  
+  This signal is emitted when \ref setAutoTicks is false and the axis is about to generate tick
+  labels and replot itself.
+  
+  Modifying the tick positions can be done with \ref setTickVector. If you also want to control the
+  tick labels, set \ref setAutoTickLabels to false and also provide the labels with \ref
+  setTickVectorLabels.
+  
+  If you only want static ticks you probably don't need this signal, since you can just set the
+  tick vector (and possibly tick label vector) once. However, if you want to provide ticks (and
+  maybe labels) dynamically, e.g. depending on the current axis range, connect a slot to this
+  signal and set the vector/vectors there.
+*/
+
+/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange)
+
+  This signal is emitted when the range of this axis has changed. You can connect it to the \ref
+  setRange slot of another axis to communicate the new range to the other axis, in order for it to
+  be synchronized.
+*/
+
+/*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
+  
+  This signal is emitted when the selection state of this axis has changed, either by user interaction
+  or by a direct call to \ref setSelected.
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs an Axis instance of Type \a type inside \a parentPlot.
+*/
+QCPAxis::QCPAxis(QCustomPlot *parentPlot, AxisType type) :
+  QCPLayerable(parentPlot)
+{
+  mLowestVisibleTick = 0;
+  mHighestVisibleTick = -1;
+  mGrid = new QCPGrid(this);
+  setAxisType(type);
+  setAxisRect(parentPlot->axisRect()); 
+  setScaleType(stLinear);
+  setScaleLogBase(10);
+  
+  setAntialiased(false);
+  setRange(0, 5);
+  setRangeReversed(false);
+  
+  setTicks(true);
+  setTickStep(1);
+  setAutoTickCount(6);
+  setAutoTicks(true);
+  setAutoTickLabels(true);
+  setAutoTickStep(true);
+  setTickLabelFont(parentPlot->font());
+  setTickLabelColor(Qt::black);
+  setTickLength(5);
+  setTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap));
+  setTickLabels(true);
+  setTickLabelType(ltNumber);
+  setTickLabelRotation(0);
+  setDateTimeFormat("hh:mm:ss\ndd.MM.yy");
+  setNumberFormat("gbd");
+  setNumberPrecision(6);
+  setLabel("");
+  setLabelFont(parentPlot->font());
+  setLabelColor(Qt::black);
+  
+  setAutoSubTicks(true);
+  setSubTickCount(4);
+  setSubTickLength(2);
+  setSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap));
+  setBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap));
+  
+  setSelected(spNone);
+  setSelectable(spAxis | spTickLabels | spAxisLabel);
+  QFont selTickLabelFont = tickLabelFont();
+  selTickLabelFont.setBold(true);
+  setSelectedTickLabelFont(selTickLabelFont);
+  QFont selLabelFont = labelFont();
+  selLabelFont.setBold(true);
+  setSelectedLabelFont(selLabelFont);
+  setSelectedTickLabelColor(Qt::blue);
+  setSelectedLabelColor(Qt::blue);
+  QPen blueThickPen(Qt::blue, 2);
+  setSelectedBasePen(blueThickPen);
+  setSelectedTickPen(blueThickPen);
+  setSelectedSubTickPen(blueThickPen);
+  
+  setPadding(0);
+  if (type == atTop)
+  {
+    setTickLabelPadding(3);
+    setLabelPadding(6);
+  } else if (type == atRight)
+  {
+    setTickLabelPadding(7);
+    setLabelPadding(12);
+  } else if (type == atBottom)
+  {
+    setTickLabelPadding(3);
+    setLabelPadding(3);
+  } else if (type == atLeft)
+  {
+    setTickLabelPadding(5);
+    setLabelPadding(10);
+  }
+}
+
+QCPAxis::~QCPAxis()
+{
+  delete mGrid;
+}
+
+/* No documentation as it is a property getter */
+QString QCPAxis::numberFormat() const
+{
+  QString result;
+  result.append(mNumberFormatChar);
+  if (mNumberBeautifulPowers)
+  {
+    result.append("b");
+    if (mNumberMultiplyCross)
+      result.append("c");
+  }
+  return result;
+}
+
+/*! \internal
+  
+  Sets the axis type. This determines the \ref orientation and together with the current axis rect
+  (see \ref setAxisRect), the position of the axis. Depending on \a type, ticks, tick labels, and
+  label are drawn on corresponding sides of the axis base line.
+*/
+void QCPAxis::setAxisType(AxisType type)
+{
+  mAxisType = type;
+  mOrientation = (type == atBottom || type == atTop) ? Qt::Horizontal : Qt::Vertical;
+}
+
+/*! \internal
+  
+  Sets the axis rect. The axis uses this rect to position itself within the plot,
+  together with the information of its type (\ref setAxisType). Theoretically it's possible to give
+  a plot's axes different axis rects (e.g. for gaps between them), however, they are currently all
+  synchronized by the QCustomPlot::setAxisRect function.
+*/
+void QCPAxis::setAxisRect(const QRect &rect)
+{
+  mAxisRect = rect;
+}
+
+/*!
+  Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
+  stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
+  scaling, major tick marks appear at all powers of the logarithm base. Properties like tick step
+  (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but less major
+  ticks, consider choosing a logarithm base of 100, 1000 or even higher.
+  
+  If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat) uses the 'b' option
+  (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
+  [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
+  part). To only display the decimal power, set the number precision to zero with
+  \ref setNumberPrecision.
+*/
+void QCPAxis::setScaleType(ScaleType type)
+{
+  mScaleType = type;
+  if (mScaleType == stLogarithmic)
+    mRange = mRange.sanitizedForLogScale();
+}
+
+/*!
+  If \ref setScaleType is set to \ref stLogarithmic, \a base will be the logarithm base of the
+  scaling. In logarithmic axis scaling, major tick marks appear at all powers of \a base.
+  
+  Properties like tick step (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but
+  less major ticks, consider choosing \a base 100, 1000 or even higher.
+*/
+void QCPAxis::setScaleLogBase(double base)
+{
+  if (base > 1)
+  {
+    mScaleLogBase = base;
+    mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
+  } else
+    qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
+}
+
+/*!
+  Sets the range of the axis.
+  
+  This slot may be connected with the \ref rangeChanged signal of another axis so this axis
+  is always synchronized with the other axis range, when it changes.
+  
+  To invert the direction of an axis range, use \ref setRangeReversed.
+*/
+void QCPAxis::setRange(const QCPRange &range)
+{
+  if (range.lower == mRange.lower && range.upper == mRange.upper)
+    return;
+  
+  if (!QCPRange::validRange(range)) return;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = range.sanitizedForLogScale();
+  } else
+  {
+    mRange = range.sanitizedForLinScale();
+  }
+  emit rangeChanged(mRange);
+}
+
+/*!
+  Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
+  
+  However, even when \a selectable is set to a value not allowing the selection of a specific part,
+  it is still possible to set the selection of this part manually, by calling \ref setSelected
+  directly.
+  
+  \see SelectablePart, setSelected
+*/
+void QCPAxis::setSelectable(const SelectableParts &selectable)
+{
+  mSelectable = selectable;
+}
+
+/*!
+  Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
+  is selected, it uses a different pen/font.
+  
+  The entire selection mechanism for axes is handled automatically when \ref
+  QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
+  wish to change the selection state manually.
+  
+  This function can change the selection state of a part even when \ref setSelectable was set to a
+  value that actually excludes the part.
+  
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+  
+  \see SelectablePart, setSelectable, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
+  setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
+*/
+void QCPAxis::setSelected(const SelectableParts &selected)
+{
+  if (mSelected != selected)
+  {
+    mSelected = selected;
+    emit selectionChanged(mSelected);
+  }
+}
+
+/*!
+  \overload
+  Sets the lower and upper bound of the axis range.
+  
+  To invert the direction of an axis range, use \ref setRangeReversed.
+  
+  There is also a slot to set a range, see \ref setRange(const QCPRange &range).
+*/
+void QCPAxis::setRange(double lower, double upper)
+{
+  if (lower == mRange.lower && upper == mRange.upper)
+    return;
+  
+  if (!QCPRange::validRange(lower, upper)) return;
+  mRange.lower = lower;
+  mRange.upper = upper;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  emit rangeChanged(mRange);
+}
+
+/*!
+  \overload
+  Sets the range of the axis.
+
+  \param position the \a position coordinate indicates together with the \a alignment parameter, where
+  the new range will be positioned.
+  \param size defines the size (upper-lower) of the new axis range.
+  \param alignment determines how \a position is to be interpreted.\n
+  If \a alignment is Qt::AlignLeft, \a position will be the lower bound of the range.\n
+  If \a alignment is Qt::AlignRight, \a position will be the upper bound of the range.\n
+  If \a alignment is Qt::AlignCenter, the new range will be centered around \a position.\n
+  Any other values for \a alignment will default to Qt::AlignCenter.
+*/
+void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
+{
+  if (alignment == Qt::AlignLeft)
+    setRange(position, position+size);
+  else if (alignment == Qt::AlignRight)
+    setRange(position-size, position);
+  else // alignment == Qt::AlignCenter
+    setRange(position-size/2.0, position+size/2.0);
+}
+
+/*!
+  Sets the lower bound of the axis range, independently of the upper bound.
+  \see setRange
+*/
+void QCPAxis::setRangeLower(double lower)
+{
+  if (mRange.lower == lower)
+    return;
+  
+  mRange.lower = lower;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  emit rangeChanged(mRange);
+}
+
+/*!
+  Sets the upper bound of the axis range, independently of the lower bound.
+  \see setRange
+*/
+void QCPAxis::setRangeUpper(double upper)
+{
+  if (mRange.upper == upper)
+    return;
+  
+  mRange.upper = upper;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  emit rangeChanged(mRange);
+}
+
+/*!
+  Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
+  axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
+  direction of increasing values is inverted. Note that the range and data interface stays the same
+  for reversed axes, e.g. the \a lower part of the \ref setRange interface will still reference the
+  mathematically smaller number than the \a upper part.
+*/
+void QCPAxis::setRangeReversed(bool reversed)
+{
+  mRangeReversed = reversed;
+}
+
+/*!
+  Sets whether the grid of this axis is drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAxis::setAntialiasedGrid(bool enabled)
+{
+  mGrid->setAntialiased(enabled);
+}
+
+/*!
+  Sets whether the sub grid of this axis is drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAxis::setAntialiasedSubGrid(bool enabled)
+{
+  mGrid->setAntialiasedSubGrid(enabled);
+}
+
+/*!
+  Sets whether the zero line of this axis is drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAxis::setAntialiasedZeroLine(bool enabled)
+{
+  mGrid->setAntialiasedZeroLine(enabled);
+}
+
+/*!
+  Sets whether the grid lines are visible.
+  \see setSubGrid, setGridPen, setZeroLinePen
+*/
+void QCPAxis::setGrid(bool show)
+{
+  mGrid->setVisible(show);
+}
+
+/*!
+  Sets whether the sub grid lines are visible.
+  \see setGrid, setSubGridPen, setZeroLinePen
+*/
+void QCPAxis::setSubGrid(bool show)
+{
+  mGrid->setSubGridVisible(show);
+}
+
+/*!
+  Sets whether the tick positions should be calculated automatically (either from an automatically
+  generated tick step or a tick step provided manually via \ref setTickStep, see \ref setAutoTickStep).
+  
+  If \a on is set to false, you must provide the tick positions manually via \ref setTickVector.
+  For these manual ticks you may let QCPAxis generate the appropriate labels automatically
+  by setting/leaving \ref setAutoTickLabels true. If you also wish to control the displayed labels
+  manually, set \ref setAutoTickLabels to false and provide the label strings with \ref setTickVectorLabels.
+  
+  If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
+  vectors in a slot connected to the \ref ticksRequest signal.
+*/
+void QCPAxis::setAutoTicks(bool on)
+{
+  mAutoTicks = on;
+}
+
+/*!
+  When \ref setAutoTickStep is true, \a approximateCount determines how many ticks should be generated
+  in the visible range approximately.
+*/
+void QCPAxis::setAutoTickCount(int approximateCount)
+{
+  mAutoTickCount = approximateCount;
+}
+
+/*!
+  Sets whether the tick labels are generated automatically depending on the tick label type
+  (\ref ltNumber or \ref ltDateTime).
+  
+  If \a on is set to false, you should provide the tick labels via \ref setTickVectorLabels. This
+  is usually used in a combination with \ref setAutoTicks set to false for complete control over
+  tick positions and labels, e.g. when the ticks should be at multiples of pi and show "2pi", "3pi"
+  etc. as tick labels.
+  
+  If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
+  vectors in a slot connected to the \ref ticksRequest signal.
+*/
+void QCPAxis::setAutoTickLabels(bool on)
+{
+  mAutoTickLabels = on;
+}
+
+/*!
+  Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated
+  automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human
+  readable plots. This means the tick step mantissa is chosen such that it's either a multiple of
+  two or ends in 0.5. The number of ticks the algorithm aims for within the visible range can be
+  set with \ref setAutoTickCount. It's not guaranteed that this number of ticks is met exactly, but
+  approximately within a tolerance of two or three.
+  
+  If \a on is set to false, you may set the tick step manually with \ref setTickStep.
+*/
+void QCPAxis::setAutoTickStep(bool on)
+{
+  mAutoTickStep = on;
+}
+
+/*!
+  Sets whether the number of sub ticks in one tick interval is determined automatically.
+  This works, as long as the tick step mantissa is a multiple of 0.5 (which it is, when
+  \ref setAutoTickStep is enabled).\n
+  When \a on is set to false, you may set the sub tick count with \ref setSubTickCount manually.
+*/
+void QCPAxis::setAutoSubTicks(bool on)
+{
+  mAutoSubTicks = on;
+}
+
+/*!
+  Sets whether tick marks are displayed. Setting \a show to false does not imply, that tick labels
+  are invisible, too. To achieve that, see \ref setTickLabels.
+*/
+void QCPAxis::setTicks(bool show)
+{
+  mTicks = show;
+}
+
+/*!
+  Sets whether tick labels are displayed.
+*/
+void QCPAxis::setTickLabels(bool show)
+{
+  mTickLabels = show;
+}
+
+/*!
+  Sets the distance between the axis base line (or any tick marks pointing outward) and the tick labels.
+  \see setLabelPadding, setPadding
+*/
+void QCPAxis::setTickLabelPadding(int padding)
+{
+  mTickLabelPadding = padding;
+}
+
+/*!
+  Sets whether the tick labels display numbers or dates/times.\n
+  If \a type is set to \ref ltNumber, the format specifications of \ref setNumberFormat apply.\n
+  If \a type is set to \ref ltDateTime, the format specifications of \ref setDateTimeFormat apply.\n
+  In QCustomPlot, date/time coordinates are double numbers representing the seconds since 1970-01-01T00:00:00 UTC.
+  This format can be retrieved from QDateTime objects with the QDateTime::toTime_t() function. Since this
+  only gives a resolution of one second, there is also the QDateTime::toMSecsSinceEpoch() function which
+  returns the timespan described above in milliseconds. Divide its return value by 1000.0 to get a value with
+  the format needed for date/time plotting, this time with a resolution of one millisecond.
+*/
+void QCPAxis::setTickLabelType(LabelType type)
+{
+  mTickLabelType = type;
+}
+
+/*!
+  Sets the font of the tick labels, i.e. the numbers drawn next to tick marks.
+  
+  \see setTickLabelColor
+*/
+void QCPAxis::setTickLabelFont(const QFont &font)
+{
+  mTickLabelFont = font;
+}
+
+/*!
+  Sets the color of the tick labels, i.e. the numbers drawn next to tick marks.
+  
+  \see setTickLabelFont
+*/
+void QCPAxis::setTickLabelColor(const QColor &color)
+{
+  mTickLabelColor = color;
+}
+
+/*!
+  Sets the rotation of the tick labels, i.e. the numbers drawn next to tick marks. If \a degrees
+  is zero, the labels are drawn normally. Else, the tick labels are drawn rotated by \a degrees
+  clockwise. The specified angle is bound to values from -90 to 90 degrees.
+*/
+void QCPAxis::setTickLabelRotation(double degrees)
+{
+  mTickLabelRotation = qBound(-90.0, degrees, 90.0);
+}
+
+/*!
+  Sets the format in which dates and times are displayed as tick labels, if \ref setTickLabelType is \ref ltDateTime.
+  for details about the \a format string, see the documentation of QDateTime::toString().
+  Newlines can be inserted with "\n".
+*/
+void QCPAxis::setDateTimeFormat(const QString &format)
+{
+  mDateTimeFormat = format;
+}
+
+/*!
+  Sets the number format for the numbers drawn as tick labels (if tick label type is \ref
+  ltNumber). This \a formatCode is an extended version of the format code used e.g. by
+  QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats"
+  section in the detailed description of the QString class. \a formatCode is a string of one, two
+  or three characters. The first character is identical to the normal format code used by Qt. In
+  short, this means: 'e'/'E' scientific format, 'f' fixed format, 'g'/'G' scientific or fixed,
+  whichever is shorter.
+  
+  The second and third characters are optional and specific to QCustomPlot:\n
+  If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
+  "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
+  "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
+  [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
+  If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
+  be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
+  cross and 183 (0xB7) for the dot.
+  
+  If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a formatCode uses the 'b'
+  option (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
+  [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
+  part). To only display the decimal power, set the number precision to zero with \ref
+  setNumberPrecision.
+  
+  Examples for \a formatCode:
+  \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
+  normal scientific format is used
+  \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
+  beautifully typeset decimal powers and a dot as multiplication sign
+  \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
+  multiplication sign
+  \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
+  powers. Format code will be reduced to 'f'.
+  \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
+  code will not be changed.
+*/
+void QCPAxis::setNumberFormat(const QString &formatCode)
+{
+  if (formatCode.length() < 1) return;
+  
+  // interpret first char as number format char:
+  QString allowedFormatChars = "eEfgG";
+  if (allowedFormatChars.contains(formatCode.at(0)))
+  {
+    mNumberFormatChar = formatCode.at(0).toAscii();
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
+    return;
+  }
+  if (formatCode.length() < 2)
+  {
+    mNumberBeautifulPowers = false;
+    mNumberMultiplyCross = false;
+    return;
+  }
+  
+  // interpret second char as indicator for beautiful decimal powers:
+  if (formatCode.at(1) == 'b' && (mNumberFormatChar == 'e' || mNumberFormatChar == 'g'))
+  {
+    mNumberBeautifulPowers = true;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
+    return;
+  }
+  if (formatCode.length() < 3)
+  {
+    mNumberMultiplyCross = false;
+    return;
+  }
+  
+  // interpret third char as indicator for dot or cross multiplication symbol:
+  if (formatCode.at(2) == 'c')
+  {
+    mNumberMultiplyCross = true;
+  } else if (formatCode.at(2) == 'd')
+  {
+    mNumberMultiplyCross = false;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
+    return;
+  }
+}
+
+/*!
+  Sets the precision of the numbers drawn as tick labels. See QLocale::toString(double i, char f,
+  int prec) for details. The effect of precisions are most notably for number Formats starting with
+  'e', see \ref setNumberFormat
+
+  If the scale type (\ref setScaleType) is \ref stLogarithmic and the number format (\ref
+  setNumberFormat) uses the 'b' format code (beautifully typeset decimal powers), the display
+  usually is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for logarithmic
+  scaling (the "1 [multiplication sign]" part). To only display the decimal power, set \a precision
+  to zero.
+*/
+void QCPAxis::setNumberPrecision(int precision)
+{
+  mNumberPrecision = precision;
+}
+
+/*!
+  If \ref setAutoTickStep is set to false, use this function to set the tick step manually.
+  The tick step is the interval between (major) ticks, in plot coordinates.
+  \see setSubTickCount
+*/
+void QCPAxis::setTickStep(double step)
+{
+  mTickStep = step;
+}
+
+/*!
+  If you want full control over what ticks (and possibly labels) the axes show, this function is
+  used to set the coordinates at which ticks will appear.\ref setAutoTicks must be disabled, else
+  the provided tick vector will be overwritten with automatically generated tick coordinates. The
+  labels of the ticks can either be generated automatically when \ref setAutoTickLabels is left
+  enabled, or be set manually with \ref setTickVectorLabels, when \ref setAutoTickLabels is
+  disabled.
+  
+  \a vec is a vector containing the positions of the ticks.
+
+  \see setTickVectorLabels
+*/
+void QCPAxis::setTickVector(const QVector<double> &vec)
+{
+  mTickVector = vec;
+}
+
+/*!
+  If you want full control over what ticks and labels the axes show, this function is used to set a
+  number of QStrings that will be displayed at the tick positions which you need to provide with
+  \ref setTickVector. These two vectors should have the same size. (Note that you need to disable
+  \ref setAutoTicks and \ref setAutoTickLabels first.)
+  
+  \a vec is a vector containing the labels of the ticks.
+  
+  \see setTickVector
+*/
+void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
+{
+  mTickVectorLabels = vec;
+}
+
+/*!
+  Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
+  plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
+  zero, the tick labels will increase their distance to the axis accordingly, so they won't collide
+  with the ticks.
+  \see setSubTickLength
+*/
+void QCPAxis::setTickLength(int inside, int outside)
+{
+  mTickLengthIn = inside;
+  mTickLengthOut = outside;
+}
+
+/*!
+  Sets the number of sub ticks in one (major) tick step. A sub tick count of three for example,
+  divides the tick intervals in four sub intervals.
+  
+  By default, the number of sub ticks is chosen automatically in a reasonable manner as long as
+  the mantissa of the tick step is a multiple of 0.5 (which it is, when \ref setAutoTickStep is enabled).
+  If you want to disable automatic sub ticks and use this function to set the count manually, see
+  \ref setAutoSubTicks.
+*/
+void QCPAxis::setSubTickCount(int count)
+{
+  mSubTickCount = count;
+}
+
+/*!
+  Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside the
+  plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
+  zero, the tick labels will increase their distance to the axis accordingly, so they won't collide
+  with the ticks.
+  \see setTickLength
+*/
+void QCPAxis::setSubTickLength(int inside, int outside)
+{
+  mSubTickLengthIn = inside;
+  mSubTickLengthOut = outside;
+}
+
+/*!
+  Sets the pen, the axis base line is drawn with.
+  
+  \see setTickPen, setSubTickPen
+*/
+void QCPAxis::setBasePen(const QPen &pen)
+{
+  mBasePen = pen;
+}
+
+/*!
+  Sets the pen, grid lines are drawn with.
+  \see setSubGridPen, setZeroLinePen
+*/
+void QCPAxis::setGridPen(const QPen &pen)
+{
+  mGrid->setPen(pen);
+}
+
+/*!
+  Sets the pen, the sub grid lines are drawn with.
+  (By default, subgrid drawing needs to be enabled first with \ref setSubGrid.)
+  \see setGridPen, setZeroLinePen
+*/
+void QCPAxis::setSubGridPen(const QPen &pen)
+{
+  mGrid->setSubGridPen(pen);
+}
+
+/*!
+  Sets the pen with which a single grid-like line will be drawn at value position zero. The line
+  will be drawn instead of a grid line at that position, and not on top. To disable the drawing of
+  a zero-line, set \a pen to Qt::NoPen. Then, if \ref setGrid is enabled, a grid line will be
+  drawn instead.
+  \see setGrid, setGridPen
+*/
+void QCPAxis::setZeroLinePen(const QPen &pen)
+{
+  mGrid->setZeroLinePen(pen);
+}
+
+/*!
+  Sets the pen, tick marks will be drawn with.
+  \see setTickLength, setBasePen
+*/
+void QCPAxis::setTickPen(const QPen &pen)
+{
+  mTickPen = pen;
+}
+
+/*!
+  Sets the pen, subtick marks will be drawn with.
+  \see setSubTickCount, setSubTickLength, setBasePen
+*/
+void QCPAxis::setSubTickPen(const QPen &pen)
+{
+  mSubTickPen = pen;
+}
+
+/*!
+  Sets the font of the axis label.
+  
+  \see setLabelColor
+*/
+void QCPAxis::setLabelFont(const QFont &font)
+{
+  mLabelFont = font;
+}
+
+/*!
+  Sets the color of the axis label.
+  
+  \see setLabelFont
+*/
+void QCPAxis::setLabelColor(const QColor &color)
+{
+  mLabelColor = color;
+}
+
+/*!
+  Sets the axis label that will be shown below/above or next to the axis, depending on its orientation.
+*/
+void QCPAxis::setLabel(const QString &str)
+{
+  mLabel = str;
+}
+
+/*!
+  Sets the distance between the tick labels and the axis label.
+  \see setTickLabelPadding, setPadding
+*/
+void QCPAxis::setLabelPadding(int padding)
+{
+  mLabelPadding = padding;
+}
+
+/*!
+  Sets the padding of the axis.
+
+  When \ref QCustomPlot::setAutoMargin is enabled, the padding is the additional distance to the
+  respective widget border, that is left blank. If \a padding is zero (default), the auto margin
+  mechanism will find a margin that the axis label (or tick label, if no axis label is set) barely
+  fits inside the QCustomPlot widget. To give the label closest to the border some freedom,
+  increase \a padding.
+  
+  The axis padding has no meaning if \ref QCustomPlot::setAutoMargin is disabled.
+  
+  \see setLabelPadding, setTickLabelPadding
+*/
+void QCPAxis::setPadding(int padding)
+{
+  mPadding = padding;
+}
+
+/*!
+  Sets the font that is used for tick labels when they are selected.
+  
+  \see setTickLabelFont, setSelectable, setSelected, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedTickLabelFont(const QFont &font)
+{
+  mSelectedTickLabelFont = font;
+}
+
+/*!
+  Sets the font that is used for the axis label when it is selected.
+  
+  \see setLabelFont, setSelectable, setSelected, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedLabelFont(const QFont &font)
+{
+  mSelectedLabelFont = font;
+}
+
+/*!
+  Sets the color that is used for tick labels when they are selected.
+  
+  \see setTickLabelColor, setSelectable, setSelected, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedTickLabelColor(const QColor &color)
+{
+  mSelectedTickLabelColor = color;
+}
+
+/*!
+  Sets the color that is used for the axis label when it is selected.
+  
+  \see setLabelColor, setSelectable, setSelected, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedLabelColor(const QColor &color)
+{
+  mSelectedLabelColor = color;
+}
+
+/*!
+  Sets the pen that is used to draw the axis base line when selected.
+  
+  \see setBasePen, setSelectable, setSelected, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedBasePen(const QPen &pen)
+{
+  mSelectedBasePen = pen;
+}
+
+/*!
+  Sets the pen that is used to draw the (major) ticks when selected.
+  
+  \see setTickPen, setSelectable, setSelected, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedTickPen(const QPen &pen)
+{
+  mSelectedTickPen = pen;
+}
+
+/*!
+  Sets the pen that is used to draw the subticks when selected.
+  
+  \see setSubTickPen, setSelectable, setSelected, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedSubTickPen(const QPen &pen)
+{
+  mSelectedSubTickPen = pen;
+}
+
+/*!
+  If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
+  bounds of the range. The range is simply moved by \a diff.
+  
+  If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
+  corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
+*/
+void QCPAxis::moveRange(double diff)
+{
+  if (mScaleType == stLinear)
+  {
+    mRange.lower += diff;
+    mRange.upper += diff;
+  } else // mScaleType == stLogarithmic
+  {
+    mRange.lower *= diff;
+    mRange.upper *= diff;
+  }
+  emit rangeChanged(mRange);
+}
+
+/*!
+  Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
+  factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
+  coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
+  around 1.0 will have moved symmetrically closer to 1.0).
+*/
+void QCPAxis::scaleRange(double factor, double center)
+{
+  
+  if (mScaleType == stLinear)
+  {
+    QCPRange newRange;
+    newRange.lower = (mRange.lower-center)*factor + center;
+    newRange.upper = (mRange.upper-center)*factor + center;
+    if (QCPRange::validRange(newRange))
+      mRange = newRange.sanitizedForLinScale();
+  } else // mScaleType == stLogarithmic
+  {
+    if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
+    {
+      QCPRange newRange;
+      newRange.lower = pow(mRange.lower/center, factor)*center;
+      newRange.upper = pow(mRange.upper/center, factor)*center;
+      if (QCPRange::validRange(newRange))
+        mRange = newRange.sanitizedForLogScale();
+    } else
+      qDebug() << Q_FUNC_INFO << "center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
+  }
+  emit rangeChanged(mRange);
+}
+
+/*!
+  Sets the range of this axis to have a certain scale \a ratio to \a otherAxis. For example, if \a
+  ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs plotted with those
+  axes will appear in a 1:1 ratio, independent of the aspect ratio the axis rect has. This is an
+  operation that changes the range of this axis once, it doesn't fix the scale ratio indefinitely.
+  Consequently calling this function in the constructor won't have the desired effect, since the
+  widget's dimensions aren't defined yet, and a resizeEvent will follow.
+*/
+void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
+{
+  int otherPixelSize, ownPixelSize;
+  
+  if (otherAxis->orientation() == Qt::Horizontal)
+    otherPixelSize = otherAxis->mAxisRect.width();
+  else
+    otherPixelSize = otherAxis->mAxisRect.height();
+  
+  if (orientation() == Qt::Horizontal)
+    ownPixelSize = mAxisRect.width();
+  else
+    ownPixelSize = mAxisRect.height();
+  
+  double newRangeSize = ratio*otherAxis->mRange.size()*ownPixelSize/(double)otherPixelSize;
+  setRange(range().center(), newRangeSize, Qt::AlignCenter);
+}
+
+/*!
+  Transforms \a value (in pixel coordinates of the QCustomPlot widget) to axis coordinates.
+*/
+double QCPAxis::pixelToCoord(double value) const
+{
+  if (orientation() == Qt::Horizontal)
+  {
+    if (mScaleType == stLinear)
+    {
+      if (!mRangeReversed)
+        return (value-mAxisRect.left())/(double)mAxisRect.width()*mRange.size()+mRange.lower;
+      else
+        return -(value-mAxisRect.left())/(double)mAxisRect.width()*mRange.size()+mRange.upper;
+    } else // mScaleType == stLogarithmic
+    {
+      if (!mRangeReversed)
+        return pow(mRange.upper/mRange.lower, (value-mAxisRect.left())/(double)mAxisRect.width())*mRange.lower;
+      else
+        return pow(mRange.upper/mRange.lower, (mAxisRect.left()-value)/(double)mAxisRect.width())*mRange.upper;
+    }
+  } else // orientation() == Qt::Vertical
+  {
+    if (mScaleType == stLinear)
+    {
+      if (!mRangeReversed)
+        return (mAxisRect.bottom()-value)/(double)mAxisRect.height()*mRange.size()+mRange.lower;
+      else
+        return -(mAxisRect.bottom()-value)/(double)mAxisRect.height()*mRange.size()+mRange.upper;
+    } else // mScaleType == stLogarithmic
+    {
+      if (!mRangeReversed)
+        return pow(mRange.upper/mRange.lower, (mAxisRect.bottom()-value)/(double)mAxisRect.height())*mRange.lower;
+      else
+        return pow(mRange.upper/mRange.lower, (value-mAxisRect.bottom())/(double)mAxisRect.height())*mRange.upper;
+    }
+  }
+}
+
+/*!
+  Transforms \a value (in coordinates of the axis) to pixel coordinates of the QCustomPlot widget.
+*/
+double QCPAxis::coordToPixel(double value) const
+{
+  if (orientation() == Qt::Horizontal)
+  {
+    if (mScaleType == stLinear)
+    {
+      if (!mRangeReversed)
+        return (value-mRange.lower)/mRange.size()*mAxisRect.width()+mAxisRect.left();
+      else
+        return (mRange.upper-value)/mRange.size()*mAxisRect.width()+mAxisRect.left();
+    } else // mScaleType == stLogarithmic
+    {
+      if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
+        return !mRangeReversed ? mAxisRect.right()+200 : mAxisRect.left()-200;
+      else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
+        return !mRangeReversed ? mAxisRect.left()-200 : mAxisRect.right()+200;
+      else
+      {
+        if (!mRangeReversed)
+          return baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect.width()+mAxisRect.left();
+        else
+          return baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect.width()+mAxisRect.left();
+      }
+    }
+  } else // orientation() == Qt::Vertical
+  {
+    if (mScaleType == stLinear)
+    {
+      if (!mRangeReversed)
+        return mAxisRect.bottom()-(value-mRange.lower)/mRange.size()*mAxisRect.height();
+      else
+        return mAxisRect.bottom()-(mRange.upper-value)/mRange.size()*mAxisRect.height();
+    } else // mScaleType == stLogarithmic
+    {     
+      if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
+        return !mRangeReversed ? mAxisRect.top()-200 : mAxisRect.bottom()+200;
+      else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
+        return !mRangeReversed ? mAxisRect.bottom()+200 : mAxisRect.top()-200;
+      else
+      {
+        if (!mRangeReversed)
+          return mAxisRect.bottom()-baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect.height();
+        else
+          return mAxisRect.bottom()-baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect.height();
+      }
+    }
+  }
+}
+
+/*!
+  Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
+  is independent of the user-selectable parts defined with \ref setSelectable. Further, this
+  function does not change the current selection state of the axis.
+  
+  If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
+  
+  \see setSelected, setSelectable, QCustomPlot::setInteractions
+*/
+QCPAxis::SelectablePart QCPAxis::selectTest(const QPointF &pos) const
+{
+  if (!mVisible)
+    return spNone;
+  
+  if (mAxisSelectionBox.contains(pos.toPoint()))
+    return spAxis;
+  else if (mTickLabelsSelectionBox.contains(pos.toPoint()))
+    return spTickLabels;
+  else if (mLabelSelectionBox.contains(pos.toPoint()))
+    return spAxisLabel;
+  else
+    return spNone;
+}
+
+/*! \internal
+  
+  This function is called before the grid and axis is drawn, in order to prepare the tick vector,
+  sub tick vector and tick label vector. If \ref setAutoTicks is set to true, appropriate tick
+  values are determined automatically via \ref generateAutoTicks. If it's set to false, the signal
+  ticksRequest is emitted, which can be used to provide external tick positions. Then the sub tick
+  vectors and tick label vectors are created.
+*/
+void QCPAxis::setupTickVectors()
+{
+  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
+  
+  // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
+  if (mAutoTicks)
+  {
+    generateAutoTicks();
+  } else
+  {
+    emit ticksRequest();
+  }
+  
+  visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick);
+  if (mTickVector.isEmpty())
+  {
+    mSubTickVector.clear();
+    return;
+  }
+  
+  // generate subticks between ticks:
+  mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
+  if (mSubTickCount > 0)
+  {
+    double subTickStep = 0;
+    double subTickPosition = 0;
+    int subTickIndex = 0;
+    bool done = false;
+    for (int i=1; i<mTickVector.size(); ++i)
+    {
+      subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
+      for (int k=1; k<=mSubTickCount; ++k)
+      {
+        subTickPosition = mTickVector.at(i-1) + k*subTickStep;
+        if (subTickPosition < mRange.lower)
+          continue;
+        if (subTickPosition > mRange.upper)
+        {
+          done = true;
+          break;
+        }
+        mSubTickVector[subTickIndex] = subTickPosition;
+        subTickIndex++;
+      }
+      if (done) break;
+    }
+    mSubTickVector.resize(subTickIndex);
+  }
+
+  // generate tick labels according to tick positions:
+  mExponentialChar = mParentPlot->locale().exponential();   // will be needed when drawing the numbers generated here, in drawTickLabel()
+  mPositiveSignChar = mParentPlot->locale().positiveSign(); // will be needed when drawing the numbers generated here, in drawTickLabel()
+  if (mAutoTickLabels)
+  {
+    int vecsize = mTickVector.size();
+    mTickVectorLabels.resize(vecsize);
+    if (mTickLabelType == ltNumber)
+    {
+      for (int i=0; i<vecsize; ++i)
+        mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar, mNumberPrecision);
+    } else if (mTickLabelType == ltDateTime)
+    {
+      for (int i=0; i<vecsize; ++i)
+      {
+#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) // use fromMSecsSinceEpoch function if available, to gain sub-second accuracy on tick labels (e.g. for format "hh:mm:ss:zzz")
+        mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)), mDateTimeFormat);
+#else
+        mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000), mDateTimeFormat);
+#endif
+      }
+    }
+  } else // mAutoTickLabels == false
+  {
+    if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
+    {
+      emit ticksRequest();
+    }
+    // make sure provided tick label vector has correct (minimal) length:
+    if (mTickVectorLabels.size() < mTickVector.size())
+      mTickVectorLabels.resize(mTickVector.size());
+  }
+}
+
+/*! \internal
+  
+  If \ref setAutoTicks is set to true, this function is called by \ref setupTickVectors to
+  generate reasonable tick positions (and subtick count). The algorithm tries to create
+  approximately <tt>mAutoTickCount</tt> ticks (set via \ref setAutoTickCount), taking into account,
+  that tick mantissas that are divisable by two or end in .5 are nice to look at and practical in
+  linear scales. If the scale is logarithmic, one tick is generated at every power of the current
+  logarithm base, set via \ref setScaleLogBase.
+*/
+void QCPAxis::generateAutoTicks()
+{
+  if (mScaleType == stLinear)
+  {
+    if (mAutoTickStep)
+    {
+      // Generate tick positions according to linear scaling:
+      mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
+      double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
+      double tickStepMantissa = mTickStep/magnitudeFactor;
+      if (tickStepMantissa < 5)
+      {
+        // round digit after decimal point to 0.5
+        mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
+      } else
+      {
+        // round to first digit in multiples of 2
+        mTickStep = (int)((tickStepMantissa/10.0)*5)/5.0*10*magnitudeFactor;
+      }
+    }
+    if (mAutoSubTicks)
+      mSubTickCount = calculateAutoSubTickCount(mTickStep);
+    // Generate tick positions according to mTickStep:
+    int firstStep = floor(mRange.lower/mTickStep);
+    int lastStep = ceil(mRange.upper/mTickStep);
+    int tickcount = lastStep-firstStep+1;
+    if (tickcount < 0) tickcount = 0;
+    mTickVector.resize(tickcount);
+    for (int i=0; i<tickcount; ++i)
+    {
+      mTickVector[i] = (firstStep+i)*mTickStep;
+    }
+  } else // mScaleType == stLogarithmic
+  {
+    // Generate tick positions according to logbase scaling:
+    if (mRange.lower > 0 && mRange.upper > 0) // positive range
+    {
+      double lowerMag = basePow((int)floor(baseLog(mRange.lower)));
+      double currentMag = lowerMag;
+      mTickVector.clear();
+      mTickVector.append(currentMag);
+      while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
+      {
+        currentMag *= mScaleLogBase;
+        mTickVector.append(currentMag);
+      }
+    } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
+    {
+      double lowerMag = -basePow((int)ceil(baseLog(-mRange.lower)));
+      double currentMag = lowerMag;
+      mTickVector.clear();
+      mTickVector.append(currentMag);
+      while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
+      {
+        currentMag /= mScaleLogBase;
+        mTickVector.append(currentMag);
+      }
+    } else // invalid range for logarithmic scale, because lower and upper have different sign
+    {
+      mTickVector.clear();
+      qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
+    }
+  }
+}
+
+/*! \internal
+  
+  Called by generateAutoTicks when \ref setAutoSubTicks is set to true. Depending on the \a
+  tickStep between two major ticks on the axis, a different number of sub ticks is appropriate. For
+  Example taking 4 sub ticks for a \a tickStep of 1 makes more sense than taking 5 sub ticks,
+  because this corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16666. Note
+  that a subtick count of 4 means dividing the major tick step into 5 sections.
+  
+  This is implemented by a hand made lookup for integer tick steps as well as fractional tick steps
+  with a fractional part of (approximately) 0.5. If a tick step is different (i.e. has no
+  fractional part close to 0.5), the currently set sub tick count (\ref setSubTickCount) is
+  returned.
+*/
+int QCPAxis::calculateAutoSubTickCount(double tickStep) const
+{
+  int result = mSubTickCount; // default to current setting, if no proper value can be found
+  
+  // get mantissa of tickstep:
+  double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
+  double tickStepMantissa = tickStep/magnitudeFactor;
+  
+  // separate integer and fractional part of mantissa:
+  double epsilon = 0.01;
+  double intPartf;
+  int intPart;
+  double fracPart = modf(tickStepMantissa, &intPartf);
+  intPart = intPartf;
+  
+  // handle cases with (almost) integer mantissa:
+  if (fracPart < epsilon || 1.0-fracPart < epsilon)
+  {
+    if (1.0-fracPart < epsilon)
+      intPart++;
+    switch (intPart)
+    {
+      case 1: result = 4; break; // 1.0 -> 0.2 substep
+      case 2: result = 3; break; // 2.0 -> 0.5 substep
+      case 3: result = 2; break; // 3.0 -> 1.0 substep
+      case 4: result = 3; break; // 4.0 -> 1.0 substep
+      case 5: result = 4; break; // 5.0 -> 1.0 substep
+      case 6: result = 2; break; // 6.0 -> 2.0 substep
+      case 7: result = 6; break; // 7.0 -> 1.0 substep
+      case 8: result = 3; break; // 8.0 -> 2.0 substep
+      case 9: result = 2; break; // 9.0 -> 3.0 substep
+    }
+  } else
+  {
+    // handle cases with significantly fractional mantissa:
+    if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
+    {
+      switch (intPart)
+      {
+        case 1: result = 2; break; // 1.5 -> 0.5 substep
+        case 2: result = 4; break; // 2.5 -> 0.5 substep
+        case 3: result = 4; break; // 3.5 -> 0.7 substep
+        case 4: result = 2; break; // 4.5 -> 1.5 substep
+        case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
+        case 6: result = 4; break; // 6.5 -> 1.3 substep
+        case 7: result = 2; break; // 7.5 -> 2.5 substep
+        case 8: result = 4; break; // 8.5 -> 1.7 substep
+        case 9: result = 4; break; // 9.5 -> 1.9 substep
+      }
+    }
+    // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
+  }
+  
+  return result;
+}
+
+/*! \internal
+  
+  The main draw function of an axis, called by QCustomPlot::draw for each axis. Draws axis
+  baseline, major ticks, subticks, tick labels and axis label.
+  
+  The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
+  here, too.
+*/
+void QCPAxis::draw(QCPPainter *painter)
+{
+  QPoint origin;
+  if (mAxisType == atLeft)
+    origin = mAxisRect.bottomLeft();
+  else if (mAxisType == atRight)
+    origin = mAxisRect.bottomRight();
+  else if (mAxisType == atTop)
+    origin = mAxisRect.topLeft();
+  else if (mAxisType == atBottom)
+    origin = mAxisRect.bottomLeft();
+  
+  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
+  switch (mAxisType)
+  {
+    case atTop: yCor = -1; break;
+    case atRight: xCor = 1; break;
+    default: break;
+  }
+  
+  int margin = 0;
+  int lowTick = mLowestVisibleTick;
+  int highTick = mHighestVisibleTick;
+  double t; // helper variable, result of coordinate-to-pixel transforms
+
+  // draw baseline:
+  painter->setPen(getBasePen());
+  if (orientation() == Qt::Horizontal)
+    painter->drawLine(QLineF(origin+QPointF(xCor, yCor), origin+QPointF(mAxisRect.width()+xCor, yCor)));
+  else
+    painter->drawLine(QLineF(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -mAxisRect.height()+yCor)));
+  
+  // draw ticks:
+  if (mTicks)
+  {
+    painter->setPen(getTickPen());
+    // direction of ticks ("inward" is right for left axis and left for right axis)
+    int tickDir = (mAxisType == atBottom || mAxisType == atRight) ? -1 : 1;
+    if (orientation() == Qt::Horizontal)
+    {
+      for (int i=lowTick; i <= highTick; ++i)
+      {
+        t = coordToPixel(mTickVector.at(i)); // x
+        painter->drawLine(QLineF(t+xCor, origin.y()-mTickLengthOut*tickDir+yCor, t+xCor, origin.y()+mTickLengthIn*tickDir+yCor));
+      }
+    } else
+    {
+      for (int i=lowTick; i <= highTick; ++i)
+      {
+        t = coordToPixel(mTickVector.at(i)); // y
+        painter->drawLine(QLineF(origin.x()-mTickLengthOut*tickDir+xCor, t+yCor, origin.x()+mTickLengthIn*tickDir+xCor, t+yCor));
+      }
+    }
+  }
+  
+  // draw subticks:
+  if (mTicks && mSubTickCount > 0)
+  {
+    painter->setPen(getSubTickPen());
+    // direction of ticks ("inward" is right for left axis and left for right axis)
+    int tickDir = (mAxisType == atBottom || mAxisType == atRight) ? -1 : 1;
+    if (orientation() == Qt::Horizontal)
+    {
+      for (int i=0; i<mSubTickVector.size(); ++i) // no need to check bounds because subticks are always only created inside current mRange
+      {
+        t = coordToPixel(mSubTickVector.at(i));
+        painter->drawLine(QLineF(t+xCor, origin.y()-mSubTickLengthOut*tickDir+yCor, t+xCor, origin.y()+mSubTickLengthIn*tickDir+yCor));
+      }
+    } else
+    {
+      for (int i=0; i<mSubTickVector.size(); ++i)
+      {
+        t = coordToPixel(mSubTickVector.at(i));
+        painter->drawLine(QLineF(origin.x()-mSubTickLengthOut*tickDir+xCor, t+yCor, origin.x()+mSubTickLengthIn*tickDir+xCor, t+yCor));
+      }
+    }
+  }
+  margin += qMax(0, qMax(mTickLengthOut, mSubTickLengthOut));
+  
+  // tick labels:
+  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
+  if (mTickLabels)
+  {
+    margin += mTickLabelPadding;
+    painter->setFont(getTickLabelFont());
+    painter->setPen(QPen(getTickLabelColor()));
+    for (int i=lowTick; i <= highTick; ++i)
+    {
+      t = coordToPixel(mTickVector.at(i));
+      drawTickLabel(painter, t, margin, mTickVectorLabels.at(i), &tickLabelsSize);
+    }
+  }
+  if (orientation() == Qt::Horizontal)
+    margin += tickLabelsSize.height();
+  else
+    margin += tickLabelsSize.width();
+
+  // axis label:
+  QRect labelBounds;
+  if (!mLabel.isEmpty())
+  {
+    margin += mLabelPadding;
+    painter->setFont(getLabelFont());
+    painter->setPen(QPen(getLabelColor()));
+    labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, mLabel);
+    if (mAxisType == atLeft)
+    {
+      QTransform oldTransform = painter->transform();
+      painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
+      painter->rotate(-90);
+      painter->drawText(0, 0, mAxisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
+      painter->setTransform(oldTransform);
+    }
+    else if (mAxisType == atRight)
+    {
+      QTransform oldTransform = painter->transform();
+      painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-mAxisRect.height());
+      painter->rotate(90);
+      painter->drawText(0, 0, mAxisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
+      painter->setTransform(oldTransform);
+    }
+    else if (mAxisType == atTop)
+      painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), mAxisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
+    else if (mAxisType == atBottom)
+      painter->drawText(origin.x(), origin.y()+margin, mAxisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
+  }
+  
+  // set selection boxes:
+  int selAxisOutSize = qMax(qMax(mTickLengthOut, mSubTickLengthOut), mParentPlot->selectionTolerance());
+  int selAxisInSize = mParentPlot->selectionTolerance();
+  int selTickLabelSize = (orientation()==Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
+  int selTickLabelOffset = qMax(mTickLengthOut, mSubTickLengthOut)+mTickLabelPadding;
+  int selLabelSize = labelBounds.height();
+  int selLabelOffset = selTickLabelOffset+selTickLabelSize+mLabelPadding;
+  if (mAxisType == atLeft)
+  {
+    mAxisSelectionBox.setCoords(mAxisRect.left()-selAxisOutSize, mAxisRect.top(), mAxisRect.left()+selAxisInSize, mAxisRect.bottom());
+    mTickLabelsSelectionBox.setCoords(mAxisRect.left()-selTickLabelOffset-selTickLabelSize, mAxisRect.top(), mAxisRect.left()-selTickLabelOffset, mAxisRect.bottom());
+    mLabelSelectionBox.setCoords(mAxisRect.left()-selLabelOffset-selLabelSize, mAxisRect.top(), mAxisRect.left()-selLabelOffset, mAxisRect.bottom());
+  } else if (mAxisType == atRight)
+  {
+    mAxisSelectionBox.setCoords(mAxisRect.right()-selAxisInSize, mAxisRect.top(), mAxisRect.right()+selAxisOutSize, mAxisRect.bottom());
+    mTickLabelsSelectionBox.setCoords(mAxisRect.right()+selTickLabelOffset+selTickLabelSize, mAxisRect.top(), mAxisRect.right()+selTickLabelOffset, mAxisRect.bottom());
+    mLabelSelectionBox.setCoords(mAxisRect.right()+selLabelOffset+selLabelSize, mAxisRect.top(), mAxisRect.right()+selLabelOffset, mAxisRect.bottom());
+  } else if (mAxisType == atTop)
+  {
+    mAxisSelectionBox.setCoords(mAxisRect.left(), mAxisRect.top()-selAxisOutSize, mAxisRect.right(), mAxisRect.top()+selAxisInSize);
+    mTickLabelsSelectionBox.setCoords(mAxisRect.left(), mAxisRect.top()-selTickLabelOffset-selTickLabelSize, mAxisRect.right(), mAxisRect.top()-selTickLabelOffset);
+    mLabelSelectionBox.setCoords(mAxisRect.left(), mAxisRect.top()-selLabelOffset-selLabelSize, mAxisRect.right(), mAxisRect.top()-selLabelOffset);
+  } else if (mAxisType == atBottom)
+  {
+    mAxisSelectionBox.setCoords(mAxisRect.left(), mAxisRect.bottom()-selAxisInSize, mAxisRect.right(), mAxisRect.bottom()+selAxisOutSize);
+    mTickLabelsSelectionBox.setCoords(mAxisRect.left(), mAxisRect.bottom()+selTickLabelOffset+selTickLabelSize, mAxisRect.right(), mAxisRect.bottom()+selTickLabelOffset);
+    mLabelSelectionBox.setCoords(mAxisRect.left(), mAxisRect.bottom()+selLabelOffset+selLabelSize, mAxisRect.right(), mAxisRect.bottom()+selLabelOffset);
+  }
+  // draw hitboxes for debug purposes:
+  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
+}
+
+/*! \internal
+  
+  Draws a single tick label with the provided \a painter. The tick label is always bound to an axis
+  in one direction (distance to axis in that direction is however controllable via \a
+  distanceToAxis in pixels). The position in the other direction is passed in the \a position
+  parameter. Hence for the bottom axis, \a position would indicate the horizontal pixel position
+  (not coordinate!), at which the label should be drawn.
+  
+  In order to draw the axis label after all the tick labels in a position, that doesn't overlap
+  with the tick labels, we need to know the largest tick label size. This is done by passing a \a
+  tickLabelsSize to all \ref drawTickLabel calls during the process of drawing all tick labels of
+  one axis. \a tickLabelSize is only expanded, if the drawn label exceeds the value \a
+  tickLabelsSize currently holds.
+  
+  This function is also responsible for turning ugly exponential numbers "5.5e9" into a more
+  beautifully typeset format "5.5 [multiplication sign] 10 [superscript] 9". This feature is
+  controlled with \ref setNumberFormat.
+  
+  The label is drawn with the font and pen that are currently set on the \a painter. To draw
+  superscripted powers, the font is temporarily made smaller by a fixed factor.
+*/
+void QCPAxis::drawTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
+{
+  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
+  
+  // determine whether beautiful decimal powers should be used
+  bool useBeautifulPowers = false;
+  int ePos = -1;
+  if (mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber)
+  {
+    ePos = text.indexOf('e');
+    if (ePos > -1)
+      useBeautifulPowers = true;
+  }
+  
+  // calculate text bounding rects and do string preparation for beautiful decimal powers:
+  QRect bounds, baseBounds, expBounds;
+  QString basePart, expPart;
+  QFont bugFixFont(painter->font());
+  bugFixFont.setPointSizeF(bugFixFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding 
+  QFont expFont;
+  if (useBeautifulPowers)
+  {
+    // split string parts for part of number/symbol that will be drawn normally and part that will be drawn as exponent:
+    basePart = text.left(ePos);
+    // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
+    if (mScaleType == stLogarithmic && basePart == "1")
+      basePart = "10";
+    else
+      basePart += (mNumberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
+    expPart = text.mid(ePos+1);
+    // clip "+" and leading zeros off expPart:
+    while (expPart.at(1) == '0' && expPart.length() > 2) // length > 2 so we leave one zero when numberFormatChar is 'e'
+      expPart.remove(1, 1);
+    if (expPart.at(0) == mPositiveSignChar)
+      expPart.remove(0, 1);
+    // prepare smaller font for exponent:
+    expFont = painter->font();
+    expFont.setPointSize(expFont.pointSize()*0.75);
+    // calculate bounding rects of base part, exponent part and total one:
+    QFontMetrics fontMetrics(bugFixFont);
+    baseBounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, basePart);
+    QFontMetrics expFontMetrics(expFont);
+    expBounds = expFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, expPart);
+    bounds = baseBounds.adjusted(0, 0, expBounds.width(), 0);
+  } else // useBeautifulPowers == false
+  {
+    QFontMetrics fontMetrics(bugFixFont);
+    bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, text);
+  }
+  
+  // if using rotated tick labels, transform bounding rect, too:
+  QRect rotatedBounds = bounds;
+  if (!qFuzzyIsNull(mTickLabelRotation))
+  {
+    QTransform transform;
+    transform.rotate(mTickLabelRotation);
+    rotatedBounds = transform.mapRect(bounds);
+  }
+  // expand passed tickLabelsSize if current tick label is larger:
+  if (rotatedBounds.width() > tickLabelsSize->width()) 
+    tickLabelsSize->setWidth(rotatedBounds.width());
+  if (rotatedBounds.height() > tickLabelsSize->height())
+    tickLabelsSize->setHeight(rotatedBounds.height());
+  
+  /*
+    calculate coordinates (non-trivial, for best visual appearance): short explanation for bottom
+    axis: The anchor, i.e. the point in the label that is placed horizontally under the
+    corresponding tick is always on the label side that is closer to the axis (e.g. the left side
+    of the text when we're rotating clockwise). On that side, the height is halved and the
+    resulting point is defined the anchor. This way, a 90 degree rotated text will be centered
+    under the tick (i.e. displaced horizontally by half its height). At the same time, a 45 degree
+    rotated text will "point toward" its tick, as is typical for rotated tick labels.
+  */
+  bool doRotation = !qFuzzyIsNull(mTickLabelRotation);
+  double radians = mTickLabelRotation/180.0*M_PI;
+  int x=0,y=0;
+  if (mAxisType == atLeft)
+  {
+    if (doRotation)
+    {
+      if (mTickLabelRotation > 0)
+      {
+        x = mAxisRect.left()-qCos(radians)*bounds.width()-distanceToAxis;
+        y = position-qSin(radians)*bounds.width()-qCos(radians)*bounds.height()/2.0;
+      } else
+      {
+        x = mAxisRect.left()-qCos(-radians)*bounds.width()-qSin(-radians)*bounds.height()-distanceToAxis;
+        y = position+qSin(-radians)*bounds.width()-qCos(-radians)*bounds.height()/2.0;
+      }
+    } else
+    {
+      x = mAxisRect.left()-bounds.width()-distanceToAxis;
+      y = position-bounds.height()/2.0;
+    }
+  } else if (mAxisType == atRight)
+  {
+    if (doRotation)
+    {
+      if (mTickLabelRotation > 0)
+      {
+        x = mAxisRect.right()+qSin(radians)*bounds.height()+distanceToAxis;
+        y = position-qCos(radians)*bounds.height()/2.0;
+      } else
+      {
+        x = mAxisRect.right()+distanceToAxis;
+        y = position-qCos(-radians)*bounds.height()/2.0;
+      }
+    } else
+    {
+      x = mAxisRect.right()+distanceToAxis;
+      y = position-bounds.height()/2.0;
+    }
+  } else if (mAxisType == atTop)
+  {
+    if (doRotation)
+    {
+      if (mTickLabelRotation > 0)
+      {
+        x = position-qCos(radians)*bounds.width()+qSin(radians)*bounds.height()/2.0;
+        y = mAxisRect.top()-qSin(radians)*bounds.width()-qCos(radians)*bounds.height()-distanceToAxis;
+      } else
+      {
+        x = position-qSin(-radians)*bounds.height()/2.0;
+        y = mAxisRect.top()-qCos(-radians)*bounds.height()-distanceToAxis;
+      }
+    } else
+    {
+      x = position-bounds.width()/2.0;
+      y = mAxisRect.top()-bounds.height()-distanceToAxis;
+    }
+  } else if (mAxisType == atBottom)
+  {
+    if (doRotation)
+    {
+      if (mTickLabelRotation > 0)
+      {
+        x = position+qSin(radians)*bounds.height()/2.0;
+        y = mAxisRect.bottom()+distanceToAxis;
+      } else
+      {
+        x = position-qCos(-radians)*bounds.width()-qSin(-radians)*bounds.height()/2.0;
+        y = mAxisRect.bottom()+qSin(-radians)*bounds.width()+distanceToAxis;
+      }
+    } else
+    {
+      x = position-bounds.width()/2.0;
+      y = mAxisRect.bottom()+distanceToAxis;
+    }
+  }
+  
+  // if label would be partly clipped by widget border on sides, don't draw it:
+  if (orientation() == Qt::Horizontal)
+  {
+    if (x+bounds.width() > mParentPlot->mViewport.right() ||
+        x < mParentPlot->mViewport.left())
+      return;
+  } else
+  {
+    if (y+bounds.height() > mParentPlot->mViewport.bottom() ||
+        y < mParentPlot->mViewport.top())
+      return;
+  }
+  
+  // transform painter to position/rotation:
+  QTransform oldTransform = painter->transform();
+  painter->translate(x, y);
+  if (doRotation)
+    painter->rotate(mTickLabelRotation);
+  // draw text:
+  if (useBeautifulPowers)
+  {
+    // draw base:
+    painter->drawText(0, 0, 0, 0, Qt::TextDontClip, basePart);
+    // draw exponent:
+    QFont normalFont = painter->font();
+    painter->setFont(expFont);
+    painter->drawText(baseBounds.width()+1, 0, expBounds.width(), expBounds.height(), Qt::TextDontClip,  expPart);
+    painter->setFont(normalFont);
+  } else // useBeautifulPowers == false
+  {
+    painter->drawText(0, 0, bounds.width(), bounds.height(), Qt::TextDontClip | Qt::AlignHCenter, text);
+  }
+  
+  // reset rotation/translation transform to what it was before:
+  painter->setTransform(oldTransform);
+}
+
+/*! \internal
+  
+  Simulates the steps done by \ref drawTickLabel by calculating bounding boxes of the text label to
+  be drawn, depending on number format etc. Since we only want the largest tick label for the
+  margin calculation, the passed \a tickLabelsSize isn't overridden with the calculated label size,
+  but it's only expanded, if it's currently set to a smaller width/height.
+*/
+void QCPAxis::getMaxTickLabelSize(const QFont &font, const QString &text,  QSize *tickLabelsSize) const
+{
+  // This function does the same as drawTickLabel but omits the actual drawing
+  // changes involve creating extra QFontMetrics instances for font, since painter->fontMetrics() isn't available
+  
+  // determine whether beautiful powers should be used
+  bool useBeautifulPowers = false;
+  int ePos=-1;
+  if (mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber)
+  {
+    ePos = text.indexOf(mExponentialChar);
+    if (ePos > -1)
+      useBeautifulPowers = true;
+  }
+  
+  // calculate and draw text, depending on whether beautiful powers are applicable or not:
+  QRect bounds, baseBounds, expBounds;
+  QString basePart, expPart;
+  QFont bugFixFont(font);
+  bugFixFont.setPointSizeF(bugFixFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding 
+  QFont expFont;
+  if (useBeautifulPowers)
+  {
+    // split string parts for part of number/symbol that will be drawn normally and part that will be drawn as exponent:
+    basePart = text.left(ePos);
+    // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
+    if (mScaleType == stLogarithmic && basePart == "1")
+      basePart = "10";
+    else
+      basePart += (mNumberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
+    expPart = text.mid(ePos+1);
+    // clip "+" and leading zeros off expPart:
+    while (expPart.at(1) == '0' && expPart.length() > 2) // length > 2 so we leave one zero when numberFormatChar is 'e'
+      expPart.remove(1, 1);
+    if (expPart.at(0) == mPositiveSignChar)
+      expPart.remove(0, 1);
+    // prepare smaller font for exponent:
+    expFont = font;
+    expFont.setPointSize(expFont.pointSize()*0.75);
+    // calculate bounding rects of base part, exponent part and total one:
+    QFontMetrics baseFontMetrics(bugFixFont);
+    baseBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, basePart);
+    QFontMetrics expFontMetrics(expFont);
+    expBounds = expFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, expPart);
+    bounds = baseBounds.adjusted(0, 0, expBounds.width(), 0); 
+  } else // useBeautifulPowers == false
+  {
+    QFontMetrics fontMetrics(bugFixFont);
+    bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, text);
+  }
+  
+  // if rotated tick labels, transform bounding rect, too:
+  QRect rotatedBounds = bounds;
+  if (!qFuzzyIsNull(mTickLabelRotation))
+  {
+    QTransform transform;
+    transform.rotate(mTickLabelRotation);
+    rotatedBounds = transform.mapRect(bounds);
+  }
+  
+  // expand passed tickLabelsSize if current tick label is larger:
+  if (rotatedBounds.width() > tickLabelsSize->width()) 
+    tickLabelsSize->setWidth(rotatedBounds.width());
+  if (rotatedBounds.height() > tickLabelsSize->height())
+    tickLabelsSize->setHeight(rotatedBounds.height());
+}
+
+/*! \internal
+  
+  Handles the selection \a event and returns true when the selection event hit any parts of the
+  axis. If the selection state of any parts of the axis was changed, the output parameter \a
+  modified is set to true.
+  
+  When \a additiveSelecton is true, any new selections become selected in addition to the recent
+  selections. The recent selections are not cleared. Further, clicking on one object multiple times
+  in additive selection mode, toggles the selection of that object on and off.
+  
+  To indicate that an event deselects the axis (i.e. the parts that are deselectable by the user,
+  see \ref setSelectable), pass 0 as \a event.
+*/
+bool QCPAxis::handleAxisSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
+{
+  bool selectionFound = false;
+  if (event)
+  {
+    SelectablePart selectedAxisPart = selectTest(event->pos());
+    if (selectedAxisPart == spNone || !selectable().testFlag(selectedAxisPart))
+    {
+      // deselect parts that are changeable (selectable):
+      SelectableParts newState = selected() & ~selectable();
+      if (newState != selected() && !additiveSelection)
+      {
+        modified = true;
+        setSelected(newState);
+      }
+    } else
+    {
+      selectionFound = true;
+      if (additiveSelection)
+      {
+        // additive selection, so toggle selected part:
+        setSelected(selected() ^ selectedAxisPart);
+        modified = true;
+      } else
+      {
+        // not additive selection, so select part and deselect all others that are changeable (selectable):
+        SelectableParts newState = (selected() & ~selectable()) | selectedAxisPart;
+        if (newState != selected())
+        {
+          modified = true;
+          setSelected(newState);
+        }
+      }
+    }
+  } else // event == 0, so deselect all changeable parts
+  {
+    SelectableParts newState = selected() & ~selectable();
+    if (newState != selected())
+    {
+      modified = true;
+      setSelected(newState);
+    }
+  }
+  return selectionFound;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing axis lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+  
+  This function takes into account the local setting of the antialiasing flag as well as
+  the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased
+*/
+void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
+}
+
+/*! \internal
+  
+  Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
+  the current range. The return values are indices of the tick vector, not the positions of the
+  ticks themselves.
+  
+  The actual use of this function is when we have an externally provided tick vector, which might
+  exceed far beyond the currently displayed range, and would cause unnecessary calculations e.g. of
+  subticks.
+*/
+void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
+{
+  lowIndex = 0;
+  highIndex = -1;
+  // make sure only ticks that are in visible range are returned
+  for (int i=0; i < mTickVector.size(); ++i)
+  {
+    lowIndex = i;
+    if (mTickVector.at(i) >= mRange.lower) break;
+  }
+  for (int i=mTickVector.size()-1; i >= 0; --i)
+  {
+    highIndex = i;
+    if (mTickVector.at(i) <= mRange.upper) break;
+  }
+}
+
+/*! \internal
+  
+  A log function with the base mScaleLogBase, used mostly for coordinate transforms in logarithmic
+  scales with arbitrary log base. Uses the buffered mScaleLogBaseLogInv for faster calculation.
+  This is set to <tt>1.0/qLn(mScaleLogBase)</tt> in \ref setScaleLogBase.
+  
+  \see basePow, setScaleLogBase, setScaleType
+*/
+double QCPAxis::baseLog(double value) const
+{
+  return qLn(value)*mScaleLogBaseLogInv;
+}
+
+/*! \internal
+  
+  A power function with the base mScaleLogBase, used mostly for coordinate transforms in
+  logarithmic scales with arbitrary log base.
+  
+  \see baseLog, setScaleLogBase, setScaleType
+*/
+double QCPAxis::basePow(double value) const
+{
+  return qPow(mScaleLogBase, value);
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the axis base line. Depending on the selection state, this
+  is either mSelectedBasePen or mBasePen.
+*/
+QPen QCPAxis::getBasePen() const
+{
+  return mSelected.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
+  is either mSelectedTickPen or mTickPen.
+*/
+QPen QCPAxis::getTickPen() const
+{
+  return mSelected.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
+}
+
+/*! \internal
+  
+  Returns the pen that is used to draw the subticks. Depending on the selection state, this
+  is either mSelectedSubTickPen or mSubTickPen.
+*/
+QPen QCPAxis::getSubTickPen() const
+{
+  return mSelected.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
+}
+
+/*! \internal
+  
+  Returns the font that is used to draw the tick labels. Depending on the selection state, this
+  is either mSelectedTickLabelFont or mTickLabelFont.
+*/
+QFont QCPAxis::getTickLabelFont() const
+{
+  return mSelected.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
+}
+
+/*! \internal
+  
+  Returns the font that is used to draw the axis label. Depending on the selection state, this
+  is either mSelectedLabelFont or mLabelFont.
+*/
+QFont QCPAxis::getLabelFont() const
+{
+  return mSelected.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
+}
+
+/*! \internal
+  
+  Returns the color that is used to draw the tick labels. Depending on the selection state, this
+  is either mSelectedTickLabelColor or mTickLabelColor.
+*/
+QColor QCPAxis::getTickLabelColor() const
+{
+  return mSelected.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
+}
+
+/*! \internal
+  
+  Returns the color that is used to draw the axis label. Depending on the selection state, this
+  is either mSelectedLabelColor or mLabelColor.
+*/
+QColor QCPAxis::getLabelColor() const
+{
+  return mSelected.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
+}
+
+/*! \internal
+  
+  Simulates the steps of \ref draw by calculating all appearing text bounding boxes. From this
+  information, the appropriate margin for this axis is determined, so nothing is drawn beyond the
+  widget border in the actual \ref draw function (if \ref QCustomPlot::setAutoMargin is set to
+  true).
+  
+  The margin consists of: tick label padding, tick label size, label padding, label size. The
+  return value is the calculated margin for this axis. Thus, an axis with axis type \ref atLeft
+  will return an appropriate left margin, \ref atBottom will return an appropriate bottom margin
+  and so forth.
+  
+  \warning if anything is changed in this function, make sure it's synchronized with the actual
+  drawing function \ref draw.
+*/
+int QCPAxis::calculateMargin() const
+{
+  // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
+  int margin = 0;
+  
+  if (mVisible)
+  {
+    int lowTick, highTick;
+    visibleTickBounds(lowTick, highTick);
+    // get length of tick marks reaching outside axis rect:
+    margin += qMax(0, qMax(mTickLengthOut, mSubTickLengthOut));
+    // calculate size of tick labels:
+    QSize tickLabelsSize(0, 0);
+    if (mTickLabels)
+    {
+      for (int i=lowTick; i <= highTick; ++i)
+      {
+        getMaxTickLabelSize(mTickLabelFont, mTickVectorLabels.at(i), &tickLabelsSize); // don't use getTickLabelFont() because we don't want margin to possibly change on selection
+      }
+      if (orientation() == Qt::Horizontal)
+        margin += tickLabelsSize.height() + mTickLabelPadding;
+      else
+        margin += tickLabelsSize.width() + mTickLabelPadding;
+    }
+    // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
+    if (!mLabel.isEmpty())
+    {
+      QFontMetrics fontMetrics(mLabelFont); // don't use getLabelFont() because we don't want margin to possibly change on selection
+      QRect bounds;
+      bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, mLabel);
+      margin += bounds.height() + mLabelPadding;
+    }
+  }
+  margin += mPadding;
+  
+  if (margin < 15) // need a bit of margin if no axis text is shown at all (i.e. only baseline and tick lines, or no axis at all)
+    margin = 15;
+  return margin;
+}
+
+
+// ================================================================================
+// =================== QCustomPlot
+// ================================================================================
+
+/*! \class QCustomPlot
+  \brief The central class of the library, the QWidget which displays the plot and interacts with the user.
+  
+  For tutorials on how to use QCustomPlot, see the website\n
+  http://www.WorksLikeClockWork.com/index.php/components/qt-plotting-widget
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QRect QCustomPlot::viewport() const
+  
+  Returns the viewport rect of this QCustomPlot instance. The viewport is the area the plot is
+  drawn in, all mechanisms, e.g. margin caluclation take the viewport to be the outer border of the
+  plot. The viewport normally is the rect() of the QCustomPlot widget, i.e. a rect with top left
+  (0, 0) and size of the QCustomPlot widget.
+  
+  Don't confuse the viewport with the axisRect. An axisRect is the rect defined by two axes, where
+  the graphs/plottables are drawn in. The viewport is larger and contains also the axes themselves, their
+  tick numbers, their labels, the plot title etc.
+  
+  Only when saving to a file (see \ref savePng, savePdf etc.) the viewport is temporarily modified
+  to allow saving plots with sizes independent of the current widget size.
+*/
+
+/* end of documentation of inline functions */
+/* start of documentation of signals */
+
+/*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse double click event.
+*/
+
+/*! \fn void QCustomPlot::mousePress(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse press event.
+  
+  It is emitted before the QCustomPlot handles its range dragging mechanism, so a slot connected to
+  this signal can still influence the behaviour e.g. with \ref setRangeDrag or \ref
+  setRangeDragAxes.
+*/
+
+/*! \fn void QCustomPlot::mouseMove(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse move event.
+  
+  It is emitted before the QCustomPlot handles its range dragging mechanism, so a slot connected to
+  this signal can still influence the behaviour e.g. with \ref setRangeDrag.
+  
+  \warning It is discouraged to change the drag-axes with \ref setRangeDragAxes here, because the
+  dragging starting point was saved the moment the mouse was pressed. Thus it only has a sensible
+  meaning for the range drag axes that were set at that moment. If you want to change the drag
+  axes, consider doing this in the \ref mousePress signal instead.
+*/
+
+/*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse release event.
+  
+  It is emitted before the QCustomPlot handles its selection mechanism, so a slot connected to this
+  signal can still influence the behaviour e.g. with \ref setInteractions or \ref
+  QCPAbstractPlottable::setSelectable.
+*/
+
+/*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse wheel event.
+  
+  It is emitted before the QCustomPlot handles its range zooming mechanism, so a slot connected to
+  this signal can still influence the behaviour e.g. with \ref setRangeZoom, \ref setRangeZoomAxes
+  or \ref setRangeZoomFactor.
+*/
+
+/*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
+  
+  This signal is emitted when a plottable is clicked.
+
+  \a event is the mouse event that caused the click and \a plottable is the plottable that received
+  the click.
+  
+  \see plottableDoubleClick
+*/
+
+/*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
+  
+  This signal is emitted when a plottable is double clicked.
+  
+  \a event is the mouse event that caused the click and \a plottable is the plottable that received
+  the click.
+  
+  \see plottableClick
+*/
+
+/*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)
+  
+  This signal is emitted when an item is clicked.
+
+  \a event is the mouse event that caused the click and \a item is the item that received the
+  click.
+  
+  \see itemDoubleClick
+*/
+
+/*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
+  
+  This signal is emitted when an item is double clicked.
+  
+  \a event is the mouse event that caused the click and \a item is the item that received the
+  click.
+  
+  \see itemClick
+*/
+
+/*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
+  
+  This signal is emitted when an axis is clicked.
+  
+  \a event is the mouse event that caused the click, \a axis is the axis that received the click and
+  \a part indicates the part of the axis that was clicked.
+  
+  \see axisDoubleClick
+*/
+
+/*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
+
+  This signal is emitted when an axis is double clicked.
+  
+  \a event is the mouse event that caused the click, \a axis is the axis that received the click and
+  \a part indicates the part of the axis that was clicked.
+  
+  \see axisClick
+*/
+
+/*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
+
+  This signal is emitted when a legend (item) is clicked.
+  
+  \a event is the mouse event that caused the click, \a legend is the legend that received the
+  click and \a item is the legend item that received the click. If only the legend and no item is
+  clicked, \a item is 0 (e.g. a click inside the legend padding, which is not part of any item).
+  
+  \see legendDoubleClick
+*/
+
+/*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend,  QCPAbstractLegendItem *item, QMouseEvent *event)
+
+  This signal is emitted when a legend (item) is double clicked.
+  
+  \a event is the mouse event that caused the click, \a legend is the legend that received the
+  click and \a item is the legend item that received the click. If only the legend and no item is
+  clicked, \a item is 0 (e.g. a click inside the legend padding, which is not part of any item).
+  
+  \see legendClick
+*/
+
+/*! \fn void QCustomPlot:: titleClick(QMouseEvent *event)
+
+  This signal is emitted when the plot title is clicked.
+  
+  \a event is the mouse event that caused the click.
+  
+  \see titleDoubleClick
+*/
+
+/*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event)
+
+  This signal is emitted when the plot title is double clicked.
+  
+  \a event is the mouse event that caused the click.
+  
+  \see titleClick
+*/
+
+/*! \fn void QCustomPlot::selectionChangedByUser()
+  
+  This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by
+  clicking. It is not emitted, when the selection state of an object has changed programmatically,
+  e.g. by a direct call to setSelected() on a plottable or by calling \ref deselectAll.
+  
+  See the documentation of \ref setInteractions for how to find out which objects are currently
+  selected.
+  
+  \see setInteractions, QCPAbstractPlottable::selectionChanged, QCPAxis::selectionChanged
+*/
+
+/*! \fn void QCustomPlot::beforeReplot()
+  
+  This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref
+  replot).
+  
+  It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
+  replot synchronously (i.e. it won't cause an infinite recursion).
+  
+  \see replot, afterReplot
+*/
+
+/*! \fn void QCustomPlot::afterReplot()
+  
+  This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref
+  replot).
+  
+  It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
+  replot synchronously (i.e. it won't cause an infinite recursion).
+  
+  \see replot, beforeReplot
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs a QCustomPlot and sets reasonable default values.
+  Four axes are created at the bottom, left, top and right sides (xAxis, yAxis, xAxis2, yAxis2),
+  however, only the bottom and left axes are set to be visible.
+  The legend is also set to be invisible initially.
+*/
+QCustomPlot::QCustomPlot(QWidget *parent) :
+  QWidget(parent),
+  mDragging(false),
+  mReplotting(false),
+  mPlottingHints(QCP::phNone)
+{
+  setAttribute(Qt::WA_NoMousePropagation);
+  setAttribute(Qt::WA_OpaquePaintEvent);
+  setMouseTracking(true);
+  QLocale currentLocale = locale();
+  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
+  setLocale(currentLocale);
+  
+  // create very first layers:
+  QCPLayer *gridLayer = new QCPLayer(this, "grid");
+  QCPLayer *mainLayer = new QCPLayer(this, "main");
+  QCPLayer *axesLayer = new QCPLayer(this, "axes");
+  mLayers.append(gridLayer);
+  mLayers.append(mainLayer);
+  mLayers.append(axesLayer);
+  setCurrentLayer(mainLayer);
+
+  mPaintBuffer = QPixmap(size());
+  legend = new QCPLegend(this);
+  legend->setVisible(false);
+  legend->setLayer(axesLayer);
+  xAxis = new QCPAxis(this, QCPAxis::atBottom);
+  yAxis = new QCPAxis(this, QCPAxis::atLeft);
+  xAxis2 = new QCPAxis(this, QCPAxis::atTop);
+  yAxis2 = new QCPAxis(this, QCPAxis::atRight);
+  xAxis2->setGrid(false);
+  yAxis2->setGrid(false);
+  xAxis2->setZeroLinePen(Qt::NoPen);
+  yAxis2->setZeroLinePen(Qt::NoPen);
+  xAxis2->setVisible(false);
+  yAxis2->setVisible(false);
+  xAxis->setLayer(axesLayer);
+  yAxis->setLayer(axesLayer);
+  xAxis2->setLayer(axesLayer);
+  yAxis2->setLayer(axesLayer);
+  xAxis->mGrid->setLayer(gridLayer);
+  yAxis->mGrid->setLayer(gridLayer);
+  xAxis2->mGrid->setLayer(gridLayer);
+  yAxis2->mGrid->setLayer(gridLayer);
+  mViewport = rect();
+  
+  setNoAntialiasingOnDrag(false);
+  setAutoAddPlottableToLegend(true);
+  setAxisBackgroundScaled(true);
+  setAxisBackgroundScaledMode(Qt::KeepAspectRatioByExpanding);
+  setTitleFont(QFont(font().family(), 14, QFont::Bold));
+  setTitleColor(Qt::black);
+  setSelectedTitleFont(QFont(font().family(), 14, QFont::Bold));
+  setSelectedTitleColor(Qt::blue);
+  setTitleSelected(false);
+  setTitle("");
+  setColor(Qt::white);
+  
+#ifdef Q_WS_WIN
+  setPlottingHint(QCP::phForceRepaint);
+#endif
+  setAntialiasedElements(QCP::aeNone);
+  setNotAntialiasedElements(QCP::aeNone);
+  setInteractions(iRangeDrag|iRangeZoom);
+  setMultiSelectModifier(Qt::ControlModifier);
+  setRangeDragAxes(xAxis, yAxis);
+  setRangeZoomAxes(xAxis, yAxis);
+  setRangeDrag(0);
+  setRangeZoom(0);
+  setRangeZoomFactor(0.85);
+  setSelectionTolerance(8);
+  
+  setMargin(0, 0, 0, 0); // also initializes the mAxisRect
+  setAutoMargin(true);
+  replot();
+}
+
+QCustomPlot::~QCustomPlot()
+{
+  clearPlottables();
+  clearItems();
+  delete legend;
+  delete xAxis;
+  delete yAxis;
+  delete xAxis2;
+  delete yAxis2;
+  qDeleteAll(mLayers);
+  mLayers.clear();
+}
+
+/*!
+  Returns the range drag axis of the \a orientation provided
+  \see setRangeDragAxes
+*/
+QCPAxis *QCustomPlot::rangeDragAxis(Qt::Orientation orientation)
+{
+  return (orientation == Qt::Horizontal ? mRangeDragHorzAxis : mRangeDragVertAxis);
+}
+
+/*!
+  Returns the range zoom axis of the \a orientation provided
+  \see setRangeZoomAxes
+*/
+QCPAxis *QCustomPlot::rangeZoomAxis(Qt::Orientation orientation)
+{
+  return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis : mRangeZoomVertAxis);
+}
+
+/*!
+  Returns the range zoom factor of the \a orientation provided
+  \see setRangeZoomFactor
+*/
+double QCustomPlot::rangeZoomFactor(Qt::Orientation orientation)
+{
+  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
+}
+
+/*!
+  Sets the plot title which will be drawn centered at the top of the widget.
+  The title position is not dependant on the actual position of the axes. However, if
+  \ref setAutoMargin is set to true, the top margin will be adjusted appropriately,
+  so the top axis labels/tick labels will not overlap with the title.
+  
+  \see setTitleFont, setTitleColor
+*/
+void QCustomPlot::setTitle(const QString &title)
+{
+  mTitle = title;
+}
+
+/*!
+  Sets the font of the plot title
+  \see setTitleColor, setTitle
+*/
+void QCustomPlot::setTitleFont(const QFont &font)
+{
+  mTitleFont = font;
+}
+
+/*!
+  Sets the text color of the plot title
+  \see setTitleFont, setTitle
+*/
+void QCustomPlot::setTitleColor(const QColor &color)
+{
+  mTitleColor = color;
+}
+
+/*!
+  An alternative way to set the margins, by directly setting the wanted axis rect. The rect
+  will be translated into appropriate margin values.
+  
+  \warning Setting the axis rect with this function does not guarantee that the axis rect will stay
+  like this indefinitely. In QCustomPlot, margins are the fixed values (if \ref setAutoMargin is
+  false). Hence the axis rect is automatically changed when the widget size changes, but the
+  margins (distances between axis rect sides and widget/viewport rect sides) stay the same.
+
+  \see setMargin
+*/
+void QCustomPlot::setAxisRect(const QRect &arect)
+{
+  mMarginLeft = arect.left()-mViewport.left();
+  mMarginRight = mViewport.right()-arect.right();
+  mMarginTop = arect.top()-mViewport.top();
+  mMarginBottom = mViewport.bottom()-arect.bottom();
+  updateAxisRect();
+}
+
+/*!
+  Sets the left margin manually. Will only have effect, if \ref setAutoMargin is set to false.
+  see \ref setMargin for an explanation of what margins mean in QCustomPlot.
+*/
+void QCustomPlot::setMarginLeft(int margin)
+{
+  mMarginLeft = margin;
+  updateAxisRect();
+}
+
+/*!
+  Sets the right margin manually. Will only have effect, if \ref setAutoMargin is set to false.
+  see \ref setMargin for an explanation of what margins mean in QCustomPlot.
+*/
+void QCustomPlot::setMarginRight(int margin)
+{
+  mMarginRight = margin;
+  updateAxisRect();
+}
+
+/*!
+  Sets the top margin manually. Will only have effect, if \ref setAutoMargin is set to false.
+  see \ref setMargin for an explanation of what margins mean in QCustomPlot.
+*/
+void QCustomPlot::setMarginTop(int margin)
+{
+  mMarginTop = margin;
+  updateAxisRect();
+}
+
+/*!
+  Sets the bottom margin manually. Will only have effect, if \ref setAutoMargin is set to false.
+  see \ref setMargin for an explanation of what margins mean in QCustomPlot.
+*/
+void QCustomPlot::setMarginBottom(int margin)
+{
+  mMarginBottom = margin;
+  updateAxisRect();
+}
+
+/*!
+  Sets the margins manually. Will only have effect, if \ref setAutoMargin is set to false.
+  The margins are the distances in pixels between the axes box and the viewport box.
+  The viewport box normally is the entire QCustomPlot widget or the entire image, if
+  using one of the export functions. Positive margin values always mean the axes box
+  is shrinked, going inward from the sides of the viewport box.
+*/
+void QCustomPlot::setMargin(int left, int right, int top, int bottom)
+{
+  mMarginLeft = left;
+  mMarginRight = right;
+  mMarginTop = top;
+  mMarginBottom = bottom;
+  updateAxisRect();
+}
+
+/*!
+  Sets whether the margins are calculated automatically depeding on the sizes
+  of the tick labels, axis labels, paddings etc.
+  If disabled, the margins must be set manually with the \a setMargin functions.
+  \see setMargin, QCPAxis::setLabelPadding, QCPAxis::setTickLabelPadding
+*/
+void QCustomPlot::setAutoMargin(bool enabled)
+{
+  mAutoMargin = enabled;
+}
+
+/*!
+  Sets the background color of the QCustomPlot widget.
+*/
+void QCustomPlot::setColor(const QColor &color)
+{
+  mColor = color;
+}
+
+/*!
+  Sets which axis orientation may be range dragged by the user with mouse interaction.
+  What orientation corresponds to which specific axis can be set with
+  \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
+  default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
+  is the left axis (yAxis).
+  
+  To disable range dragging entirely, pass 0 as \a orientations or remove \ref iRangeDrag from \ref
+  setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
+  Qt::Vertical</tt> as \a orientations.
+  
+  In addition to setting \a orientations to a non-zero value, make sure \ref setInteractions
+  contains \ref iRangeDrag to enable the range dragging interaction.
+  
+  \see setRangeZoom, setRangeDragAxes, setNoAntialiasingOnDrag
+*/
+void QCustomPlot::setRangeDrag(Qt::Orientations orientations)
+{
+  mRangeDrag = orientations;
+}
+
+/*!
+  Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
+  corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
+  QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
+  axis is the left axis (yAxis).
+
+  To disable range zooming entirely, pass 0 as \a orientations or remove \ref iRangeZoom from \ref
+  setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
+  Qt::Vertical</tt> as \a orientations.
+  
+  In addition to setting \a orientations to a non-zero value, make sure \ref setInteractions
+  contains \ref iRangeZoom to enable the range zooming interaction.
+  
+  \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
+*/
+void QCustomPlot::setRangeZoom(Qt::Orientations orientations)
+{
+  mRangeZoom = orientations;
+}
+
+/*!
+  Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
+  on the QCustomPlot widget.
+  
+  \see setRangeZoomAxes
+*/
+void QCustomPlot::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
+{
+  if (horizontal)
+    mRangeDragHorzAxis = horizontal;
+  if (vertical)
+    mRangeDragVertAxis = vertical;
+}
+
+/*!
+  Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
+  QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
+  are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
+  
+  \see setRangeDragAxes
+*/
+void QCustomPlot::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
+{
+  if (horizontal)
+    mRangeZoomHorzAxis = horizontal;
+  if (vertical)
+    mRangeZoomVertAxis = vertical;
+}
+
+/*!
+  Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
+  \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
+  let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
+  and which is vertical, can be set with \ref setRangeZoomAxes.
+
+  When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
+  will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
+  same scrolling direction will zoom out.
+*/
+void QCustomPlot::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
+{
+  mRangeZoomFactorHorz = horizontalFactor;
+  mRangeZoomFactorVert = verticalFactor;
+}
+
+/*! \overload
+  
+  Sets both the horizontal and vertical zoom \a factor.
+*/
+void QCustomPlot::setRangeZoomFactor(double factor)
+{
+  mRangeZoomFactorHorz = factor;
+  mRangeZoomFactorVert = factor;
+}
+
+/*!
+  Sets which elements are forcibly drawn antialiased as an or combination of QCP::AntialiasedElement.
+  
+  This overrides the antialiasing settings for whole element groups, normally controlled with the
+  \a setAntialiasing function on the individual elements. If an element is neither specified in
+  \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
+  each individual element instance is used.
+  
+  For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be
+  drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
+  to.
+  
+  \see setNotAntialiasedElements
+*/
+void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
+{
+  mAntialiasedElements = antialiasedElements;
+  
+  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
+  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
+    mNotAntialiasedElements |= ~mAntialiasedElements;
+}
+
+/*!
+  Sets whether the specified \a antialiasedElement is forcibly drawn antialiased.
+  
+  This overrides the antialiasing settings for whole element groups, normally controlled with the
+  \a setAntialiasing function on the individual elements. If an element is neither specified in
+  \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
+  each individual element instance is used.
+  
+  For example, if \a enabled is true and \a antialiasedElement is \ref QCP::aePlottables, all
+  plottables will be drawn antialiased, no matter what the specific
+  QCPAbstractPlottable::setAntialiased value was set to.
+  
+  \see setNotAntialiasedElement
+*/
+void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
+{
+  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
+    mAntialiasedElements &= ~antialiasedElement;
+  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
+    mAntialiasedElements |= antialiasedElement;
+  
+  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
+  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
+    mNotAntialiasedElements |= ~mAntialiasedElements;
+}
+
+/*!
+  Sets which elements are forcibly drawn not antialiased as an or combination of
+  QCP::AntialiasedElement.
+  
+  This overrides the antialiasing settings for whole element groups, normally controlled with the
+  \a setAntialiasing function on the individual elements. If an element is neither specified in
+  \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
+  each individual element instance is used.
+  
+  For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be
+  drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
+  to.
+  
+  if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is
+  removed from there.
+  
+  \see setAntialiasedElements
+*/
+void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
+{
+  mNotAntialiasedElements = notAntialiasedElements;
+  
+  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
+  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
+    mAntialiasedElements |= ~mNotAntialiasedElements;
+}
+
+/*!
+  Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased.
+  
+  This overrides the antialiasing settings for whole element groups, normally controlled with the
+  \a setAntialiasing function on the individual elements. If an element is neither specified in
+  \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
+  each individual element instance is used.
+  
+  For example, if \a enabled is true and \a notAntialiasedElement is \ref QCP::aePlottables, no
+  plottables will be drawn antialiased, no matter what the specific
+  QCPAbstractPlottable::setAntialiased value was set to.
+  
+  if \a enabled is true and \a notAntialiasedElement is already set with \ref
+  setAntialiasedElement, it is removed from there.
+  
+  \see setAntialiasedElement
+*/
+void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
+{
+  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
+    mNotAntialiasedElements &= ~notAntialiasedElement;
+  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
+    mNotAntialiasedElements |= notAntialiasedElement;
+  
+  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
+  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
+    mAntialiasedElements |= ~mNotAntialiasedElements;
+}
+
+/*!
+  If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the
+  newly created plottable to the legend.
+  
+  \see addPlottable, addGraph, QCPLegend::addItem
+*/
+void QCustomPlot::setAutoAddPlottableToLegend(bool on)
+{
+  mAutoAddPlottableToLegend = on;
+}
+
+/*!
+  Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the current
+  axis rect, before anything else (e.g. the axes themselves, grids, graphs, etc.) is drawn.
+  If the provided pixmap doesn't have the same size as the axis rect, scaling can be enabled with \ref setAxisBackgroundScaled
+  and the scaling mode (i.e. whether and how the aspect ratio is preserved) can be set with \ref setAxisBackgroundScaledMode.
+  To set all these options in one call, consider using the overloaded version of this function.
+  \see setAxisBackgroundScaled, setAxisBackgroundScaledMode
+*/
+void QCustomPlot::setAxisBackground(const QPixmap &pm)
+{
+  mAxisBackground = pm;
+  mScaledAxisBackground = QPixmap();
+}
+
+/*!
+  \overload
+  Allows setting the background pixmap, whether it shall be scaled and how it shall be scaled in one call.
+  \see setAxisBackground(const QPixmap &pm), setAxisBackgroundScaled, setAxisBackgroundScaledMode
+*/
+void QCustomPlot::setAxisBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
+{
+  mAxisBackground = pm;
+  mScaledAxisBackground = QPixmap();
+  mAxisBackgroundScaled = scaled;
+  mAxisBackgroundScaledMode = mode;
+}
+
+/*!
+  Sets whether the axis background pixmap shall be scaled to fit the current axis rect or not. If
+  \a scaled is set to true, you may control whether and how the aspect ratio of the original pixmap is
+  preserved with \ref setAxisBackgroundScaledMode.
+  
+  Note that the scaled version of the original pixmap is buffered, so there is no performance penalty
+  on replots, when enabling the scaling. (Except of course, the axis rect is continuously
+  changed, but that's not very likely.)
+  
+  \see setAxisBackground, setAxisBackgroundScaledMode
+*/
+void QCustomPlot::setAxisBackgroundScaled(bool scaled)
+{
+  mAxisBackgroundScaled = scaled;
+}
+
+/*!
+  If scaling of the axis background pixmap is enabled (\ref setAxisBackgroundScaled), use this function to
+  define whether and how the aspect ratio of the original pixmap passed to \ref setAxisBackground is preserved.
+  \see setAxisBackground, setAxisBackgroundScaled
+*/
+void QCustomPlot::setAxisBackgroundScaledMode(Qt::AspectRatioMode mode)
+{
+  mAxisBackgroundScaledMode = mode;
+}
+
+/*!
+  Sets the possible interactions of this QCustomPlot as an or-combination of \ref Interaction
+  enums. There are the following types of interactions:
+  
+  <b>Axis range manipulation</b> is controlled via \ref iRangeDrag and \ref iRangeZoom. When the
+  respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel.
+  For details how to control which axes the user may drag/zoom and in what orientations, see \ref
+  setRangeDrag, \ref setRangeZoom, \ref setRangeDragAxes, \ref setRangeZoomAxes.
+  
+  <b>Plottable selection</b> is controlled by \ref iSelectPlottables. If \ref iSelectPlottables is
+  set, the user may select plottables (e.g. graphs, curves, bars,...) by clicking on them or in
+  their vicinity, see \ref setSelectionTolerance. Whether the user can actually select a plottable
+  can further be restricted with the \ref QCPAbstractPlottable::setSelectable function on the
+  specific plottable. To find out whether a specific plottable is selected, call
+  QCPAbstractPlottable::selected(). To retrieve a list of all currently selected plottables, call
+  \ref selectedPlottables. If you're only interested in QCPGraphs, you may use the convenience
+  function \ref selectedGraphs.
+  
+  <b>Item selection</b> is controlled by \ref iSelectItems. If \ref iSelectItems is set, the user
+  may select items (e.g. QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To
+  find out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a
+  list of all currently selected items, call \ref selectedItems.
+  
+  <b>Axis selection</b> is controlled with \ref iSelectAxes. If \ref iSelectAxes is set, the user
+  may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick
+  labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectable for each
+  axis. To retrieve a list of all axes that currently contain selected parts, call \ref
+  selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selected().
+  
+  <b>Legend selection</b> is controlled with \ref iSelectLegend. If this is set, the user may
+  select the legend itself or individual items by clicking on them. What parts exactly are
+  selectable can be controlled via \ref QCPLegend::setSelectable. To find out whether the legend or
+  any child items are selected, check the value of QCPLegend::selected. To find out which child
+  items are selected, call \ref QCPLegend::selectedItems.
+  
+  <b>Plot title selection</b> is controlled with \ref iSelectTitle. If set, the user may select the
+  plot title by clicking on it. To find out whether the title is currently selected, call
+  QCustomPlot::titleSelected().
+  
+  If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is
+  emitted. Each selectable object additionally emits an individual selectionChanged signal whenever
+  their selection state has changed, i.e. not only by user interaction.
+  
+  To allow multiple objects to be selected by holding the modifier set with \ref
+  setMultiSelectModifier, set the flag \ref iMultiSelect.
+  
+  \note In addition to the selection mechanism presented here, QCustomPlot always emits
+  corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and
+  \ref plottableDoubleClick for example.
+  
+  \see setInteraction, setSelectionTolerance
+*/
+void QCustomPlot::setInteractions(const Interactions &interactions)
+{
+  mInteractions = interactions;
+}
+
+/*!
+  Sets the single \a interaction of this QCustomPlot to \a enabled.
+  
+  For details about the interaction system, see \ref setInteractions.
+  
+  \see setInteractions
+*/
+void QCustomPlot::setInteraction(const QCustomPlot::Interaction &interaction, bool enabled)
+{
+  if (!enabled && mInteractions.testFlag(interaction))
+    mInteractions &= ~interaction;
+  else if (enabled && !mInteractions.testFlag(interaction))
+    mInteractions |= interaction;
+}
+
+/*!
+  Sets the tolerance that is used when deciding whether a click on the QCustomPlot surface selects
+  an object (e.g. a plottable) or not.
+  
+  If for example the user clicks in the vicinity of the line of a QCPGraph, it's only regarded as a
+  potential selection when the minimum distance between the click position and the graph line is
+  smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks
+  directly inside the area and ignore this selection tolerance. In other words it only has meaning
+  for parts of objects that are too thin to exactly hit with a click and thus need such a
+  tolerance.
+  
+  \see setInteractions, QCPAbstractPlottable::selectTest
+*/
+void QCustomPlot::setSelectionTolerance(int pixels)
+{
+  mSelectionTolerance = pixels;
+}
+
+/*!
+  This \a font is used to draw the title, when it is selected.
+  
+  \see setTitleSelected, setTitleFont
+*/
+void QCustomPlot::setSelectedTitleFont(const QFont &font)
+{
+  mSelectedTitleFont = font;
+}
+
+/*!
+  This \a color is used to draw the title, when it is selected.
+  
+  \see setTitleSelected, setTitleColor
+*/
+void QCustomPlot::setSelectedTitleColor(const QColor &color)
+{
+  mSelectedTitleColor = color;
+}
+
+/*!
+  Sets whether the plot title is selected.
+  
+  \see setInteractions, setSelectedTitleFont, setSelectedTitleColor, setTitle
+*/
+void QCustomPlot::setTitleSelected(bool selected)
+{
+  mTitleSelected = selected;
+}
+
+/*!
+  Sets whether antialiasing is disabled for all elements while the user is dragging axes ranges. If
+  many objects, especially plottables, are normally drawn antialiased, this greatly improves
+  performance during dragging. Thus it creates a more responsive user experience. As soon as the
+  user stops dragging, the last replot is done with normal antialiasing, to restore high image
+  quality.
+  
+  \see setAntialiasedElements, setNotAntialiasedElements
+*/
+void QCustomPlot::setNoAntialiasingOnDrag(bool enabled)
+{
+  mNoAntialiasingOnDrag = enabled;
+}
+
+/*!
+  Sets the plotting hints for this QCustomPlot instance.
+  \see setPlottingHint
+*/
+void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
+{
+  mPlottingHints = hints;
+}
+
+/*!
+  Sets the specified plotting \a hint to \a enabled.
+  \see setPlottingHints
+*/
+void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)
+{
+  QCP::PlottingHints newHints = mPlottingHints;
+  if (!enabled)
+    newHints &= ~hint;
+  else
+    newHints |= hint;
+  
+  if (newHints != mPlottingHints)
+    setPlottingHints(newHints);
+}
+
+/*!
+  Sets the keyboard modifier that will be recognized as multi-select-modifier.
+  
+  If \ref iMultiSelect is specified in \ref setInteractions, the user may select multiple objects
+  by clicking on them one after the other while holding down \a modifier.
+  
+  By default the multi-select-modifier is set to Qt::ControlModifier.
+  
+  \see setInteractions
+*/
+void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
+{
+  mMultiSelectModifier = modifier;
+}
+
+/*!
+  Returns the plottable with \a index. If the index is invalid, returns 0.
+  
+  There is an overloaded version of this function with no parameter which returns the last added
+  plottable, see QCustomPlot::plottable()
+  
+  \see plottableCount, addPlottable
+*/
+QCPAbstractPlottable *QCustomPlot::plottable(int index)
+{
+  if (index >= 0 && index < mPlottables.size())
+  {
+    return mPlottables.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*! \overload
+  
+  Returns the last plottable, that was added with \ref addPlottable. If there are no plottables in the plot,
+  returns 0.
+  
+  \see plottableCount, addPlottable
+*/
+QCPAbstractPlottable *QCustomPlot::plottable()
+{
+  if (!mPlottables.isEmpty())
+  {
+    return mPlottables.last();
+  } else
+    return 0;
+}
+
+/*!
+  Adds the specified plottable to the plot and, if \ref setAutoAddPlottableToLegend is enabled, to the legend.
+  QCustomPlot takes ownership of the plottable.
+  
+  Returns true on success, i.e. when \a plottable wasn't already added to the plot and
+  the parent plot of \a plottable is this QCustomPlot (the latter is controlled by what
+  axes the plottable was passed in the constructor).
+  
+  \see plottable, plottableCount, removePlottable, clearPlottables
+*/
+bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable)
+{
+  if (mPlottables.contains(plottable))
+  {
+    qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
+    return false;
+  }
+  if (plottable->parentPlot() != this)
+  {
+    qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
+    return false;
+  }
+  
+  mPlottables.append(plottable);
+  // possibly add plottable to legend:
+  if (mAutoAddPlottableToLegend)
+    plottable->addToLegend();
+  // special handling for QCPGraphs to maintain the simple graph interface:
+  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
+    mGraphs.append(graph);
+  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
+    plottable->setLayer(currentLayer());
+  return true;
+}
+
+/*!
+  Removes the specified plottable from the plot and, if necessary, from the legend.
+  
+  Returns true on success.
+  
+  \see addPlottable, clearPlottables
+*/
+bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)
+{
+  if (!mPlottables.contains(plottable))
+  {
+    qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
+    return false;
+  }
+  
+  // remove plottable from legend:
+  plottable->removeFromLegend();
+  // special handling for QCPGraphs to maintain the simple graph interface:
+  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
+    mGraphs.removeOne(graph);
+  // remove plottable:
+  delete plottable;
+  mPlottables.removeOne(plottable);
+  return true;
+}
+
+/*! \overload
+  
+  Removes the plottable by its \a index.
+*/
+bool QCustomPlot::removePlottable(int index)
+{
+  if (index >= 0 && index < mPlottables.size())
+    return removePlottable(mPlottables[index]);
+  else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return false;
+  }
+}
+
+/*!
+  Removes all plottables from the plot (and the legend, if necessary).
+  
+  Returns the number of plottables removed.
+  
+  \see removePlottable
+*/
+int QCustomPlot::clearPlottables()
+{
+  int c = mPlottables.size();
+  for (int i=c-1; i >= 0; --i)
+    removePlottable(mPlottables[i]);
+  return c;
+}
+
+/*!
+  Returns the number of currently existing plottables in the plot
+  
+  \see plottable, addPlottable
+*/
+int QCustomPlot::plottableCount() const
+{
+  return mPlottables.size();
+}
+
+/*!
+  Returns a list of the selected plottables. If no plottables are currently selected, the list is empty.
+  
+  There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs.
+  
+  \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected, selectedGraphs
+*/
+QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
+{
+  QList<QCPAbstractPlottable*> result;
+  for (int i=0; i<mPlottables.size(); ++i)
+  {
+    if (mPlottables.at(i)->selected())
+      result.append(mPlottables.at(i));
+  }
+  return result;
+}
+
+/*!
+  Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines
+  (e.g. graphs) have a tolerance band around them, see \ref setSelectionTolerance.
+  If multiple plottables come into consideration, the one closest to \a pos is returned.
+  
+  If \a onlySelectable is true, only plottables that are selectable
+  (QCPAbstractPlottable::setSelectable) are considered.
+  
+  If there is no plottable at \a pos, the return value is 0.
+*/
+QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
+{
+  QCPAbstractPlottable *resultPlottable = 0;
+  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
+  
+  for (int i=0; i<mPlottables.size(); ++i)
+  {
+    QCPAbstractPlottable *currentPlottable = mPlottables[i];
+    if (onlySelectable && !currentPlottable->selectable())
+      continue;
+    if ((currentPlottable->keyAxis()->axisRect() | currentPlottable->valueAxis()->axisRect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
+    {
+      double currentDistance = currentPlottable->selectTest(pos);
+      if (currentDistance >= 0 && currentDistance < resultDistance)
+      {
+        resultPlottable = currentPlottable;
+        resultDistance = currentDistance;
+      }
+    }
+  }
+  
+  return resultPlottable;
+}
+
+/*!
+  Returns whether this QCustomPlot instance contains the \a plottable.
+  
+  \see addPlottable
+*/
+bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
+{
+  return mPlottables.contains(plottable);
+}
+
+/*!
+  Returns the graph with \a index. If the index is invalid, returns 0.
+  
+  There is an overloaded version of this function with no parameter which returns the last created
+  graph, see QCustomPlot::graph()
+  
+  \see graphCount, addGraph
+*/
+QCPGraph *QCustomPlot::graph(int index) const
+{
+  if (index >= 0 && index < mGraphs.size())
+  {
+    return mGraphs.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*! \overload
+  
+  Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot,
+  returns 0.
+  
+  \see graphCount, addGraph
+*/
+QCPGraph *QCustomPlot::graph() const
+{
+  if (!mGraphs.isEmpty())
+  {
+    return mGraphs.last();
+  } else
+    return 0;
+}
+
+/*!
+  Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the
+  bottom (xAxis) is used as key and the left (yAxis) is used as value. If specified, \a keyAxis and
+  \a valueAxis must reside in this QCustomPlot.
+  
+  \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically
+  "y") for the graph.
+  
+  Returns a pointer to the newly created graph.
+  
+  \see graph, graphCount, removeGraph, clearGraphs
+*/
+QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
+{
+  if (!keyAxis) keyAxis = xAxis;
+  if (!valueAxis) valueAxis = yAxis;
+  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
+  {
+    qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
+    return 0;
+  }
+  
+  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
+  if (addPlottable(newGraph))
+  {
+    newGraph->setName("Graph "+QString::number(mGraphs.size()));
+    return newGraph;
+  } else
+  {
+    delete newGraph;
+    return 0;
+  }
+}
+
+/*!
+  Removes the specified \a graph from the plot and, if necessary, from the legend. If
+  any other graphs in the plot have a channel fill set towards the removed graph, the channel fill
+  property of those graphs is reset to zero (no channel fill).
+  
+  Returns true on success.
+  
+  \see clearGraphs
+*/
+bool QCustomPlot::removeGraph(QCPGraph *graph)
+{
+  return removePlottable(graph);
+}
+
+/*! \overload
+  
+  Removes the graph by its \a index.
+*/
+bool QCustomPlot::removeGraph(int index)
+{
+  if (index >= 0 && index < mGraphs.size())
+    return removeGraph(mGraphs[index]);
+  else
+    return false;
+}
+
+/*!
+  Removes all graphs from the plot (and the legend, if necessary).
+  Returns the number of graphs removed.
+  \see removeGraph
+*/
+int QCustomPlot::clearGraphs()
+{
+  int c = mGraphs.size();
+  for (int i=c-1; i >= 0; --i)
+    removeGraph(mGraphs[i]);
+  return c;
+}
+
+/*!
+  Returns the number of currently existing graphs in the plot
+  
+  \see graph, addGraph
+*/
+int QCustomPlot::graphCount() const
+{
+  return mGraphs.size();
+}
+
+/*!
+  Returns a list of the selected graphs. If no graphs are currently selected, the list is empty.
+  
+  \note Even if the returned list is empty, it might still be, that there are selected plottables
+  in the plot that are not of type QCPGraph (e.g. QCPCurve, QCPBars, etc.), see \ref
+  selectedPlottables. Of course, this only applies, if you actually add non-QCPGraph plottables.
+  
+  \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
+*/
+QList<QCPGraph*> QCustomPlot::selectedGraphs() const
+{
+  QList<QCPGraph*> result;
+  for (int i=0; i<mGraphs.size(); ++i)
+  {
+    if (mGraphs.at(i)->selected())
+      result.append(mGraphs.at(i));
+  }
+  return result;
+}
+
+/*!
+  Returns the item with \a index. If the index is invalid, returns 0.
+  
+  There is an overloaded version of this function with no parameter which returns the last added
+  item, see QCustomPlot::item()
+  
+  \see itemCount, addItem
+*/
+QCPAbstractItem *QCustomPlot::item(int index) const
+{
+  if (index >= 0 && index < mItems.size())
+  {
+    return mItems.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*! \overload
+  
+  Returns the last item, that was added with \ref addItem. If there are no items in the plot,
+  returns 0.
+  
+  \see itemCount, addItem
+*/
+QCPAbstractItem *QCustomPlot::item() const
+{
+  if (!mItems.isEmpty())
+  {
+    return mItems.last();
+  } else
+    return 0;
+}
+
+/*!
+  Adds the specified item to the plot. QCustomPlot takes ownership of the item.
+  
+  Returns true on success, i.e. when \a item wasn't already added to the plot and the parent plot
+  of \a item is this QCustomPlot.
+  
+  \see item, itemCount, removeItem, clearItems
+*/
+bool QCustomPlot::addItem(QCPAbstractItem *item)
+{
+  if (!mItems.contains(item) && item->parentPlot() == this)
+  {
+    mItems.append(item);
+    return true;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
+    return false;
+  }
+}
+
+/*!
+  Removes the specified item from the plot.
+  
+  Returns true on success.
+  
+  \see addItem, clearItems
+*/
+bool QCustomPlot::removeItem(QCPAbstractItem *item)
+{
+  if (mItems.contains(item))
+  {
+    delete item;
+    mItems.removeOne(item);
+    return true;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
+    return false;
+  }
+}
+
+/*! \overload
+  
+  Removes the item by its \a index.
+*/
+bool QCustomPlot::removeItem(int index)
+{
+  if (index >= 0 && index < mItems.size())
+    return removeItem(mItems[index]);
+  else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return false;
+  }
+}
+
+/*!
+  Removes all items from the plot.
+  
+  Returns the number of items removed.
+  
+  \see removeItem
+*/
+int QCustomPlot::clearItems()
+{
+  int c = mItems.size();
+  for (int i=c-1; i >= 0; --i)
+    removeItem(mItems[i]);
+  return c;
+}
+
+/*!
+  Returns the number of currently existing items in the plot
+  
+  \see item, addItem
+*/
+int QCustomPlot::itemCount() const
+{
+  return mItems.size();
+}
+
+/*!
+  Returns a list of the selected items. If no items are currently selected, the list is empty.
+  
+  \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected
+*/
+QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
+{
+  QList<QCPAbstractItem*> result;
+  for (int i=0; i<mItems.size(); ++i)
+  {
+    if (mItems.at(i)->selected())
+      result.append(mItems.at(i));
+  }
+  return result;
+}
+
+/*!
+  Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref
+  QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
+  setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is
+  returned.
+  
+  If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
+  considered.
+  
+  If there is no item at \a pos, the return value is 0.
+*/
+QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
+{
+  QCPAbstractItem *resultItem = 0;
+  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
+  
+  for (int i=0; i<mItems.size(); ++i)
+  {
+    QCPAbstractItem *currentItem = mItems[i];
+    if (onlySelectable && !currentItem->selectable())
+      continue;
+    if (!currentItem->clipToAxisRect() || currentItem->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
+    {
+      double currentDistance = currentItem->selectTest(pos);
+      if (currentDistance >= 0 && currentDistance < resultDistance)
+      {
+        resultItem = currentItem;
+        resultDistance = currentDistance;
+      }
+    }
+  }
+  
+  return resultItem;
+}
+
+/*!
+  Returns the layer with the specified \a name.
+  
+  \see addLayer, moveLayer, removeLayer
+*/
+QCPLayer *QCustomPlot::layer(const QString &name) const
+{
+  for (int i=0; i<mLayers.size(); ++i)
+  {
+    if (mLayers.at(i)->name() == name)
+      return mLayers.at(i);
+  }
+  return 0;
+}
+
+/*! \overload
+  
+  Returns the layer by index.
+  
+  \see addLayer, moveLayer, removeLayer
+*/
+QCPLayer *QCustomPlot::layer(int index) const
+{
+  if (index >= 0 && index < mLayers.size())
+  {
+    return mLayers.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*!
+  Returns the layer that is set as current layer (see \ref setCurrentLayer).
+*/
+QCPLayer *QCustomPlot::currentLayer() const
+{
+  return mCurrentLayer; 
+}
+
+/*!
+  Sets the layer with the specified \a name to be the current layer. All newly created/added
+  layerables (\ref QCPLayerable), e.g. plottables and items, are initially placed on the current
+  layer.
+  
+  Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot.
+  
+  \see addLayer, moveLayer, removeLayer
+*/
+bool QCustomPlot::setCurrentLayer(const QString &name)
+{
+  if (QCPLayer *newCurrentLayer = layer(name))
+  {
+    return setCurrentLayer(newCurrentLayer);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
+    return false;
+  }
+}
+
+/*! \overload
+  
+  Sets the provided \a layer to be the current layer.
+  
+  Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot.
+  
+  \see addLayer, moveLayer, removeLayer
+*/
+bool QCustomPlot::setCurrentLayer(QCPLayer *layer)
+{
+  if (!mLayers.contains(layer))
+  {
+    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
+    return false;
+  }
+  
+  mCurrentLayer = layer;
+  return true;
+}
+
+/*!
+  Returns the number of currently existing layers in the plot
+  
+  \see layer, addLayer
+*/
+int QCustomPlot::layerCount() const
+{
+  return mLayers.size();
+}
+
+/*!
+  Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which must
+  be unique. It is positioned either below or above \a otherLayer, which can be controlled with \a insertMode.
+  
+  Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a
+  valid layer inside this QCustomPlot.
+  
+  If \a otherLayer is 0, the highest layer in the QCustomPlot will be used.
+  
+  For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer.
+  
+  \see layer, moveLayer, removeLayer
+*/
+bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
+{
+  if (!otherLayer)
+    otherLayer = mLayers.last();
+  if (!mLayers.contains(otherLayer))
+  {
+    qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
+    return false;
+  }
+  if (layer(name))
+  {
+    qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
+    return false;
+  }
+    
+  QCPLayer *newLayer = new QCPLayer(this, name);
+  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
+  return true;
+}
+
+/*!
+  Removes the specified \a layer and returns true on success.
+  
+  All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below
+  \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both
+  cases, the total rendering order of all layerables in the QCustomPlot is preserved.
+  
+  If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom
+  layer) becomes the new current layer.
+  
+  Note that it is not possible to remove the last layer.
+  
+  \see layer, addLayer, moveLayer
+*/
+bool QCustomPlot::removeLayer(QCPLayer *layer)
+{
+  if (!mLayers.contains(layer))
+  {
+    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
+    return false;
+  }
+  //if (!mLayers.size() > 1)
+  if (mLayers.size() <= 1)
+  {
+    qDebug() << Q_FUNC_INFO << "can't remove last layer";
+    return false;
+  }
+  
+  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
+  int removedIndex = layer->index();
+  bool isFirstLayer = removedIndex==0;
+  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
+  QList<QCPLayerable*> children = layer->children();
+  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
+  {
+    for (int i=children.size()-1; i>=0; --i)
+      children.at(i)->moveToLayer(targetLayer, true);
+  } else  // append normally
+  {
+    for (int i=0; i<children.size(); ++i)
+      children.at(i)->moveToLayer(targetLayer, false);
+  }
+  // if removed layer is current layer, change current layer to layer below/above:
+  if (layer == mCurrentLayer)
+    setCurrentLayer(targetLayer);
+  // remove layer:
+  delete layer;
+  mLayers.removeOne(layer);
+  return true;
+}
+
+/*!
+  Moves the specified \a layer to the position relative to \a otherLayer. Whether \a layer is
+  placed above or below \a otherLayer can be controlled with \a insertMode.
+  
+  Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the
+  QCustomPlot.
+  
+  \see layer, addLayer, moveLayer
+*/
+bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
+{
+  if (!mLayers.contains(layer))
+  {
+    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
+    return false;
+  }
+  if (!mLayers.contains(otherLayer))
+  {
+    qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
+    return false;
+  }
+  
+  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
+  return true;
+}
+
+/*!
+  Returns the axes that currently have selected parts, i.e. whose selection is not \ref QCPAxis::spNone.
+  
+  \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelected, QCPAxis::setSelectable
+*/
+QList<QCPAxis*> QCustomPlot::selectedAxes() const
+{
+  QList<QCPAxis*> result = QList<QCPAxis*>() << xAxis << yAxis << xAxis2 << yAxis2;
+  for (int i=result.size()-1; i>=0; --i)
+  {
+    if (result.at(i)->selected() == QCPAxis::spNone)
+      result.removeAt(i);
+  }
+  return result;
+}
+
+/*!
+  Returns the legends (typically one or zero) that currently have selected parts, i.e. whose
+  selection is not \ref QCPLegend::spNone.
+  
+  \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelected, QCPLegend::setSelectable, QCPLegend::selectedItems
+*/
+QList<QCPLegend*> QCustomPlot::selectedLegends() const
+{
+  /* for now, we only have the one legend. Maybe later, there will be a mechanism to have more. */
+  QList<QCPLegend*> result;
+  if (legend->selected() != QCPLegend::spNone)
+    result.append(legend);
+  return result;
+}
+
+/*!
+  Deselects everything in the QCustomPlot (plottables, items, axes, legend and title).
+  
+  Since calling this function is not a user interaction, this does not emit the \ref
+  selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the
+  objects were previously selected.
+  
+  \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends
+*/
+void QCustomPlot::deselectAll()
+{
+  // deselect plottables:
+  QList<QCPAbstractPlottable*> selPlottables = selectedPlottables();
+  for (int i=0; i<selPlottables.size(); ++i)
+    selPlottables.at(i)->setSelected(false);
+  
+  // deselect items:
+  QList<QCPAbstractItem*> selItems = selectedItems();
+  for (int i=0; i<selItems.size(); ++i)
+    selItems.at(i)->setSelected(false);
+  
+  // deselect axes:
+  QList<QCPAxis*> selAxes = selectedAxes();
+  for (int i=0; i<selAxes.size(); ++i)
+    selAxes.at(i)->setSelected(QCPAxis::spNone);
+  
+  // deselect legend (and legend items):
+  legend->setSelected(QCPLegend::spNone);
+  
+  // deselect title:
+  setTitleSelected(false);
+}
+
+/*!
+  Causes a complete replot (axes, labels, graphs, etc.) into the internal buffer. Finally, update()
+  is called, to redraw the buffer on the QCustomPlot widget surface.
+  
+  Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref afterReplot is
+  emitted. It is safe to mutually connect the replot slot with any of those two signals on two QCustomPlots
+  to make them replot synchronously (i.e. it won't cause an infinite recursion).
+*/
+void QCustomPlot::replot()
+{
+  if (mReplotting) // incase signals loop back to replot slot
+    return;
+  mReplotting = true;
+  emit beforeReplot();
+  mPaintBuffer.fill(mColor);
+  QCPPainter painter;
+  painter.begin(&mPaintBuffer);
+  if (painter.isActive()) 
+  {
+    painter.setRenderHint(QPainter::HighQualityAntialiasing);
+    draw(&painter);
+    if (mPlottingHints.testFlag(QCP::phForceRepaint))
+      repaint();
+    else
+      update();
+    painter.end();
+  } else // might happen if QCustomPlot has width or height zero
+    qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";
+  emit afterReplot();
+  mReplotting = false;
+}
+
+/*!
+  Convenience function to make the top and right axes visible and assign them the following
+  properties from their corresponding bottom/left axes:
+  
+  \li range (\ref QCPAxis::setRange)
+  \li range reversed (\ref QCPAxis::setRangeReversed)
+  \li scale type (\ref QCPAxis::setScaleType)
+  \li scale log base  (\ref QCPAxis::setScaleLogBase)
+  \li ticks (\ref QCPAxis::setTicks)
+  \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
+  \li sub tick count (\ref QCPAxis::setSubTickCount)
+  \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
+  \li tick step (\ref QCPAxis::setTickStep)
+  \li auto tick step (\ref QCPAxis::setAutoTickStep)
+  
+  Tick labels (\ref QCPAxis::setTickLabels) however, is always set to false.
+
+  This function does \a not connect the rangeChanged signals of the bottom and left axes to the \ref
+  QCPAxis::setRange slots of the top and right axes in order to synchronize the ranges permanently.
+*/
+void QCustomPlot::setupFullAxesBox()
+{
+  xAxis2->setVisible(true);
+  yAxis2->setVisible(true);
+  
+  xAxis2->setTickLabels(false);
+  yAxis2->setTickLabels(false);
+  
+  xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
+  yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
+  
+  xAxis2->setAutoTickCount(xAxis->autoTickCount());
+  yAxis2->setAutoTickCount(yAxis->autoTickCount());
+  
+  xAxis2->setAutoTickStep(xAxis->autoTickStep());
+  yAxis2->setAutoTickStep(yAxis->autoTickStep());
+  
+  xAxis2->setScaleType(xAxis->scaleType());
+  yAxis2->setScaleType(yAxis->scaleType());
+  
+  xAxis2->setScaleLogBase(xAxis->scaleLogBase());
+  yAxis2->setScaleLogBase(yAxis->scaleLogBase());
+  
+  xAxis2->setTicks(xAxis->ticks());
+  yAxis2->setTicks(yAxis->ticks());
+  
+  xAxis2->setSubTickCount(xAxis->subTickCount());
+  yAxis2->setSubTickCount(yAxis->subTickCount());
+  
+  xAxis2->setTickStep(xAxis->tickStep());
+  yAxis2->setTickStep(yAxis->tickStep());
+  
+  xAxis2->setRange(xAxis->range());
+  yAxis2->setRange(yAxis->range());
+  
+  xAxis2->setRangeReversed(xAxis->rangeReversed());
+  yAxis2->setRangeReversed(yAxis->rangeReversed());
+}
+
+/*!
+  Rescales the axes such that all plottables (e.g. graphs) in the plot are fully visible.
+  It does this by calling \ref QCPAbstractPlottable::rescaleAxes on all plottables.
+  
+  \see QCPAbstractPlottable::rescaleAxes
+*/
+void QCustomPlot::rescaleAxes()
+{
+  if (mPlottables.isEmpty()) return;
+  
+  mPlottables.at(0)->rescaleAxes(false); // onlyEnlarge disabled on first plottable
+  for (int i=1; i<mPlottables.size(); ++i)
+    mPlottables.at(i)->rescaleAxes(true);  // onlyEnlarge enabled on all other plottables
+}
+
+/*!
+  Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale
+  of texts and lines will be derived from the specified \a width and \a height. This means, the
+  output will look like the normal on-screen output of a QCustomPlot widget with the corresponding
+  pixel width and height. If either \a width or \a height is zero, the exported image will have
+  the same dimensions as the QCustomPlot widget currently has.
+
+  \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF file. Cosmetic pens
+  are pens with numerical width 0, which are always drawn as a one pixel wide line, no matter what
+  zoom factor is set in the PDF-Viewer. For more information about cosmetic pens, see QPainter and
+  QPen documentation.
+  
+  The objects of the plot will appear in the current selection state. So when you don't want e.g.
+  selected axes to be painted in their selected look, deselect everything with \ref deselectAll
+  before calling this function.
+
+  Returns true on success.
+  
+  \warning
+  \li If you plan on editing the exported PDF file with a vector graphics editor like
+  Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing those cosmetic lines
+  (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks).
+  \li If calling this function inside the constructor of the parent of the QCustomPlot widget
+  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
+  explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
+  function uses the current width and height of the QCustomPlot widget. However, in Qt, these
+  aren't defined yet inside the constructor, so you would get an image that has strange
+  widths/heights.
+  
+  \see savePng, saveBmp, saveJpg, saveRastered
+*/
+bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height)
+{
+  bool success = false;
+  int newWidth, newHeight;
+  if (width == 0 || height == 0)
+  {
+    newWidth = this->width();
+    newHeight = this->height();
+  } else
+  {
+    newWidth = width;
+    newHeight = height;
+  }
+  
+  QPrinter printer(QPrinter::ScreenResolution);
+  printer.setOutputFileName(fileName);
+  printer.setFullPage(true);
+  QRect oldViewport = mViewport;
+  mViewport = QRect(0, 0, newWidth, newHeight);
+  updateAxisRect();
+  printer.setPaperSize(mViewport.size(), QPrinter::DevicePixel);
+  QCPPainter printpainter;
+  if (printpainter.begin(&printer))
+  {
+    printpainter.setPdfExportMode(true);
+    printpainter.setWindow(mViewport);
+    printpainter.setRenderHint(QPainter::NonCosmeticDefaultPen, noCosmeticPen);
+    if (mColor != Qt::white && mColor != Qt::transparent && mColor.alpha() > 0) // draw pdf background color if not white/transparent
+      printpainter.fillRect(mViewport, mColor);
+    draw(&printpainter);
+    printpainter.end();
+    success = true;
+  }
+  mViewport = oldViewport;
+  updateAxisRect();
+  return success;
+}
+
+/*!
+  Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width
+  and \a height in pixels. If either \a width or \a height is zero, the exported image will have
+  the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
+  scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
+
+  For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
+  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
+  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
+  200*200 pixel resolution.
+
+  \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
+  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
+  explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
+  function uses the current width and height of the QCustomPlot widget. However, in Qt, these
+  aren't defined yet inside the constructor, so you would get an image that has strange
+  widths/heights.
+  
+  The objects of the plot will appear in the current selection state. If you don't want any selected
+  objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
+  this function.
+
+  If you want the plot to be painted in a PNG with transparent background, call \ref setColor with a
+  transparent color, e.g. Qt::transparent, before saving.
+
+  PNG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
+  -1 to use the default setting.
+  
+  Returns true on success. If this function fails, most likely the PNG format isn't supported by
+  the system, see Qt docs about QImageWriter::supportedImageFormats().
+
+  \see savePdf, saveBmp, saveJpg, saveRastered
+*/
+bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
+{  
+  return saveRastered(fileName, width, height, scale, "PNG", quality);
+}
+
+/*!
+  Saves a JPG image file to \a fileName on disc. The output plot will have the dimensions \a width
+  and \a height in pixels. If either \a width or \a height is zero, the exported image will have
+  the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
+  scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
+
+  For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
+  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
+  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
+  200*200 pixel resolution.
+
+  \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
+  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
+  explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
+  function uses the current width and height of the QCustomPlot widget. However, in Qt, these
+  aren't defined yet inside the constructor, so you would get an image that has strange
+  widths/heights.
+
+  The objects of the plot will appear in the current selection state. If you don't want any selected
+  objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
+  this function.
+
+  JPG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
+  -1 to use the default setting.
+  
+  Returns true on success. If this function fails, most likely the JPG format isn't supported by
+  the system, see Qt docs about QImageWriter::supportedImageFormats().
+
+  \see savePdf, savePng, saveBmp, saveRastered
+*/
+bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
+{
+  return saveRastered(fileName, width, height, scale, "JPG", quality);
+}
+
+/*!
+  Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width
+  and \a height in pixels. If either \a width or \a height is zero, the exported image will have
+  the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
+  scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
+
+  For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
+  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
+  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
+  200*200 pixel resolution.
+
+  \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
+  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
+  explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
+  function uses the current width and height of the QCustomPlot widget. However, in Qt, these
+  aren't defined yet inside the constructor, so you would get an image that has strange
+  widths/heights.
+
+  The objects of the plot will appear in the current selection state. If you don't want any selected
+  objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
+  this function.
+  
+  Returns true on success. If this function fails, most likely the BMP format isn't supported by
+  the system, see Qt docs about QImageWriter::supportedImageFormats().
+
+  \see savePdf, savePng, saveJpg, saveRastered
+*/
+bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
+{
+  return saveRastered(fileName, width, height, scale, "BMP");
+}
+
+/*! \internal
+  
+  Returns a minimum size hint of QSize(50, 50). This prevents QCustomPlot from being collapsed to
+  size/width zero when placed in a layout where other components try to take in as much space as
+  possible (e.g. QMdiArea).
+
+  (To overwrite this minimum size hint of QCustomPlot, simply call QWidget::setMinimumSize in the
+  QCustomPlot widget.)
+*/
+QSize QCustomPlot::minimumSizeHint() const
+{
+  return QSize(50, 50);
+}
+
+/*! \internal
+  
+  Event handler for when the QCustomPlot widget needs repainting. This does not cause a replot, but
+  draws the internal buffer on the widget surface.
+*/
+void QCustomPlot::paintEvent(QPaintEvent *event)
+{
+  Q_UNUSED(event);
+  QPainter painter(this);
+  painter.drawPixmap(0, 0, mPaintBuffer);
+}
+
+/*! \internal
+  
+  Event handler for a resize of the QCustomPlot widget. Causes the internal buffer to be resized to
+  the new size. The viewport and the axis rect are resized appropriately. Finally a replot is
+  performed.
+*/
+void QCustomPlot::resizeEvent(QResizeEvent *event)
+{
+  // resize and repaint the buffer:
+  mPaintBuffer = QPixmap(event->size());
+  mViewport = rect();
+  updateAxisRect();
+  replot();
+}
+
+/*! \internal
+  
+  Event handler for when a double click occurs.
+*/
+void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
+{
+  emit mouseDoubleClick(event);
+  
+  // emit specialized object double click signals:
+  bool foundHit = false;
+  // for legend:
+  if (receivers(SIGNAL(legendDoubleClick(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*))) > 0)
+  {
+    if (legend->selectTestLegend(event->pos()))
+    {
+      emit legendDoubleClick(legend, legend->selectTestItem(event->pos()), event);
+      foundHit = true;
+    }
+  }
+  // for plottables:
+  if (!foundHit && receivers(SIGNAL(plottableDoubleClick(QCPAbstractPlottable*,QMouseEvent*))) > 0)
+  {
+    if (QCPAbstractPlottable *ap = plottableAt(event->pos(), false))
+    {
+      emit plottableDoubleClick(ap, event);
+      foundHit = true;
+    }
+  }
+  // for items:
+  if (!foundHit && receivers(SIGNAL(itemDoubleClick(QCPAbstractItem*,QMouseEvent*))) > 0)
+  {
+    if (QCPAbstractItem *ai = itemAt(event->pos(), false))
+    {
+      emit itemDoubleClick(ai, event);
+      foundHit = true;
+    }
+  }
+  // for axes:
+  if (!foundHit && receivers(SIGNAL(axisDoubleClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*))) > 0)
+  {
+    QVector<QCPAxis*> axes = QVector<QCPAxis*>() << xAxis << yAxis << xAxis2 << yAxis2;
+    for (int i=0; i<axes.size(); ++i)
+    {
+      QCPAxis::SelectablePart part = axes.at(i)->selectTest(event->pos());
+      if (part != QCPAxis::spNone)
+      {
+        foundHit = true;
+        emit axisDoubleClick(axes.at(i), part, event);
+        break;
+      }
+    }
+  }
+  // for title:
+  if (!foundHit && receivers(SIGNAL(titleDoubleClick(QMouseEvent*))) > 0)
+  {
+    if (selectTestTitle(event->pos()))
+    {
+      emit titleDoubleClick(event);
+      foundHit = true;
+    }
+  }
+}
+
+/*! \internal
+  
+  Event handler for when a mouse button is pressed. If the left mouse button is pressed, the range
+  dragging interaction is initialized (the actual range manipulation happens in the \ref
+  mouseMoveEvent).
+
+  The mDragging flag is set to true and some anchor points are set that are needed to determine the
+  distance the mouse was dragged in the mouse move/release events later.
+  
+  \see mouseMoveEvent, mouseReleaseEvent
+*/
+void QCustomPlot::mousePressEvent(QMouseEvent *event)
+{
+  emit mousePress(event);
+  mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
+  if (event->buttons() & Qt::LeftButton)
+  {
+    mDragging = true;
+    // initialize antialiasing backup in case we start dragging:
+    if (mNoAntialiasingOnDrag)
+    {
+      mAADragBackup = antialiasedElements();
+      mNotAADragBackup = notAntialiasedElements();
+    }
+    // Mouse range dragging interaction:
+    if (mInteractions.testFlag(iRangeDrag))
+    {
+      mDragStartHorzRange = mRangeDragHorzAxis->range();
+      mDragStartVertRange = mRangeDragVertAxis->range();
+    }
+  }
+  
+  QWidget::mousePressEvent(event);
+}
+
+/*! \internal
+  
+  Event handler for when the cursor is moved. This is where the built-in range dragging mechanism
+  is handled.
+  
+  \see mousePressEvent, mouseReleaseEvent
+*/
+void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
+{
+  emit mouseMove(event);
+
+  // Mouse range dragging interaction:
+  if (mInteractions.testFlag(iRangeDrag))
+  {
+    if (mDragging)
+    {
+      if (mRangeDrag.testFlag(Qt::Horizontal))
+      {
+        if (mRangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
+        {
+          double diff = mRangeDragHorzAxis->pixelToCoord(mDragStart.x()) - mRangeDragHorzAxis->pixelToCoord(event->pos().x());
+          mRangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
+        } else if (mRangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
+        {
+          double diff = mRangeDragHorzAxis->pixelToCoord(mDragStart.x()) / mRangeDragHorzAxis->pixelToCoord(event->pos().x());
+          mRangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
+        }
+      }
+      if (mRangeDrag.testFlag(Qt::Vertical))
+      {
+        if (mRangeDragVertAxis->mScaleType == QCPAxis::stLinear)
+        {
+          double diff = mRangeDragVertAxis->pixelToCoord(mDragStart.y()) - mRangeDragVertAxis->pixelToCoord(event->pos().y());
+          mRangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
+        } else if (mRangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
+        {
+          double diff = mRangeDragVertAxis->pixelToCoord(mDragStart.y()) / mRangeDragVertAxis->pixelToCoord(event->pos().y());
+          mRangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
+        }
+      }
+      if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
+      {
+        if (mNoAntialiasingOnDrag)
+          setNotAntialiasedElements(QCP::aeAll);
+        replot();
+      }
+    }
+  }
+  
+  QWidget::mouseMoveEvent(event);
+}
+
+/*! \internal
+  
+  Event handler for when a mouse button is released. This is where the selection mechanism is
+  handled.
+  
+  \see mousePressEvent, mouseMoveEvent
+*/
+void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
+{
+  emit mouseRelease(event);
+  mDragging = false;
+  bool doReplot = false;
+  if (mNoAntialiasingOnDrag)
+  {
+    setAntialiasedElements(mAADragBackup);
+    setNotAntialiasedElements(mNotAADragBackup);
+    doReplot = true;
+  }
+  
+  // determine whether it was a drag or click operation:
+  if ((mDragStart-event->pos()).manhattanLength() < 5) // was a click
+  {
+    // Mouse selection interaction:
+    if ((mInteractions & (iSelectPlottables|iSelectItems|iSelectAxes|iSelectLegend|iSelectTitle)) > 0 
+        && event->button() == Qt::LeftButton)
+    {
+      bool selectionFound = false;
+      bool emitChangedSignal = false;
+      bool additiveSelection = mInteractions.testFlag(iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
+      // Mouse selection of legend:
+      if (mInteractions.testFlag(iSelectLegend))
+        selectionFound |= legend->handleLegendSelection(event, additiveSelection, emitChangedSignal);
+      // Mouse selection of plottables:
+      if (mInteractions.testFlag(iSelectPlottables))
+        selectionFound |= handlePlottableSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, emitChangedSignal);
+      // Mouse selection of items:
+      if (mInteractions.testFlag(iSelectItems))
+        selectionFound |= handleItemSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, emitChangedSignal);
+      // Mouse selection of axes:
+      if (mInteractions.testFlag(iSelectAxes))
+        selectionFound |= handleAxisSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, emitChangedSignal);
+      // Mouse selection of title:
+      if (mInteractions.testFlag(iSelectTitle))
+        selectionFound |= handleTitleSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, emitChangedSignal);
+      
+      if (emitChangedSignal)
+        emit selectionChangedByUser();
+      doReplot = true;
+    }
+    
+    // emit specialized object click signals:
+    bool foundHit = false;
+    // for legend:
+    if (receivers(SIGNAL(legendClick(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*))) > 0)
+    {
+      if (legend->selectTestLegend(event->pos()))
+      {
+        emit legendClick(legend, legend->selectTestItem(event->pos()), event);
+        foundHit = true;
+      }
+    }
+    // for plottables:
+    if (!foundHit && receivers(SIGNAL(plottableClick(QCPAbstractPlottable*,QMouseEvent*))) > 0)
+    {
+      if (QCPAbstractPlottable *ap = plottableAt(event->pos(), false))
+      {
+        emit plottableClick(ap, event);
+        foundHit = true;
+      }
+    }
+    // for items:
+    if (!foundHit && receivers(SIGNAL(itemClick(QCPAbstractItem*,QMouseEvent*))) > 0)
+    {
+      if (QCPAbstractItem *ai = itemAt(event->pos(), false))
+      {
+        emit itemClick(ai, event);
+        foundHit = true;
+      }
+    }
+    // for axes:
+    if (!foundHit && receivers(SIGNAL(axisClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*))) > 0)
+    {
+      QVector<QCPAxis*> axes = QVector<QCPAxis*>() << xAxis << yAxis << xAxis2 << yAxis2;
+      for (int i=0; i<axes.size(); ++i)
+      {
+        QCPAxis::SelectablePart part = axes.at(i)->selectTest(event->pos());
+        if (part != QCPAxis::spNone)
+        {
+          foundHit = true;
+          emit axisClick(axes.at(i), part, event);
+          break;
+        }
+      }
+    }
+    // for title:
+    if (!foundHit && receivers(SIGNAL(titleClick(QMouseEvent*))) > 0)
+    {
+      if (selectTestTitle(event->pos()))
+      {
+        emit titleClick(event);
+        foundHit = true;
+      }
+    }
+  } // was a click end
+  
+  if (doReplot)
+    replot();
+  
+  QWidget::mouseReleaseEvent(event);
+}
+
+/*! \internal
+  
+  Event handler for mouse wheel events. First, the mouseWheel signal is emitted.
+  If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the ranges of the axes defined as
+  rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of the scaling
+  operation is the current cursor position inside the plot. The scaling factor
+  is dependant on the mouse wheel delta (which direction the wheel was rotated)
+  to provide a natural zooming feel. The Strength of the zoom can be controlled via
+  \ref setRangeZoomFactor.
+  
+  Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
+  wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
+  multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
+  exponent of the range zoom factor. This takes care of the wheel direction automatically, by
+  inverting the factor, when the wheel step is negative (f^-1 = 1/f).
+*/
+void QCustomPlot::wheelEvent(QWheelEvent *event)
+{
+  emit mouseWheel(event);
+  
+  // Mouse range zooming interaction:
+  if (mInteractions.testFlag(iRangeZoom))
+  {
+    if (mRangeZoom != 0)
+    {
+      double factor;
+      double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
+      if (mRangeZoom.testFlag(Qt::Horizontal))
+      {
+        factor = pow(mRangeZoomFactorHorz, wheelSteps);
+        mRangeZoomHorzAxis->scaleRange(factor, mRangeZoomHorzAxis->pixelToCoord(event->pos().x()));
+      }
+      if (mRangeZoom.testFlag(Qt::Vertical))
+      {
+        factor = pow(mRangeZoomFactorVert, wheelSteps);
+        mRangeZoomVertAxis->scaleRange(factor, mRangeZoomVertAxis->pixelToCoord(event->pos().y()));
+      }
+      replot();
+    }
+  }
+  
+  QWidget::wheelEvent(event);
+}
+
+/*! \internal
+  
+  Handles a mouse \a event for the plottable selection interaction. Returns true, when a selectable
+  plottable was hit by the mouse event. The output variable \a modified is set to true when the
+  selection state of a plottable has changed.
+  
+  When \a additiveSelecton is true, any new selections become selected in addition to the recent
+  selections. The recent selections are not cleared. Further, clicking on one object multiple times
+  in additive selection mode, toggles the selection of that object on and off.
+  
+  To indicate that all plottables that are selectable shall be deselected, pass 0 as \a event.
+  
+  Unlike for axis and legend selection, this function can't be exported to the respective class
+  itself (i.e. QCPAbstractPlottable). The function needs to know the distance of the mouse event to
+  all plottables in the plot, in order to choose the plottable with the smallest distance. This
+  wouldn't work if it were local to a single plottable.
+*/
+bool QCustomPlot::handlePlottableSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
+{
+  // Note: This code is basically identical to handleItemSelection, only for plottables
+  
+  bool selectionFound = false;
+  if (event)
+  {
+    QCPAbstractPlottable *plottableSelection = plottableAt(event->pos(), true);
+    // handle selection of found plottable:
+    if (plottableSelection)
+    {
+      selectionFound = true;
+      if (!plottableSelection->selected() || additiveSelection)
+      {
+        plottableSelection->setSelected(!plottableSelection->selected());
+        modified = true;
+      }
+    }
+    // deselect all others (if plottableSelection is 0, all plottables are deselected):
+    if (!additiveSelection)
+    {
+      for (int i=0; i<mPlottables.size(); ++i)
+      {
+        if (mPlottables.at(i) != plottableSelection && mPlottables.at(i)->selected() && mPlottables.at(i)->selectable())
+        {
+          mPlottables.at(i)->setSelected(false);
+          modified = true;
+        }
+      }
+    }
+  } else // event == 0, so deselect selectable plottables
+  {
+    for (int i=0; i<mPlottables.size(); ++i)
+    {
+      if (mPlottables.at(i)->selected() && mPlottables.at(i)->selectable())
+      {
+        mPlottables.at(i)->setSelected(false);
+        modified = true;
+      }
+    }
+  }
+  return selectionFound;
+}
+
+/*! \internal
+  
+  Handles a mouse \a event for the item selection interaction. Returns true, when a selectable
+  item was hit by the mouse event. The output variable \a modified is set to true when the
+  selection state of an item has changed.
+  
+  When \a additiveSelecton is true, any new selections become selected in addition to the recent
+  selections. The recent selections are not cleared. Further, clicking on one object multiple times
+  in additive selection mode, toggles the selection of that object on and off.
+  
+  To indicate that all items that are selectable shall be deselected, pass 0 as \a event.
+  
+  Unlike for axis and legend selection, this function can't be exported to the respective class
+  itself (i.e. QCPAbstractItem). The function needs to know the distance of the mouse event to
+  all items in the plot, in order to choose the item with the smallest distance. This
+  wouldn't work if it were local to a single item.
+*/
+bool QCustomPlot::handleItemSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
+{
+  // Note: This code is basically identical to handlePlottableSelection, only for items
+  
+  bool selectionFound = false;
+  if (event)
+  {
+    QCPAbstractItem *itemSelection = itemAt(event->pos(), true);
+    // handle selection of found plottable:
+    if (itemSelection)
+    {
+      selectionFound = true;
+      if (!itemSelection->selected() || additiveSelection)
+      {
+        itemSelection->setSelected(!itemSelection->selected());
+        modified = true;
+      }
+    }
+    // deselect all others (if itemSelection is 0, all items are deselected):
+    if (!additiveSelection)
+    {
+      for (int i=0; i<mItems.size(); ++i)
+      {
+        if (mItems.at(i) != itemSelection && mItems.at(i)->selected() && mItems.at(i)->selectable())
+        {
+          mItems.at(i)->setSelected(false);
+          modified = true;
+        }
+      }
+    }
+  } else // event == 0, so deselect selectable items
+  {
+    for (int i=0; i<mItems.size(); ++i)
+    {
+      if (mItems.at(i)->selected() && mItems.at(i)->selectable())
+      {
+        mItems.at(i)->setSelected(false);
+        modified = true;
+      }
+    }
+  }
+  return selectionFound;
+}
+
+/*! \internal
+  
+  Handles a mouse \a event for the axis selection interaction. Returns true, when a selectable axis
+  part was hit by the mouse event. The output variable \a modified is set to true when the
+  selection state of an axis has changed.
+  
+  When \a additiveSelecton is true, any new selections become selected in addition to the recent
+  selections. The recent selections are not cleared. Further, clicking on one object multiple times
+  in additive selection mode, toggles the selection of that object on and off.
+  
+  To indicate that all axes shall be deselected, pass 0 as \a event.
+*/
+bool QCustomPlot::handleAxisSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
+{
+  bool selectionFound = false;
+  QVector<QCPAxis*> axes = QVector<QCPAxis*>() << xAxis << yAxis << xAxis2 << yAxis2;
+  for (int i=0; i<axes.size(); ++i)
+    selectionFound |= axes.at(i)->handleAxisSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, modified);
+  return selectionFound;
+}
+
+/*! \internal
+  
+  Handles a mouse \a event for the title selection interaction. Returns true, when the title was
+  hit by the mouse event. The output variable \a modified is set to true when the selection state
+  of the title has changed.
+  
+  When \a additiveSelecton is true, any new selections become selected in addition to the recent
+  selections. The recent selections are not cleared. Further, clicking on one object multiple times
+  in additive selection mode, toggles the selection of that object on and off.
+  
+  To indicate that the title shall be deselected, pass 0 as \a event.
+*/
+bool QCustomPlot::handleTitleSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
+{
+  bool selectionFound = false;
+  if (event && selectTestTitle(event->pos())) // hit, select title
+  {
+    selectionFound = true;
+    if (!titleSelected() || additiveSelection)
+    {
+      setTitleSelected(!titleSelected());
+      modified = true;
+    }
+  } else // no hit or event == 0, deselect title
+  {
+    if (titleSelected() && !additiveSelection)
+    {
+      setTitleSelected(false);
+      modified = true;
+    }
+  }
+  return selectionFound;
+}
+
+/*! \internal
+  
+  This is the main draw function which first generates the tick vectors of all axes,
+  calculates and applies appropriate margins if autoMargin is true and finally draws
+  all elements with the passed \a painter. (axis background, title, subgrid, grid, axes, plottables)
+*/
+void QCustomPlot::draw(QCPPainter *painter)
+{
+  // calculate title bounding box:
+  if (!mTitle.isEmpty())
+  {
+    painter->setFont(titleSelected() ? mSelectedTitleFont : mTitleFont);
+    mTitleBoundingBox = painter->fontMetrics().boundingRect(mViewport, Qt::TextDontClip | Qt::AlignHCenter, mTitle);
+  } else
+    mTitleBoundingBox = QRect();
+  
+  // prepare values of ticks and tick strings:
+  xAxis->setupTickVectors();
+  yAxis->setupTickVectors();
+  xAxis2->setupTickVectors();
+  yAxis2->setupTickVectors();
+  // set auto margin such that tick/axis labels etc. are not clipped:
+  if (mAutoMargin)
+  {
+    setMargin(yAxis->calculateMargin(),
+              yAxis2->calculateMargin(),
+              xAxis2->calculateMargin()+mTitleBoundingBox.height(),
+              xAxis->calculateMargin());
+  }
+  // position legend:
+  legend->reArrange();
+  
+  // draw axis background:
+  drawAxisBackground(painter);
+  
+  // draw all layered objects (grid, axes, plottables, items, legend,...):
+  for (int layerIndex=0; layerIndex < mLayers.size(); ++layerIndex)
+  {
+    QList<QCPLayerable*> layerChildren = mLayers.at(layerIndex)->children();
+    for (int k=0; k < layerChildren.size(); ++k)
+    {
+      QCPLayerable *child = layerChildren.at(k);
+      if (child->visible())
+      {
+        painter->save();
+        painter->setClipRect(child->clipRect().translated(0, -1));
+        child->applyDefaultAntialiasingHint(painter);
+        child->draw(painter);
+        painter->restore();
+      }
+    }
+  }
+  
+  // draw title:
+  if (!mTitle.isEmpty())
+  {
+    painter->setFont(titleSelected() ? mSelectedTitleFont : mTitleFont);
+    painter->setPen(QPen(titleSelected() ? mSelectedTitleColor : mTitleColor));
+    painter->drawText(mTitleBoundingBox, Qt::TextDontClip | Qt::AlignHCenter, mTitle);
+  }
+}
+
+/*! \internal
+
+  If an axis background is provided via \ref setAxisBackground, this function first buffers the
+  scaled version depending on \ref setAxisBackgroundScaled and \ref setAxisBackgroundScaledMode and
+  then draws it inside the current axisRect with the provided \a painter. The scaled version is
+  buffered in mScaledAxisBackground to prevent the need for rescaling at every redraw. It is only
+  updated, when the axisRect has changed in a way that requires a rescale of the background pixmap
+  (this is dependant on the \ref setAxisBackgroundScaledMode), or when a differend axis backgroud
+  was set.
+  
+  \see draw, setAxisBackground, setAxisBackgroundScaled, setAxisBackgroundScaledMode
+*/
+void QCustomPlot::drawAxisBackground(QCPPainter *painter)
+{
+  if (!mAxisBackground.isNull())
+  {
+    if (mAxisBackgroundScaled)
+    {
+      // check whether mScaledAxisBackground needs to be updated:
+      QSize scaledSize(mAxisBackground.size());
+      scaledSize.scale(mAxisRect.size(), mAxisBackgroundScaledMode);
+      if (mScaledAxisBackground.size() != scaledSize)
+        mScaledAxisBackground = mAxisBackground.scaled(mAxisRect.size(), mAxisBackgroundScaledMode, Qt::SmoothTransformation);
+      painter->drawPixmap(mAxisRect.topLeft(), mScaledAxisBackground, QRect(0, 0, mAxisRect.width(), mAxisRect.height()) & mScaledAxisBackground.rect());
+    } else
+    {
+      painter->drawPixmap(mAxisRect.topLeft(), mAxisBackground, QRect(0, 0, mAxisRect.width(), mAxisRect.height()));
+    }
+  }
+}
+
+/*! \internal
+  
+  calculates mAxisRect by applying the margins inward to mViewport. The axisRect is then passed on
+  to all axes via QCPAxis::setAxisRect
+  
+  \see setMargin, setAxisRect
+*/
+void QCustomPlot::updateAxisRect()
+{
+  mAxisRect = mViewport.adjusted(mMarginLeft, mMarginTop, -mMarginRight, -mMarginBottom);
+  xAxis->setAxisRect(mAxisRect);
+  yAxis->setAxisRect(mAxisRect);
+  xAxis2->setAxisRect(mAxisRect);
+  yAxis2->setAxisRect(mAxisRect);
+}
+
+/*! \internal
+  
+  Returns whether the point \a pos in pixels hits the plot title.
+*/
+bool QCustomPlot::selectTestTitle(const QPointF &pos) const
+{
+  return mTitleBoundingBox.contains(pos.toPoint());
+}
+
+/*!
+  Saves the plot to a rastered image file \a fileName in the image format \a
+  format. The plot is sized to \a width and \a height in pixels and scaled with
+  \a scale. (width 100 and scale 2.0 lead to a full resolution file with width
+  200) If the \a format supports compression, \a quality may be between 0 and
+  100 to control it.
+  
+  Returns true on success. If this function fails, most likely the given \a format isn't supported
+  by the system, see Qt docs about QImageWriter::supportedImageFormats().
+  
+  \see saveBmp, saveJpg, savePng
+*/
+bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
+{
+  int newWidth, newHeight;
+  if (width == 0 || height == 0)
+  {
+    newWidth = this->width();
+    newHeight = this->height();
+  } else
+  {
+    newWidth = width;
+    newHeight = height;
+  }
+  int scaledWidth = qRound(scale*newWidth);
+  int scaledHeight = qRound(scale*newHeight);
+
+  QPixmap pngBuffer(scaledWidth, scaledHeight); // use QPixmap instead of QImage (like live painting buffer), because it supports background transparency (of mColor).
+  pngBuffer.fill(mColor);
+  QCPPainter painter(&pngBuffer);
+  QRect oldViewport = mViewport;
+  mViewport = QRect(0, 0, newWidth, newHeight);
+  updateAxisRect();
+  if (!qFuzzyCompare(scale, 1.0))
+  {
+    if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines would disappear
+    {
+      painter.setScaledExportMode(true);
+      painter.setRenderHint(QPainter::NonCosmeticDefaultPen);
+    }
+    painter.scale(scale, scale);
+  }
+  draw(&painter);
+  mViewport = oldViewport;
+  updateAxisRect();
+  return pngBuffer.save(fileName, format, quality);
+}
+
+
+// ================================================================================
+// =================== QCPAbstractPlottable
+// ================================================================================
+
+/*! \class QCPAbstractPlottable
+  \brief The abstract base class for all data representing objects in a plot.
+
+  It defines a very basic interface like name, pen, brush, visibility etc. Since this class is
+  abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself (see
+  below), to create new ways of displaying data.
+  
+  All further specifics are in the subclasses, for example:
+  \li A normal graph with possibly a line, scatter points and error bars is displayed by \ref QCPGraph
+  (typically created with \ref QCustomPlot::addGraph).
+  \li A parametric curve can be displayed with \ref QCPCurve.
+  \li A stackable bar chart can be achieved with \ref QCPBars.
+  \li A box of a statistical box plot is created with \ref QCPStatisticalBox.
+  
+  \section plottables-subclassing Creating own plottables
+  
+  To create an own plottable, you implement a subclass of QCPAbstractPlottable. These are the pure
+  virtual functions, you must implement:
+  \li \ref clearData
+  \li \ref selectTest
+  \li \ref draw
+  \li \ref drawLegendIcon
+  \li \ref getKeyRange
+  \li \ref getValueRange
+  
+  See the documentation of those functions for what they need to do.
+  
+  For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot
+  coordinates to pixel coordinates. This function is quite convenient, because it takes the
+  orientation of the key and value axes into account for you (x and y are swapped when the key axis
+  is vertical and the value axis horizontal). If you are worried about performance (i.e. you need
+  to translate many points in a loop like QCPGraph), you can directly use \ref
+  QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis
+  yourself.
+  
+  From QCPAbstractPlottable you inherit the following members you may use:
+  <table>
+  <tr>
+    <td>QCustomPlot *\b mParentPlot</td>
+    <td>A pointer to the parent QCustomPlot instance. This is adopted from the axes that are passed in the constructor.</td>
+  </tr><tr>
+    <td>QString \b mName</td>
+    <td>The name of the plottable.</td>
+  </tr><tr>
+    <td>bool \b mVisible</td>
+    <td>Whether the plot is visible or not. When this is false, you shouldn't draw the data in the \ref draw function (\ref draw is always called, no matter what mVisible is).</td>
+  </tr><tr>
+    <td>QPen \b mPen</td>
+    <td>The generic pen of the plottable. You should use this pen for the most prominent data representing lines in the plottable (e.g QCPGraph uses this pen for its graph lines and scatters)</td>
+  </tr><tr>
+    <td>QPen \b mSelectedPen</td>
+    <td>The generic pen that should be used when the plottable is selected (hint: \ref mainPen gives you the right pen, depending on selection state).</td>
+  </tr><tr>
+    <td>QBrush \b mBrush</td>
+    <td>The generic brush of the plottable. You should use this brush for the most prominent fillable structures in the plottable (e.g. QCPGraph uses this brush to control filling under the graph)</td>
+  </tr><tr>
+    <td>QBrush \b mSelectedBrush</td>
+    <td>The generic brush that should be used when the plottable is selected (hint: \ref mainBrush gives you the right brush, depending on selection state).</td>
+  </tr><tr>
+    <td>QCPAxis *\b mKeyAxis, *\b mValueAxis</td>
+    <td>The key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates to pixels in either the key or value dimension.</td>
+  </tr><tr>
+    <td>bool \b mSelected</td>
+    <td>indicates whether the plottable is selected or not.</td>
+  </tr>
+  </table>
+*/
+
+/* start of documentation of pure virtual functions */
+
+/*! \fn void QCPAbstractPlottable::clearData() = 0
+  Clears all data in the plottable.
+*/
+
+/*! \fn double QCPAbstractPlottable::selectTest(const QPointF &pos) const = 0
+  
+  This function is used to decide whether a click hits a plottable or not.
+
+  \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
+  shortest pixel distance of this point to the plottable (e.g. to the scatters/lines of a graph).
+  If the plottable is either invisible, contains no data or the distance couldn't be determined,
+  -1.0 is returned. \ref setSelectable has no influence on the return value of this function.
+
+  If the plottable is represented not by single lines but by an area like QCPBars or
+  QCPStatisticalBox, a click inside the area returns a constant value greater zero (typically 99%
+  of the selectionTolerance of the parent QCustomPlot). If the click lies outside the area, this
+  function returns -1.0.
+  
+  Providing a constant value for area objects allows selecting line objects even when they are
+  obscured by such area objects, by clicking close to the lines (i.e. closer than
+  0.99*selectionTolerance).
+  
+  The actual setting of the selection state is not done by this function. This is handled by the
+  parent QCustomPlot when the mouseReleaseEvent occurs.
+  
+  \see setSelected, QCustomPlot::setInteractions
+*/
+
+/*! \fn void QCPAbstractPlottable::draw(QCPPainter *painter) = 0
+  \internal
+  
+  Draws this plottable with the provided \a painter. Called by \ref QCustomPlot::draw on all its
+  visible plottables.
+  
+  The cliprect of the provided painter is set to the axis rect of the key/value axis of this
+  plottable (what \ref clipRect returns), before this function is called.
+*/
+
+/*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0
+  \internal
+  
+  called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation
+  of this plottable inside \a rect, next to the plottable name.
+*/
+
+/*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &validRange, SignDomain inSignDomain) const = 0
+  \internal
+  
+  called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
+  set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
+  returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
+  to \ref sdNegative and all positive points will be ignored for range calculation. For no
+  restriction, just set \a inSignDomain to \ref sdBoth (default). \a validRange is an output
+  parameter that indicates whether a proper range could be found or not. If this is false, you
+  shouldn't use the returned range (e.g. no points in data).
+  
+  \see rescaleAxes, getValueRange
+*/
+
+/*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &validRange, SignDomain inSignDomain) const = 0
+  \internal
+  
+  called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
+  set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
+  returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
+  to \ref sdNegative and all positive points will be ignored for range calculation. For no
+  restriction, just set \a inSignDomain to \ref sdBoth (default). \a validRange is an output
+  parameter that indicates whether a proper range could be found or not. If this is false, you
+  shouldn't use the returned range (e.g. no points in data).
+  
+  \see rescaleAxes, getKeyRange
+*/
+
+/* end of documentation of pure virtual functions */
+/* start of documentation of signals */
+
+/*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
+  This signal is emitted when the selection state of this plottable has changed, either by user interaction
+  or by a direct call to \ref setSelected.
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as
+  its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance
+  and not have the same orientation. If either of these restrictions is violated, a corresponding
+  message is printed to the debug output (qDebug), the construction is not aborted, though.
+  
+  Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables
+  (i.e. any form of data representation inside a plot, like graphs, curves etc.), it can't be
+  directly instantiated.
+  
+  You probably want one of the subclasses like \ref QCPGraph and \ref QCPCurve instead.
+  \see setKeyAxis, setValueAxis
+*/
+QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPLayerable(keyAxis->parentPlot()),
+  mName(""),
+  mAntialiasedFill(true),
+  mAntialiasedScatters(true),
+  mAntialiasedErrorBars(false),
+  mPen(Qt::black),
+  mSelectedPen(Qt::black),
+  mBrush(Qt::NoBrush),
+  mSelectedBrush(Qt::NoBrush),
+  mKeyAxis(keyAxis),
+  mValueAxis(valueAxis),
+  mSelected(false),
+  mSelectable(true)
+{
+  if (keyAxis->parentPlot() != valueAxis->parentPlot())
+    qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
+  if (keyAxis->orientation() == valueAxis->orientation())
+    qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
+}
+
+/*!
+   The name is the textual representation of this plottable as it is displayed in the QCPLegend of
+   the parent QCustomPlot. It may contain any utf-8 characters, including newlines.
+*/
+void QCPAbstractPlottable::setName(const QString &name)
+{
+  mName = name;
+}
+
+/*!
+  Sets whether fills of this plottable is drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAbstractPlottable::setAntialiasedFill(bool enabled)
+{
+  mAntialiasedFill = enabled;
+}
+
+/*!
+  Sets whether the scatter symbols of this plottable are drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAbstractPlottable::setAntialiasedScatters(bool enabled)
+{
+  mAntialiasedScatters = enabled;
+}
+
+/*!
+  Sets whether the error bars of this plottable are drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled)
+{
+  mAntialiasedErrorBars = enabled;
+}
+
+
+/*!
+  The pen is used to draw basic lines that make up the plottable representation in the
+  plot.
+  
+  For example, the \ref QCPGraph subclass draws its graph lines and scatter points
+  with this pen.
+
+  \see setBrush
+*/
+void QCPAbstractPlottable::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  When the plottable is selected, this pen is used to draw basic lines instead of the normal
+  pen set via \ref setPen.
+
+  \see setSelected, setSelectable, setSelectedBrush, selectTest
+*/
+void QCPAbstractPlottable::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  The brush is used to draw basic fills of the plottable representation in the
+  plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
+  
+  For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when
+  it's not set to Qt::NoBrush.
+
+  \see setPen
+*/
+void QCPAbstractPlottable::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  When the plottable is selected, this brush is used to draw fills instead of the normal
+  brush set via \ref setBrush.
+
+  \see setSelected, setSelectable, setSelectedPen, selectTest
+*/
+void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/*!
+  The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal
+  to the plottable's value axis. This function performs no checks to make sure this is the case.
+  The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the
+  y-axis (QCustomPlot::yAxis) as value axis.
+  
+  Normally, the key and value axes are set in the constructor of the plottable (or \ref
+  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
+
+  \see setValueAxis
+*/
+void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)
+{
+  mKeyAxis = axis;
+}
+
+/*!
+  The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is
+  orthogonal to the plottable's key axis. This function performs no checks to make sure this is the
+  case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and
+  the y-axis (QCustomPlot::yAxis) as value axis.
+
+  Normally, the key and value axes are set in the constructor of the plottable (or \ref
+  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
+  
+  \see setKeyAxis
+*/
+void QCPAbstractPlottable::setValueAxis(QCPAxis *axis)
+{
+  mValueAxis = axis;
+}
+
+/*!
+  Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains iSelectPlottables.)
+  
+  However, even when \a selectable was set to false, it is possible to set the selection manually,
+  by calling \ref setSelected directly.
+  
+  \see setSelected
+*/
+void QCPAbstractPlottable::setSelectable(bool selectable)
+{
+  mSelectable = selectable;
+}
+
+/*!
+  Sets whether this plottable is selected or not. When selected, it uses a different pen and brush
+  to draw its lines and fills, see \ref setSelectedPen and \ref setSelectedBrush.
+
+  The entire selection mechanism for plottables is handled automatically when \ref
+  QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when
+  you wish to change the selection state manually.
+  
+  This function can change the selection state even when \ref setSelectable was set to false.
+  
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+  
+  \see selectTest
+*/
+void QCPAbstractPlottable::setSelected(bool selected)
+{
+  if (mSelected != selected)
+  {
+    mSelected = selected;
+    emit selectionChanged(mSelected);
+  }
+}
+
+/*!
+  Rescales the key and value axes associated with this plottable to contain all displayed data, so
+  the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make
+  sure not to rescale to an illegal range i.e. a range containing different signs and/or zero.
+  Instead it will stay in the current sign domain and ignore all parts of the plottable that lie
+  outside of that domain.
+  
+  \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show
+  multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has
+  \a onlyEnlarge set to false (the default), and all subsequent set to true.
+*/
+void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
+{
+  rescaleKeyAxis(onlyEnlarge);
+  rescaleValueAxis(onlyEnlarge);
+}
+
+/*!
+  Rescales the key axis of the plottable so the whole plottable is visible.
+  
+  See \ref rescaleAxes for detailed behaviour.
+*/
+void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
+{
+  SignDomain signDomain = sdBoth;
+  if (mKeyAxis->scaleType() == QCPAxis::stLogarithmic)
+    signDomain = (mKeyAxis->range().upper < 0 ? sdNegative : sdPositive);
+  
+  bool validRange;
+  QCPRange newRange = getKeyRange(validRange, signDomain);
+  if (validRange)
+  {
+    if (onlyEnlarge)
+    {
+      if (mKeyAxis->range().lower < newRange.lower)
+        newRange.lower = mKeyAxis->range().lower;
+      if (mKeyAxis->range().upper > newRange.upper)
+        newRange.upper = mKeyAxis->range().upper;
+    }
+    mKeyAxis->setRange(newRange);
+  }
+}
+
+/*!
+  Rescales the value axis of the plottable so the whole plottable is visible.
+  
+  See \ref rescaleAxes for detailed behaviour.
+*/
+void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
+{
+  SignDomain signDomain = sdBoth;
+  if (mValueAxis->scaleType() == QCPAxis::stLogarithmic)
+    signDomain = (mValueAxis->range().upper < 0 ? sdNegative : sdPositive);
+  
+  bool validRange;
+  QCPRange newRange = getValueRange(validRange, signDomain);
+  
+  if (validRange)
+  {
+    if (onlyEnlarge)
+    {
+      if (mValueAxis->range().lower < newRange.lower)
+        newRange.lower = mValueAxis->range().lower;
+      if (mValueAxis->range().upper > newRange.upper)
+        newRange.upper = mValueAxis->range().upper;
+    }
+    mValueAxis->setRange(newRange);
+  }
+}
+
+/*!
+  Adds this plottable to the legend of the parent QCustomPlot.
+    
+  Normally, a QCPPlottableLegendItem is created and inserted into the legend. If the plottable
+  needs a more specialized representation in the plot, this function will take this into account
+  and instead create the specialized subclass of QCPAbstractLegendItem.
+    
+  Returns true on success, i.e. when a legend item associated with this plottable isn't already in
+  the legend.
+    
+  \see removeFromLegend, QCPLegend::addItem
+*/
+bool QCPAbstractPlottable::addToLegend()
+{
+  if (!mParentPlot->legend->hasItemWithPlottable(this))
+  {
+    mParentPlot->legend->addItem(new QCPPlottableLegendItem(mParentPlot->legend, this));
+    return true;
+  } else
+    return false;
+}
+
+/*! 
+  Removes the plottable from the legend of the parent QCustomPlot. This means the
+  QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable
+  is removed.
+    
+  Returns true on success, i.e. if a legend item associated with this plottable was found and
+  removed from the legend.
+    
+  \see addToLegend, QCPLegend::removeItem
+*/
+bool QCPAbstractPlottable::removeFromLegend() const
+{
+  if (QCPPlottableLegendItem *lip = mParentPlot->legend->itemWithPlottable(this))
+    return mParentPlot->legend->removeItem(lip);
+  else
+    return false;
+}
+
+/* inherits documentation from base class */
+QRect QCPAbstractPlottable::clipRect() const
+{
+  return mKeyAxis->axisRect() | mValueAxis->axisRect();
+}
+
+/*! \internal
+  
+  Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface,
+  taking the orientations of the axes associated with this plottable into account (e.g. whether key
+  represents x or y).
+  
+  \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y.
+    
+  \see pixelsToCoords, QCPAxis::coordToPixel
+*/
+void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
+{
+  if (mKeyAxis->orientation() == Qt::Horizontal)
+  {
+    x = mKeyAxis->coordToPixel(key);
+    y = mValueAxis->coordToPixel(value);
+  } else
+  {
+    y = mKeyAxis->coordToPixel(key);
+    x = mValueAxis->coordToPixel(value);
+  }
+}
+
+/*! \internal 
+  \overload
+  
+  Returns the input as pixel coordinates in a QPointF.
+*/
+const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
+{
+  if (mKeyAxis->orientation() == Qt::Horizontal)
+    return QPointF(mKeyAxis->coordToPixel(key), mValueAxis->coordToPixel(value));
+  else
+    return QPointF(mValueAxis->coordToPixel(value), mKeyAxis->coordToPixel(key));
+}
+
+/*! \internal
+  
+  Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates,
+  taking the orientations of the axes associated with this plottable into account (e.g. whether key
+  represents x or y).
+  
+  \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value.
+    
+  \see coordsToPixels, QCPAxis::coordToPixel
+*/
+void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
+{
+  if (mKeyAxis->orientation() == Qt::Horizontal)
+  {
+    key = mKeyAxis->pixelToCoord(x);
+    value = mValueAxis->pixelToCoord(y);
+  } else
+  {
+    key = mKeyAxis->pixelToCoord(y);
+    value = mValueAxis->pixelToCoord(x);
+  }
+}
+
+/*! \internal
+  \overload
+
+  Returns the pixel input \a pixelPos as plot coordinates \a key and \a value.
+*/
+void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
+{
+  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines of the plottable. Returns mPen when the
+  graph is not selected and mSelectedPen when it is.
+*/
+QPen QCPAbstractPlottable::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the plottable. Returns mBrush when the
+  graph is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPAbstractPlottable::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing plottable lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+  
+  This function takes into account the local setting of the antialiasing flag as well as
+  the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
+*/
+void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing plottable fills.
+  
+  This function takes into account the local setting of the fill antialiasing flag as well as
+  the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
+*/
+void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing plottable scatter points.
+  
+  This function takes into account the local setting of the scatters antialiasing flag as well as
+  the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint, applyErrorBarsAntialiasingHint
+*/
+void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing plottable error bars.
+  
+  This function takes into account the local setting of the error bars antialiasing flag as well as
+  the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyDefaultAntialiasingHint
+*/
+void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars);
+}
+
+/*! \internal
+
+  Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
+  end.
+  
+  This function may be used to help with the implementation of the \ref selectTest function for
+  specific plottables.
+  
+  \note This function is identical to QCPAbstractItem::distSqrToLine
+*/
+double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
+{
+  QVector2D a(start);
+  QVector2D b(end);
+  QVector2D p(point);
+  QVector2D v(b-a);
+  
+  double vLengthSqr = v.lengthSquared();
+  if (!qFuzzyIsNull(vLengthSqr))
+  {
+    double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
+    if (mu < 0)
+      return (a-p).lengthSquared();
+    else if (mu > 1)
+      return (b-p).lengthSquared();
+    else
+      return ((a + mu*v)-p).lengthSquared();
+  } else
+    return (a-p).lengthSquared();
+}
+
+
+// ================================================================================
+// =================== QCPAbstractLegendItem
+// ================================================================================
+
+/*! \class QCPAbstractLegendItem
+  \brief The abstract base class for all items in a QCPLegend.
+  
+  It defines a very basic interface to items in a QCPLegend. For representing plottables in the
+  legend, the subclass QCPPlottableLegendItem is more suitable.
+  
+  Only derive directly from this class when you need absolute freedom (i.e. a legend item that's
+  not associated with a plottable).
+
+  You must implement the following pure virtual functions:
+  \li \ref draw
+  \li \ref size
+  
+  You inherit the following members you may use:
+  <table>
+    <tr>
+      <td>QCPLegend *\b mParentLegend</td>
+      <td>A pointer to the parent QCPLegend.</td>
+    </tr><tr>
+      <td>QFont \b mFont</td>
+      <td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>
+    </tr>
+  </table>
+*/
+
+/* start documentation of pure virtual functions */
+
+/*! \fn void QCPAbstractLegendItem::draw(QCPPainter *painter, const QRect &rect) const = 0;
+  
+  Draws this legend item with \a painter inside the specified \a rect. The \a rect typically has
+  the size which was returned from a preceding \ref size call.
+*/
+
+/*! \fn QSize QCPAbstractLegendItem::size(const QSize &targetSize) const = 0;
+
+  Returns the size this item occupies in the legend. The legend will adapt its layout with the help
+  of this function. If this legend item can have a variable width (e.g. auto-wrapping text), this
+  function tries to find a size with a width close to the width of \a targetSize. The height of \a
+  targetSize only may have meaning in specific sublasses. Typically, it's ignored.
+*/
+
+/* end documentation of pure virtual functions */
+/* start of documentation of signals */
+
+/*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
+  
+  This signal is emitted when the selection state of this legend item has changed, either by user interaction
+  or by a direct call to \ref setSelected.
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
+  cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
+*/
+QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) : 
+  QObject(parent),
+  mParentLegend(parent),
+  mAntialiased(false),
+  mFont(parent->font()),
+  mTextColor(parent->textColor()),
+  mSelectedFont(parent->selectedFont()),
+  mSelectedTextColor(parent->selectedTextColor()),
+  mSelectable(true),
+  mSelected(false)
+{
+}
+
+/*!
+  Sets whether this legend item is drawn antialiased or not.
+  
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAbstractLegendItem::setAntialiased(bool enabled)
+{
+  mAntialiased = enabled;
+}
+
+/*!
+  Sets the default font of this specific legend item to \a font.
+  
+  \see setTextColor, QCPLegend::setFont
+*/
+void QCPAbstractLegendItem::setFont(const QFont &font)
+{
+  mFont = font;
+}
+
+/*!
+  Sets the default text color of this specific legend item to \a color.
+  
+  \see setFont, QCPLegend::setTextColor
+*/
+void QCPAbstractLegendItem::setTextColor(const QColor &color)
+{
+  mTextColor = color;
+}
+
+/*!
+  When this legend item is selected, \a font is used to draw generic text, instead of the normal
+  font set with \ref setFont.
+  
+  \see setFont, QCPLegend::setSelectedFont
+*/
+void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
+{
+  mSelectedFont = font;
+}
+
+/*!
+  When this legend item is selected, \a color is used to draw generic text, instead of the normal
+  color set with \ref setTextColor.
+  
+  \see setTextColor, QCPLegend::setSelectedTextColor
+*/
+void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
+{
+  mSelectedTextColor = color;
+}
+
+/*!
+  Sets whether this specific legend item is selectable.
+  
+  \see setSelected, QCustomPlot::setInteractions
+*/
+void QCPAbstractLegendItem::setSelectable(bool selectable)
+{
+  mSelectable = selectable;
+}
+
+/*!
+  Sets whether this specific legend item is selected. The selection state of the parent QCPLegend
+  is updated correspondingly.
+  
+  It is possible to set the selection state of this item by calling this function directly, even if
+  setSelectable is set to false.
+  
+  \see setSelectable, QCustomPlot::setInteractions
+*/
+void QCPAbstractLegendItem::setSelected(bool selected)
+{
+  if (mSelected != selected)
+  {
+    mSelected = selected;
+    emit selectionChanged(mSelected);
+    mParentLegend->updateSelectionState();
+  }
+}
+
+/*! \internal
+
+  Sets the QPainter::Antialiasing render hint on the provided \a painter, depending on the \ref
+  setAntialiased state of this legend item as well as the overrides \ref
+  QCustomPlot::setAntialiasedElements and \ref QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAbstractLegendItem::applyAntialiasingHint(QCPPainter *painter) const
+{
+  if (mParentLegend->mParentPlot->notAntialiasedElements().testFlag(QCP::aeLegendItems))
+    painter->setAntialiasing(false);
+  else if (mParentLegend->mParentPlot->antialiasedElements().testFlag(QCP::aeLegendItems))
+    painter->setAntialiasing(true);
+  else
+    painter->setAntialiasing(mAntialiased);
+}
+
+
+// ================================================================================
+// =================== QCPPlottableLegendItem
+// ================================================================================
+/*! \class QCPPlottableLegendItem
+  \brief A legend item representing a plottable with an icon and the plottable name.
+  
+  This is the standard legend item for plottables. It displays an icon of the plottable next to the
+  plottable name. The icon is drawn by the respective plottable itself (\ref
+  QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
+  For example, the QCPGraph draws a centered horizontal line with a single scatter point in the
+  middle and filling (if enabled) below.
+  
+  Legend items of this type are always associated with one plottable (retrievable via the
+  plottable() function and settable with the constructor). You may change the font of the plottable
+  name with \ref setFont. If \ref setTextWrap is set to true, the plottable name will wrap at the
+  right legend boundary (see \ref QCPLegend::setMinimumSize). Icon padding and border pen is taken
+  from the parent QCPLegend, see \ref QCPLegend::setIconBorderPen and \ref
+  QCPLegend::setIconTextPadding.
+
+  The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
+  creates/removes legend items of this type in the default implementation. However, these functions
+  may be reimplemented such that a different kind of legend item (e.g a direct subclass of
+  QCPAbstractLegendItem) is used for that plottable.
+*/
+
+/*!
+  Creates a new legend item associated with \a plottable.
+  
+  Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
+  
+  A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
+  QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
+*/
+QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
+  QCPAbstractLegendItem(parent),
+  mPlottable(plottable),
+  mTextWrap(false)
+{
+}
+
+/*!
+  Sets whether the text of the legend item is wrapped at word boundaries to fit the with of the
+  legend.
+  
+  To prevent the legend autoSize feature (QCPLegend::setAutoSize) from compressing the text too
+  strong by wrapping it very often, set an appropriate minimum width with
+  QCPLegend::setMinimumSize.
+*/
+void QCPPlottableLegendItem::setTextWrap(bool wrap)
+{
+  mTextWrap = wrap;
+}
+
+/*! \internal
+  
+  Returns the pen that shall be used to draw the icon border, taking into account the selection
+  state of this item.
+*/
+QPen QCPPlottableLegendItem::getIconBorderPen() const
+{
+  return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
+}
+
+/*! \internal
+  
+  Returns the text color that shall be used to draw text, taking into account the selection state
+  of this item.
+*/
+QColor QCPPlottableLegendItem::getTextColor() const
+{
+  return mSelected ? mSelectedTextColor : mTextColor;
+}
+
+/*! \internal
+  
+  Returns the font that shall be used to draw text, taking into account the selection state of this
+  item.
+*/
+QFont QCPPlottableLegendItem::getFont() const
+{
+  return mSelected ? mSelectedFont : mFont;
+}
+
+/*! \internal
+  
+  Draws the item with \a painter into \a rect.
+
+  The width of the passed rect is used as text wrapping width, when \ref setTextWrap is enabled.
+  The height is ignored. The rect is not used as a clipping rect (overpainting is not prevented
+  inside this function), so you should set an appropriate clipping rect on the painter before
+  calling this function. Ideally, the width of the rect should be the result of a preceding call to
+  \ref size.
+*/
+void QCPPlottableLegendItem::draw(QCPPainter *painter, const QRect &rect) const
+{
+  if (!mPlottable) return;
+  painter->setFont(getFont());
+  painter->setPen(QPen(getTextColor()));
+  int iconTextPadding = mParentLegend->iconTextPadding();
+  QSize iconSize = mParentLegend->iconSize();
+  QRect textRect;
+  QRect iconRect(rect.topLeft(), iconSize);
+  if (mTextWrap)
+  {
+    // take width from rect since our text should wrap there (only icon must fit at least):
+    textRect = painter->fontMetrics().boundingRect(0, 0, rect.width()-iconTextPadding-iconSize.width(), rect.height(), Qt::TextDontClip | Qt::TextWordWrap, mPlottable->name());
+    if (textRect.height() < iconSize.height()) // text smaller than icon, center text vertically in icon height
+    {
+      painter->drawText(rect.x()+iconSize.width()+iconTextPadding, rect.y(), rect.width()-iconTextPadding-iconSize.width(), iconSize.height(), Qt::TextDontClip | Qt::TextWordWrap, mPlottable->name());
+    } else // text bigger than icon, position top of text with top of icon
+    {
+      painter->drawText(rect.x()+iconSize.width()+iconTextPadding, rect.y(), rect.width()-iconTextPadding-iconSize.width(), textRect.height(), Qt::TextDontClip | Qt::TextWordWrap, mPlottable->name());
+    }
+  } else
+  {
+    // text can't wrap (except with explicit newlines), center at current item size (icon size)
+    textRect = painter->fontMetrics().boundingRect(0, 0, 0, rect.height(), Qt::TextDontClip, mPlottable->name());
+    if (textRect.height() < iconSize.height()) // text smaller than icon, center text vertically in icon height
+    {
+      painter->drawText(rect.x()+iconSize.width()+iconTextPadding, rect.y(), rect.width(), iconSize.height(), Qt::TextDontClip, mPlottable->name());
+    } else // text bigger than icon, position top of text with top of icon
+    {
+      painter->drawText(rect.x()+iconSize.width()+iconTextPadding, rect.y(), rect.width(), textRect.height(), Qt::TextDontClip, mPlottable->name());
+    }
+  }
+  // draw icon:
+  painter->save();
+  painter->setClipRect(iconRect, Qt::IntersectClip);
+  mPlottable->drawLegendIcon(painter, iconRect);
+  painter->restore();
+  // draw icon border:
+  if (getIconBorderPen().style() != Qt::NoPen)
+  {
+    painter->setPen(getIconBorderPen());
+    painter->setBrush(Qt::NoBrush);
+    painter->drawRect(iconRect);
+  }
+}
+
+/*! \internal
+  
+  Calculates and returns the size of this item. If \ref setTextWrap is enabled, the width of \a
+  targetSize will be used as the text wrapping width. This does not guarantee, that the width of
+  the returned QSize is the same as the width of \a targetSize, since wrapping occurs only at word
+  boundaries. So a single word that extends beyond the width of \a targetSize, will stretch the
+  returned QSize accordingly.
+  
+  The height of \a targetSize is ignored. The height of the returned QSize is either the height
+  of the icon or the height of the text bounding box, whichever is larger.
+*/
+QSize QCPPlottableLegendItem::size(const QSize &targetSize) const
+{
+  if (!mPlottable) return QSize();
+  QSize result(0, 0);
+  QRect textRect;
+  QFontMetrics fontMetrics(getFont());
+  int iconTextPadding = mParentLegend->iconTextPadding();
+  QSize iconSize = mParentLegend->iconSize();
+  if (mTextWrap)
+  {
+    // take width from targetSize since our text can wrap (Only icon must fit at least):
+    textRect = fontMetrics.boundingRect(0, 0, targetSize.width()-iconTextPadding-iconSize.width(), iconSize.height(), Qt::TextDontClip | Qt::TextWordWrap, mPlottable->name());
+  } else
+  {
+    // text can't wrap (except with explicit newlines), center at current item size (icon size)
+    textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
+  }
+  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());
+  result.setHeight(qMax(textRect.height(), iconSize.height()));
+  return result;
+}
+
+// ================================================================================
+// =================== QCPCurve
+// ================================================================================
+/*! \class QCPCurve
+  \brief A plottable representing a parametric curve in a plot.
+
+  To plot data, assign it with the \ref setData or \ref addData functions.
+  
+  \section appearance Changing the appearance
+  
+  The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush).
+  \section usage Usage
+  
+  Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So
+  the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.) 
+  
+  Usually, you first create an instance:
+  \code
+  QCPCurve *newCurve = new QCPCurve(customPlot->xAxis, customPlot->yAxis);\endcode
+  add it to the customPlot with QCustomPlot::addPlottable:
+  \code
+  customPlot->addPlottable(newCurve);\endcode
+  and then modify the properties of the newly created plottable, e.g.:
+  \code
+  newCurve->setName("Fermat's Spiral");
+  newCurve->setData(tData, xData, yData);\endcode
+*/
+
+/*!
+  Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
+  axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
+  the same orientation. If either of these restrictions is violated, a corresponding message is
+  printed to the debug output (qDebug), the construction is not aborted, though.
+  
+  The constructed QCPCurve can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
+  then takes ownership of the graph.
+*/
+QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis)
+{
+  mData = new QCPCurveDataMap;
+  mPen.setColor(Qt::blue);
+  mPen.setStyle(Qt::SolidLine);
+  mBrush.setColor(Qt::blue);
+  mBrush.setStyle(Qt::NoBrush);
+  mSelectedPen = mPen;
+  mSelectedPen.setWidthF(2.5);
+  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
+  mSelectedBrush = mBrush;
+  
+  setScatterSize(6);
+  setScatterStyle(QCP::ssNone);
+  setLineStyle(lsLine);
+}
+
+QCPCurve::~QCPCurve()
+{
+  delete mData;
+}
+
+/*!
+  Replaces the current data with the provided \a data.
+  
+  If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
+  takes ownership of the passed data and replaces the internal data pointer with it. This is
+  significantly faster than copying for large datasets.
+*/
+void QCPCurve::setData(QCPCurveDataMap *data, bool copy)
+{
+  if (copy)
+  {
+    *mData = *data;
+  } else
+  {
+    delete mData;
+    mData = data;
+  }
+}
+
+/*! \overload
+  
+  Replaces the current data with the provided points in \a t, \a key and \a value tuples. The
+  provided vectors should have equal length. Else, the number of added points will be the size of
+  the smallest vector.
+*/
+void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
+{
+  mData->clear();
+  int n = t.size();
+  n = qMin(n, key.size());
+  n = qMin(n, value.size());
+  QCPCurveData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.t = t[i];
+    newData.key = key[i];
+    newData.value = value[i];
+    mData->insertMulti(newData.t, newData);
+  }
+}
+
+/*! \overload
+  
+  Replaces the current data with the provided \a key and \a value pairs. The t parameter
+  of each data point will be set to the integer index of the respective key/value pair.
+*/
+void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  QCPCurveData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.t = i; // no t vector given, so we assign t the index of the key/value pair
+    newData.key = key[i];
+    newData.value = value[i];
+    mData->insertMulti(newData.t, newData);
+  }
+}
+
+/*! 
+  Sets the visual appearance of single data points in the plot. If set to \ref QCP::ssNone, no scatter points
+  are drawn (e.g. for line-only-plots with appropriate line style).
+  \see ScatterStyle, setLineStyle
+*/
+void QCPCurve::setScatterStyle(QCP::ScatterStyle style)
+{
+  mScatterStyle = style;
+}
+
+/*! 
+  This defines how big (in pixels) single scatters are drawn, if scatter style (\ref
+  setScatterStyle) isn't \ref QCP::ssNone, \ref QCP::ssDot or \ref QCP::ssPixmap. Floating point values are
+  allowed for fine grained control over optical appearance with antialiased painting.
+  
+  \see ScatterStyle
+*/
+void QCPCurve::setScatterSize(double size)
+{
+  mScatterSize = size;
+}
+
+/*! 
+  If the scatter style (\ref setScatterStyle) is set to ssPixmap, this function defines the QPixmap
+  that will be drawn centered on the data point coordinate.
+  
+  \see ScatterStyle
+*/
+void QCPCurve::setScatterPixmap(const QPixmap &pixmap)
+{
+  mScatterPixmap = pixmap;
+}
+
+/*!
+  Sets how the single data points are connected in the plot or how they are represented visually
+  apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref
+  setScatterStyle to the desired scatter style.
+  
+  \see setScatterStyle
+*/
+void QCPCurve::setLineStyle(QCPCurve::LineStyle style)
+{
+  mLineStyle = style;
+}
+
+/*!
+  Adds the provided data points in \a dataMap to the current data.
+  \see removeData
+*/
+void QCPCurve::addData(const QCPCurveDataMap &dataMap)
+{
+  mData->unite(dataMap);
+}
+
+/*! \overload
+  Adds the provided single data point in \a data to the current data.
+  \see removeData
+*/
+void QCPCurve::addData(const QCPCurveData &data)
+{
+  mData->insertMulti(data.t, data);
+}
+
+/*! \overload
+  Adds the provided single data point as \a t, \a key and \a value tuple to the current data
+  \see removeData
+*/
+void QCPCurve::addData(double t, double key, double value)
+{
+  QCPCurveData newData;
+  newData.t = t;
+  newData.key = key;
+  newData.value = value;
+  mData->insertMulti(newData.t, newData);
+}
+
+/*! \overload
+  
+  Adds the provided single data point as \a key and \a value pair to the current data The t
+  parameter of the data point is set to the t of the last data point plus 1. If there is no last
+  data point, t will be set to 0.
+  
+  \see removeData
+*/
+void QCPCurve::addData(double key, double value)
+{
+  QCPCurveData newData;
+  if (!mData->isEmpty())
+    newData.t = (mData->constEnd()-1).key()+1;
+  else
+    newData.t = 0;
+  newData.key = key;
+  newData.value = value;
+  mData->insertMulti(newData.t, newData);
+}
+
+/*! \overload
+  Adds the provided data points as \a t, \a key and \a value tuples to the current data.
+  \see removeData
+*/
+void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
+{
+  int n = ts.size();
+  n = qMin(n, keys.size());
+  n = qMin(n, values.size());
+  QCPCurveData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.t = ts[i];
+    newData.key = keys[i];
+    newData.value = values[i];
+    mData->insertMulti(newData.t, newData);
+  }
+}
+
+/*!
+  Removes all data points with curve parameter t smaller than \a t.
+  \see addData, clearData
+*/
+void QCPCurve::removeDataBefore(double t)
+{
+  QCPCurveDataMap::iterator it = mData->begin();
+  while (it != mData->end() && it.key() < t)
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with curve parameter t greater than \a t.
+  \see addData, clearData
+*/
+void QCPCurve::removeDataAfter(double t)
+{
+  if (mData->isEmpty()) return;
+  QCPCurveDataMap::iterator it = mData->upperBound(t);
+  while (it != mData->end())
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with curve parameter t between \a fromt and \a tot. if \a fromt is
+  greater or equal to \a tot, the function does nothing. To remove a single data point with known
+  t, use \ref removeData(double t).
+  
+  \see addData, clearData
+*/
+void QCPCurve::removeData(double fromt, double tot)
+{
+  if (fromt >= tot || mData->isEmpty()) return;
+  QCPCurveDataMap::iterator it = mData->upperBound(fromt);
+  QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
+  while (it != itEnd)
+    it = mData->erase(it);
+}
+
+/*! \overload
+  
+  Removes a single data point at curve parameter \a t. If the position is not known with absolute
+  precision, consider using \ref removeData(double fromt, double tot) with a small fuzziness
+  interval around the suspected position, depeding on the precision with which the curve parameter
+  is known.
+  
+  \see addData, clearData
+*/
+void QCPCurve::removeData(double t)
+{
+  mData->remove(t);
+}
+
+/*!
+  Removes all data points.
+  \see removeData, removeDataAfter, removeDataBefore
+*/
+void QCPCurve::clearData()
+{
+  mData->clear();
+}
+
+/* inherits documentation from base class */
+double QCPCurve::selectTest(const QPointF &pos) const
+{
+  if (mData->isEmpty() || !mVisible)
+    return -1;
+  
+  return pointDistance(pos);
+}
+
+/* inherits documentation from base class */
+void QCPCurve::draw(QCPPainter *painter)
+{
+  if (mData->isEmpty()) return;
+  
+  // allocate line vector:
+  QVector<QPointF> *lineData = new QVector<QPointF>;
+  // fill with curve data:
+  getCurveData(lineData);
+  // draw curve fill:
+  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
+  {
+    applyFillAntialiasingHint(painter);
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(mainBrush());
+    painter->drawPolygon(QPolygonF(*lineData));
+  }
+  // draw curve line:
+  if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mainPen());
+    painter->setBrush(Qt::NoBrush);
+    // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
+    if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
+        painter->pen().style() == Qt::SolidLine &&
+        !painter->pdfExportMode())
+    {
+      for (int i=1; i<lineData->size(); ++i)
+        painter->drawLine(lineData->at(i-1), lineData->at(i));
+    } else
+    {  
+      painter->drawPolyline(QPolygonF(*lineData));
+    }
+  }
+  // draw scatters:
+  if (mScatterStyle != QCP::ssNone)
+    drawScatterPlot(painter, lineData);
+  // free allocated line data:
+  delete lineData;
+}
+
+/* inherits documentation from base class */
+void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRect &rect) const
+{
+  // draw fill:
+  if (mBrush.style() != Qt::NoBrush)
+  {
+    applyFillAntialiasingHint(painter);
+    painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
+  }
+  // draw line vertically centered:
+  if (mLineStyle != lsNone)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
+  }
+  // draw scatter symbol:
+  if (mScatterStyle != QCP::ssNone)
+  {
+    if (mScatterStyle == QCP::ssPixmap && (mScatterPixmap.size().width() > rect.width() || mScatterPixmap.size().height() > rect.height()))
+    {
+      // handle pixmap scatters that are larger than legend icon rect separately.
+      // We resize them and draw them manually, instead of calling drawScatter:
+      QSize newSize = mScatterPixmap.size();
+      newSize.scale(rect.size(), Qt::KeepAspectRatio);
+      QRect targetRect;
+      targetRect.setSize(newSize);
+      targetRect.moveCenter(rect.center());
+      bool smoothBackup = painter->testRenderHint(QPainter::SmoothPixmapTransform);
+      painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
+      painter->drawPixmap(targetRect, mScatterPixmap);
+      painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
+    } else
+    {
+      applyScattersAntialiasingHint(painter);
+      painter->setPen(mPen);
+      painter->drawScatter(QRectF(rect).center().x(), QRectF(rect).center().y(), mScatterSize, mScatterStyle);
+    }
+  }
+}
+
+/*! \internal
+  
+  Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
+  the line style and are always drawn if scatter style is not \ref QCP::ssNone.
+*/
+void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
+{
+  // draw scatter point symbols:
+  applyScattersAntialiasingHint(painter);
+  painter->setPen(mainPen());
+  painter->setBrush(mainBrush());
+  painter->setScatterPixmap(mScatterPixmap);
+  for (int i=0; i<pointData->size(); ++i)
+    painter->drawScatter(pointData->at(i).x(), pointData->at(i).y(), mScatterSize, mScatterStyle);
+}
+
+/*! \internal
+  
+  called by QCPCurve::draw to generate a point vector (pixels) which represents the line of the
+  curve. Line segments that aren't visible in the current axis rect are handled in an optimized
+  way.
+*/
+void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
+{
+  /* Extended sides of axis rect R divide space into 9 regions:
+     1__|_4_|__7  
+     2__|_R_|__8
+     3  | 6 |  9 
+     General idea: If the two points of a line segment are in the same region (that is not R), the line segment corner is removed.
+     Curves outside R become straight lines closely outside of R which greatly reduces drawing time, yet keeps the look of lines and
+     fills inside R consistent.
+     The region R has index 5.
+  */
+  lineData->reserve(mData->size());
+  QCPCurveDataMap::const_iterator it;
+  int lastRegion = 5;
+  int currentRegion = 5;
+  double RLeft = mKeyAxis->range().lower;
+  double RRight = mKeyAxis->range().upper;
+  double RBottom = mValueAxis->range().lower;
+  double RTop = mValueAxis->range().upper;
+  double x, y; // current key/value
+  bool addedLastAlready = true;
+  bool firstPoint = true; // first point must always be drawn, to make sure fill works correctly
+  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
+  {
+    x = it.value().key;
+    y = it.value().value;
+    // determine current region:
+    if (x < RLeft) // region 123
+    {
+      if (y > RTop)
+        currentRegion = 1;
+      else if (y < RBottom)
+        currentRegion = 3;
+      else
+        currentRegion = 2;
+    } else if (x > RRight) // region 789
+    {
+      if (y > RTop)
+        currentRegion = 7;
+      else if (y < RBottom)
+        currentRegion = 9;
+      else
+        currentRegion = 8;
+    } else // region 456
+    {
+      if (y > RTop)
+        currentRegion = 4;
+      else if (y < RBottom)
+        currentRegion = 6;
+      else
+        currentRegion = 5;
+    }
+    
+    /*
+      Watch out, the next part is very tricky. It modifies the curve such that it seems like the
+      whole thing is still drawn, but actually the points outside the axisRect are simplified
+      ("optimized") greatly. There are some subtle special cases when line segments are large and
+      thereby each subsequent point may be in a different region or even skip some.
+    */
+    // determine whether to keep current point:
+    if (currentRegion == 5 || (firstPoint && mBrush.style() != Qt::NoBrush)) // current is in R, add current and last if it wasn't added already
+    {
+      if (!addedLastAlready) // in case curve just entered R, make sure the last point outside R is also drawn correctly
+        lineData->append(coordsToPixels((it-1).value().key, (it-1).value().value)); // add last point to vector
+      else if (lastRegion != 5) // added last already. If that's the case, we probably added it at optimized position. So go back and make sure it's at original position (else the angle changes under which this segment enters R)
+      {
+        if (!firstPoint) // because on firstPoint, currentRegion is 5 and addedLastAlready is true, although there is no last point
+          lineData->replace(lineData->size()-1, coordsToPixels((it-1).value().key, (it-1).value().value));
+      }
+      lineData->append(coordsToPixels(it.value().key, it.value().value)); // add current point to vector
+      addedLastAlready = true; // so in next iteration, we don't add this point twice
+    } else if (currentRegion != lastRegion) // changed region, add current and last if not added already
+    {
+      // using outsideCoordsToPixels instead of coorsToPixels for optimized point placement (places points just outside axisRect instead of potentially far away)
+      
+      // if we're coming from R or we skip diagonally over the corner regions (so line might still be visible in R), we can't place points optimized
+      if (lastRegion == 5 || // coming from R
+          ((lastRegion==2 && currentRegion==4) || (lastRegion==4 && currentRegion==2)) || // skip top left diagonal
+          ((lastRegion==4 && currentRegion==8) || (lastRegion==8 && currentRegion==4)) || // skip top right diagonal
+          ((lastRegion==8 && currentRegion==6) || (lastRegion==6 && currentRegion==8)) || // skip bottom right diagonal
+          ((lastRegion==6 && currentRegion==2) || (lastRegion==2 && currentRegion==6))    // skip bottom left diagonal
+          )
+      {
+        // always add last point if not added already, original:
+        if (!addedLastAlready)
+          lineData->append(coordsToPixels((it-1).value().key, (it-1).value().value));
+        // add current point, original:
+        lineData->append(coordsToPixels(it.value().key, it.value().value));
+      } else // no special case that forbids optimized point placement, so do it:
+      {
+        // always add last point if not added already, optimized:
+        if (!addedLastAlready)
+          lineData->append(outsideCoordsToPixels((it-1).value().key, (it-1).value().value, currentRegion));
+        // add current point, optimized:
+        lineData->append(outsideCoordsToPixels(it.value().key, it.value().value, currentRegion));
+      }
+      addedLastAlready = true; // so that if next point enters 5, or crosses another region boundary, we don't add this point twice
+    } else // neither in R, nor crossed a region boundary, skip current point
+    {
+      addedLastAlready = false;
+    }
+    lastRegion = currentRegion;
+    firstPoint = false;
+  }
+  // If curve ends outside R, we want to add very last point so the fill looks like it should when the curve started inside R:
+  if (lastRegion != 5 && mBrush.style() != Qt::NoBrush && !mData->isEmpty())
+    lineData->append(coordsToPixels((mData->constEnd()-1).value().key, (mData->constEnd()-1).value().value));
+}
+
+/*! \internal 
+  
+  Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a
+  pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in
+  \ref selectTest.
+*/
+double QCPCurve::pointDistance(const QPointF &pixelPoint) const
+{
+  if (mData->isEmpty())
+  {
+    qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
+    return 500;
+  }
+  if (mData->size() == 1)
+  {
+    QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
+    return QVector2D(dataPoint-pixelPoint).length();
+  }
+  
+  // calculate minimum distance to line segments:
+  QVector<QPointF> *lineData = new QVector<QPointF>;
+  getCurveData(lineData);
+  double minDistSqr = std::numeric_limits<double>::max();
+  for (int i=0; i<lineData->size()-1; ++i)
+  {
+    double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
+    if (currentDistSqr < minDistSqr)
+      minDistSqr = currentDistSqr;
+  }
+  delete lineData;
+  return sqrt(minDistSqr);
+}
+
+/*! \internal
+  
+  This is a specialized \ref coordsToPixels function for points that are outside the visible
+  axisRect and just crossing a boundary (since \ref getCurveData reduces non-visible curve segments
+  to those line segments that cross region boundaries, see documentation there). It only uses the
+  coordinate parallel to the region boundary of the axisRect. The other coordinate is picked 10
+  pixels outside the axisRect. Together with the optimization in \ref getCurveData this improves
+  performance for large curves (or zoomed in ones) significantly while keeping the illusion the
+  whole curve and its filling is still being drawn for the viewer.
+*/
+QPointF QCPCurve::outsideCoordsToPixels(double key, double value, int region) const
+{
+  int margin = 10;
+  QRect axisRect = mKeyAxis->axisRect() | mValueAxis->axisRect();
+  QPointF result = coordsToPixels(key, value);
+  switch (region)
+  {
+    case 2: result.setX(axisRect.left()-margin); break; // left
+    case 8: result.setX(axisRect.right()+margin); break; // right
+    case 4: result.setY(axisRect.top()-margin); break; // top
+    case 6: result.setY(axisRect.bottom()+margin); break; // bottom
+    case 1: result.setX(axisRect.left()-margin);
+            result.setY(axisRect.top()-margin); break; // top left
+    case 7: result.setX(axisRect.right()+margin);
+            result.setY(axisRect.top()-margin); break; // top right
+    case 9: result.setX(axisRect.right()+margin);
+            result.setY(axisRect.bottom()+margin); break; // bottom right
+    case 3: result.setX(axisRect.left()-margin);
+            result.setY(axisRect.bottom()+margin); break; // bottom left
+  }
+  return result;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPCurve::getKeyRange(bool &validRange, SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+  
+  double current;
+  
+  QCPCurveDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().key;
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
+    {
+      if (current < range.lower || !haveLower)
+      {
+        range.lower = current;
+        haveLower = true;
+      }
+      if (current > range.upper || !haveUpper)
+      {
+        range.upper = current;
+        haveUpper = true;
+      }
+    }
+    ++it;
+  }
+  
+  validRange = haveLower && haveUpper;
+  return range;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPCurve::getValueRange(bool &validRange, SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+  
+  double current;
+  
+  QCPCurveDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().value;
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
+    {
+      if (current < range.lower || !haveLower)
+      {
+        range.lower = current;
+        haveLower = true;
+      }
+      if (current > range.upper || !haveUpper)
+      {
+        range.upper = current;
+        haveUpper = true;
+      }
+    }
+    ++it;
+  }
+  
+  validRange = haveLower && haveUpper;
+  return range;
+}
+
+// ================================================================================
+// =================== QCPBars
+// ================================================================================
+/*! \class QCPBars
+  \brief A plottable representing a bar chart in a plot.
+
+  To plot data, assign it with the \ref setData or \ref addData functions.
+  
+  \section appearance Changing the appearance
+  
+  The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush).
+  
+  Bar charts are stackable. This means, Two QCPBars plottables can be placed on top of each other
+  (see \ref QCPBars::moveAbove). Then, when two bars are at the same key position, they will appear
+  stacked.
+  
+  \section usage Usage
+  
+  Like all data representing objects in QCustomPlot, the QCPBars is a plottable
+  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
+  (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
+  
+  Usually, you first create an instance:
+  \code
+  QCPBars *newBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);\endcode
+  add it to the customPlot with QCustomPlot::addPlottable:
+  \code
+  customPlot->addPlottable(newBars);\endcode
+  and then modify the properties of the newly created plottable, e.g.:
+  \code
+  newBars->setName("Country population");
+  newBars->setData(xData, yData);\endcode
+*/
+
+/*! \fn QCPBars *QCPBars::barBelow() const
+  Returns the bars plottable that is directly below this bars plottable.
+  If there is no such plottable, returns 0.
+  
+  \see barAbove, moveBelow, moveAbove
+*/
+
+/*! \fn QCPBars *QCPBars::barAbove() const
+  Returns the bars plottable that is directly above this bars plottable.
+  If there is no such plottable, returns 0.
+  
+  \see barBelow, moveBelow, moveAbove
+*/
+
+/*!
+  Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
+  axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
+  the same orientation. If either of these restrictions is violated, a corresponding message is
+  printed to the debug output (qDebug), the construction is not aborted, though.
+  
+  The constructed QCPBars can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
+  then takes ownership of the bar chart.
+*/
+QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis),
+  mBarBelow(0),
+  mBarAbove(0)
+{
+  mData = new QCPBarDataMap;
+  mPen.setColor(Qt::blue);
+  mPen.setStyle(Qt::SolidLine);
+  mBrush.setColor(QColor(40, 50, 255, 30));
+  mBrush.setStyle(Qt::SolidPattern);
+  mSelectedPen = mPen;
+  mSelectedPen.setWidthF(2.5);
+  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
+  mSelectedBrush = mBrush;
+  
+  mWidth = 0.75;
+}
+
+QCPBars::~QCPBars()
+{
+  if (mBarBelow || mBarAbove)
+    connectBars(mBarBelow, mBarAbove); // take this bar out of any stacking
+  delete mData;
+}
+
+/*!
+  Sets the width of the bars in plot (key) coordinates.
+*/
+void QCPBars::setWidth(double width)
+{
+  mWidth = width;
+}
+
+/*!
+  Replaces the current data with the provided \a data.
+  
+  If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
+  takes ownership of the passed data and replaces the internal data pointer with it. This is
+  significantly faster than copying for large datasets.
+*/
+void QCPBars::setData(QCPBarDataMap *data, bool copy)
+{
+  if (copy)
+  {
+    *mData = *data;
+  } else
+  {
+    delete mData;
+    mData = data;
+  }
+}
+
+/*! \overload
+  
+  Replaces the current data with the provided points in \a key and \a value tuples. The
+  provided vectors should have equal length. Else, the number of added points will be the size of
+  the smallest vector.
+*/
+void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  QCPBarData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    mData->insertMulti(newData.key, newData);
+  }
+}
+
+/*!
+  Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear
+  below the bars of \a bars. The move target \a bars must use the same key and value axis as this
+  plottable.
+  
+  Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
+  has a bars object below itself, this bars object is inserted between the two. If this bars object
+  is already between two other bars, the two other bars will be stacked on top of each other after
+  the operation.
+  
+  To remove this bars plottable from any stacking, set \a bars to 0.
+  
+  \see moveBelow, barAbove, barBelow
+*/
+void QCPBars::moveBelow(QCPBars *bars)
+{
+  if (bars == this) return;
+  if (bars->keyAxis() != mKeyAxis || bars->valueAxis() != mValueAxis)
+  {
+    qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
+    return;
+  }
+  // remove from stacking:
+  connectBars(mBarBelow, mBarAbove); // Note: also works if one (or both) of them is 0
+  // if new bar given, insert this bar below it:
+  if (bars)
+  {
+    if (bars->mBarBelow)
+      connectBars(bars->mBarBelow, this);
+    connectBars(this, bars);
+  }
+}
+
+/*!
+  Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear
+  above the bars of \a bars. The move target \a bars must use the same key and value axis as this
+  plottable.
+  
+  Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
+  has a bars object below itself, this bars object is inserted between the two. If this bars object
+  is already between two other bars, the two other bars will be stacked on top of each other after
+  the operation.
+  
+  To remove this bars plottable from any stacking, set \a bars to 0.
+  
+  \see moveBelow, barBelow, barAbove
+*/
+void QCPBars::moveAbove(QCPBars *bars)
+{
+  if (bars == this) return;
+  if (bars && (bars->keyAxis() != mKeyAxis || bars->valueAxis() != mValueAxis))
+  {
+    qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
+    return;
+  }
+  // remove from stacking:
+  connectBars(mBarBelow, mBarAbove); // Note: also works if one (or both) of them is 0
+  // if new bar given, insert this bar above it:
+  if (bars)
+  {
+    if (bars->mBarAbove)
+      connectBars(this, bars->mBarAbove);
+    connectBars(bars, this);
+  }
+}
+
+/*!
+  Adds the provided data points in \a dataMap to the current data.
+  \see removeData
+*/
+void QCPBars::addData(const QCPBarDataMap &dataMap)
+{
+  mData->unite(dataMap);
+}
+
+/*! \overload
+  Adds the provided single data point in \a data to the current data.
+  \see removeData
+*/
+void QCPBars::addData(const QCPBarData &data)
+{
+  mData->insertMulti(data.key, data);
+}
+
+/*! \overload
+  Adds the provided single data point as \a key and \a value tuple to the current data
+  \see removeData
+*/
+void QCPBars::addData(double key, double value)
+{
+  QCPBarData newData;
+  newData.key = key;
+  newData.value = value;
+  mData->insertMulti(newData.key, newData);
+}
+
+/*! \overload
+  Adds the provided data points as \a key and \a value tuples to the current data.
+  \see removeData
+*/
+void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
+{
+  int n = keys.size();
+  n = qMin(n, values.size());
+  QCPBarData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = keys[i];
+    newData.value = values[i];
+    mData->insertMulti(newData.key, newData);
+  }
+}
+
+/*!
+  Removes all data points with key smaller than \a key.
+  \see addData, clearData
+*/
+void QCPBars::removeDataBefore(double key)
+{
+  QCPBarDataMap::iterator it = mData->begin();
+  while (it != mData->end() && it.key() < key)
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with key greater than \a key.
+  \see addData, clearData
+*/
+void QCPBars::removeDataAfter(double key)
+{
+  if (mData->isEmpty()) return;
+  QCPBarDataMap::iterator it = mData->upperBound(key);
+  while (it != mData->end())
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with key between \a fromKey and \a toKey. if \a fromKey is
+  greater or equal to \a toKey, the function does nothing. To remove a single data point with known
+  key, use \ref removeData(double key).
+  
+  \see addData, clearData
+*/
+void QCPBars::removeData(double fromKey, double toKey)
+{
+  if (fromKey >= toKey || mData->isEmpty()) return;
+  QCPBarDataMap::iterator it = mData->upperBound(fromKey);
+  QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
+  while (it != itEnd)
+    it = mData->erase(it);
+}
+
+/*! \overload
+  
+  Removes a single data point at \a key. If the position is not known with absolute precision,
+  consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
+  around the suspected position, depeding on the precision with which the key is known.
+  
+  \see addData, clearData
+*/
+void QCPBars::removeData(double key)
+{
+  mData->remove(key);
+}
+
+/*!
+  Removes all data points.
+  \see removeData, removeDataAfter, removeDataBefore
+*/
+void QCPBars::clearData()
+{
+  mData->clear();
+}
+
+/* inherits documentation from base class */
+double QCPBars::selectTest(const QPointF &pos) const
+{
+  QCPBarDataMap::ConstIterator it;
+  double posKey, posValue;
+  pixelsToCoords(pos, posKey, posValue);
+  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
+  {
+    double baseValue = getBaseValue(it.key(), it.value().value >=0);
+    QCPRange keyRange(it.key()-mWidth*0.5, it.key()+mWidth*0.5);
+    QCPRange valueRange(baseValue, baseValue+it.value().value);
+    if (keyRange.contains(posKey) && valueRange.contains(posValue))
+      return mParentPlot->selectionTolerance()*0.99;
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPBars::draw(QCPPainter *painter)
+{
+  if (mData->isEmpty()) return;
+  
+  QCPBarDataMap::const_iterator it;
+  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
+  {
+    if (it.key()+mWidth*0.5 < mKeyAxis->range().lower || it.key()-mWidth*0.5 > mKeyAxis->range().upper)
+      continue;
+    QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
+    // draw bar fill:
+    if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
+    {
+      applyFillAntialiasingHint(painter);
+      painter->setPen(Qt::NoPen);
+      painter->setBrush(mainBrush());
+      painter->drawPolygon(barPolygon);
+    }
+    // draw bar line:
+    if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
+    {
+      applyDefaultAntialiasingHint(painter);
+      painter->setPen(mainPen());
+      painter->setBrush(Qt::NoBrush);
+      painter->drawPolyline(barPolygon);
+    }
+  }
+}
+
+/* inherits documentation from base class */
+void QCPBars::drawLegendIcon(QCPPainter *painter, const QRect &rect) const
+{
+  // draw filled rect:
+  applyDefaultAntialiasingHint(painter);
+  painter->setBrush(mBrush);
+  painter->setPen(mPen);
+  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
+  r.moveCenter(rect.center());
+  painter->drawRect(r);
+}
+
+/*! \internal
+  
+  Returns the polygon of a single bar with \a key and \a value. The Polygon is open at the bottom
+  and shifted according to the bar stacking (see \ref moveAbove).
+*/
+QPolygonF QCPBars::getBarPolygon(double key, double value) const
+{
+  QPolygonF result;
+  double baseValue = getBaseValue(key, value >= 0);
+  result << coordsToPixels(key-mWidth*0.5, baseValue);
+  result << coordsToPixels(key-mWidth*0.5, baseValue+value);
+  result << coordsToPixels(key+mWidth*0.5, baseValue+value);
+  result << coordsToPixels(key+mWidth*0.5, baseValue);
+  return result;
+}
+
+/*! \internal
+  
+  This function is called to find at which value to start drawing the base of a bar at \a key, when
+  it is stacked on top of another QCPBars (e.g. with \ref moveAbove).
+  
+  positive and negative bars are separated per stack (positive are stacked above 0-value upwards,
+  negative are stacked below 0-value downwards). This can be indicated with \a positive. So if the
+  bar for which we need the base value is negative, set \a positive to false.
+*/
+double QCPBars::getBaseValue(double key, bool positive) const
+{
+  if (mBarBelow)
+  {
+    double max = 0;
+    // find bars of mBarBelow that are approximately at key and find largest one:
+    QCPBarDataMap::const_iterator it = mBarBelow->mData->lowerBound(key-mWidth*0.1);
+    QCPBarDataMap::const_iterator itEnd = mBarBelow->mData->upperBound(key+mWidth*0.1);
+    while (it != itEnd)
+    {
+      if ((positive && it.value().value > max) ||
+          (!positive && it.value().value < max))
+        max = it.value().value;
+      ++it;
+    }
+    // recurse down the bar-stack to find the total height:
+    return max + mBarBelow->getBaseValue(key, positive);
+  } else
+    return 0;
+}
+
+/*! \internal
+
+  Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties.
+  The bar(s) currently below lower and upper will become disconnected to lower/upper.
+  
+  If lower is zero, upper will be disconnected at the bottom.
+  If upper is zero, lower will be disconnected at the top.
+*/
+void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
+{
+  if (!lower && !upper) return;
+  
+  if (!lower) // disconnect upper at bottom
+  {
+    // disconnect old bar below upper:
+    if (upper->mBarBelow && upper->mBarBelow->mBarAbove == upper)
+      upper->mBarBelow->mBarAbove = 0;
+    upper->mBarBelow = 0;
+  } else if (!upper) // disconnect lower at top
+  {
+    // disconnect old bar above lower:
+    if (lower->mBarAbove && lower->mBarAbove->mBarBelow == lower)
+      lower->mBarAbove->mBarBelow = 0;
+    lower->mBarAbove = 0;
+  } else // connect lower and upper
+  {
+    // disconnect old bar above lower:
+    if (lower->mBarAbove && lower->mBarAbove->mBarBelow == lower)
+      lower->mBarAbove->mBarBelow = 0;
+    // disconnect old bar below upper:
+    if (upper->mBarBelow && upper->mBarBelow->mBarAbove == upper)
+      upper->mBarBelow->mBarAbove = 0;
+    lower->mBarAbove = upper;
+    upper->mBarBelow = lower;
+  }
+}
+
+/* inherits documentation from base class */
+QCPRange QCPBars::getKeyRange(bool &validRange, SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+  
+  double current;
+  double barWidthHalf = mWidth*0.5;
+  QCPBarDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().key;
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current+barWidthHalf < 0) || (inSignDomain == sdPositive && current-barWidthHalf > 0))
+    {
+      if (current-barWidthHalf < range.lower || !haveLower)
+      {
+        range.lower = current-barWidthHalf;
+        haveLower = true;
+      }
+      if (current+barWidthHalf > range.upper || !haveUpper)
+      {
+        range.upper = current+barWidthHalf;
+        haveUpper = true;
+      }
+    }
+    ++it;
+  }
+  
+  validRange = haveLower && haveUpper;
+  return range;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPBars::getValueRange(bool &validRange, SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = true; // set to true, because 0 should always be visible in bar charts
+  bool haveUpper = true; // set to true, because 0 should always be visible in bar charts
+  
+  double current;
+  
+  QCPBarDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().value + getBaseValue(it.value().key, it.value().value >= 0);
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
+    {
+      if (current < range.lower || !haveLower)
+      {
+        range.lower = current;
+        haveLower = true;
+      }
+      if (current > range.upper || !haveUpper)
+      {
+        range.upper = current;
+        haveUpper = true;
+      }
+    }
+    ++it;
+  }
+  
+  validRange = range.lower < range.upper;
+  return range;
+}
+
+
+// ================================================================================
+// =================== QCPStatisticalBox
+// ================================================================================
+
+/*! \class QCPStatisticalBox
+  \brief A plottable representing a single statistical box in a plot.
+
+  To plot data, assign it with the individual parameter functions or use \ref setData to set all
+  parameters at once. The individual funcions are:
+  \li \ref setMinimum
+  \li \ref setLowerQuartile
+  \li \ref setMedian
+  \li \ref setUpperQuartile
+  \li \ref setMaximum
+  
+  Additionally you can define a list of outliers, drawn as circle datapoints:
+  \li \ref setOutliers
+  
+  \section appearance Changing the appearance
+  
+  The appearance of the box itself is controlled via \ref setPen and \ref setBrush. You
+  may change the width of the box with \ref setWidth in plot coordinates (not pixels).
+
+  Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen, \ref
+  setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of the bar at the top
+  (maximum) or bottom (minimum).
+  
+  The median indicator line has its own pen, \ref setMedianPen.
+  
+  If the pens are changed, especially the whisker pen, make sure to set the capStyle to
+  Qt::FlatCap. Else, e.g. the whisker line might exceed the bar line by a few pixels due to the pen
+  cap being not perfectly flat.
+  
+  The Outlier data points are drawn normal scatter points. Their look can be controlled with \ref
+  setOutlierStyle and \ref setOutlierPen. The size (diameter) can be set with \ref setOutlierSize
+  in pixels.
+  
+  \section usage Usage
+  
+  Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable
+  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
+  (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
+  
+  Usually, you first create an instance:
+  \code
+  QCPStatisticalBox *newBox = new QCPStatisticalBox(customPlot->xAxis, customPlot->yAxis);\endcode
+  add it to the customPlot with QCustomPlot::addPlottable:
+  \code
+  customPlot->addPlottable(newBox);\endcode
+  and then modify the properties of the newly created plottable, e.g.:
+  \code
+  newBox->setName("Measurement Series 1");
+  newBox->setData(1, 3, 4, 5, 7);
+  newBox->setOutliers(QVector<double>() << 0.5 << 0.64 << 7.2 << 7.42);\endcode
+*/
+
+/*!
+  Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its
+  value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and
+  not have the same orientation. If either of these restrictions is violated, a corresponding
+  message is printed to the debug output (qDebug), the construction is not aborted, though.
+  
+  The constructed statistical box can be added to the plot with QCustomPlot::addPlottable,
+  QCustomPlot then takes ownership of the statistical box.
+*/
+QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis),
+  mKey(0),
+  mMinimum(0),
+  mLowerQuartile(0),
+  mMedian(0),
+  mUpperQuartile(0),
+  mMaximum(0)
+{
+  setOutlierStyle(QCP::ssCircle);
+  setOutlierSize(5);
+  setWhiskerWidth(0.2);
+  setWidth(0.5);
+  
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue, 2.5));
+  setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
+  setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
+  setWhiskerBarPen(QPen(Qt::black));
+  setOutlierPen(QPen(Qt::blue));
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+}
+
+QCPStatisticalBox::~QCPStatisticalBox()
+{
+}
+
+/*!
+  Sets the key coordinate of the statistical box.
+*/
+void QCPStatisticalBox::setKey(double key)
+{
+  mKey = key;
+}
+
+/*!
+  Sets the parameter "minimum" of the statistical box plot. This is the position of the lower
+  whisker, typically the minimum measurement of the sample that's not considered an outlier.
+  
+  \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
+*/
+void QCPStatisticalBox::setMinimum(double value)
+{
+  mMinimum = value;
+}
+
+/*!
+  Sets the parameter "lower Quartile" of the statistical box plot. This is the lower end of the
+  box. The lower and the upper quartiles are the two statistical quartiles around the median of the
+  sample, they contain 50% of the sample data.
+  
+  \see setUpperQuartile, setPen, setBrush, setWidth
+*/
+void QCPStatisticalBox::setLowerQuartile(double value)
+{
+  mLowerQuartile = value;
+}
+
+/*!
+  Sets the parameter "median" of the statistical box plot. This is the value of the median mark
+  inside the quartile box. The median separates the sample data in half (50% of the sample data is
+  below/above the median).
+  
+  \see setMedianPen
+*/
+void QCPStatisticalBox::setMedian(double value)
+{
+  mMedian = value;
+}
+
+/*!
+  Sets the parameter "upper Quartile" of the statistical box plot. This is the upper end of the
+  box. The lower and the upper quartiles are the two statistical quartiles around the median of the
+  sample, they contain 50% of the sample data.
+  
+  \see setLowerQuartile, setPen, setBrush, setWidth
+*/
+void QCPStatisticalBox::setUpperQuartile(double value)
+{
+  mUpperQuartile = value;
+}
+
+/*!
+  Sets the parameter "maximum" of the statistical box plot. This is the position of the upper
+  whisker, typically the maximum measurement of the sample that's not considered an outlier.
+  
+  \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
+*/
+void QCPStatisticalBox::setMaximum(double value)
+{
+  mMaximum = value;
+}
+
+/*!
+  Sets a vector of outlier values that will be drawn as circles. Any data points in the sample that
+  are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers and
+  displayed as such.
+  
+  \see setOutlierPen, setOutlierBrush, setOutlierSize
+*/
+void QCPStatisticalBox::setOutliers(const QVector<double> &values)
+{
+  mOutliers = values;
+}
+
+/*!
+  Sets all parameters of the statistical box plot at once.
+  
+  \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile, setMaximum
+*/
+void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
+{
+  setKey(key);
+  setMinimum(minimum);
+  setLowerQuartile(lowerQuartile);
+  setMedian(median);
+  setUpperQuartile(upperQuartile);
+  setMaximum(maximum);
+}
+
+/*!
+  Sets the width of the box in key coordinates.
+  
+  \see setWhiskerWidth
+*/
+void QCPStatisticalBox::setWidth(double width)
+{
+  mWidth = width;
+}
+
+/*!
+  Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key coordinates.
+  
+  \see setWidth
+*/
+void QCPStatisticalBox::setWhiskerWidth(double width)
+{
+  mWhiskerWidth = width;
+}
+
+/*!
+  Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
+  
+  Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the backbone from reaching a few
+  pixels past the bars, when using a non-zero pen width.
+  
+  \see setWhiskerBarPen
+*/
+void QCPStatisticalBox::setWhiskerPen(const QPen &pen)
+{
+  mWhiskerPen = pen;
+}
+
+/*!
+  Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at
+  each end of the backbone).
+  
+  \see setWhiskerPen
+*/
+void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)
+{
+  mWhiskerBarPen = pen;
+}
+
+/*!
+  Sets the pen used for drawing the median indicator line inside the statistical box.
+  
+  Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the median line from reaching a
+  few pixels outside the box, when using a non-zero pen width.
+*/
+void QCPStatisticalBox::setMedianPen(const QPen &pen)
+{
+  mMedianPen = pen;
+}
+
+/*!
+  Sets the pixel size of the scatter symbols that represent the outlier data points.
+  
+  \see setOutlierPen, setOutliers
+*/
+void QCPStatisticalBox::setOutlierSize(double pixels)
+{
+  mOutlierSize = pixels;
+}
+
+/*!
+  Sets the pen used to draw the outlier data points.
+  
+  \see setOutlierSize, setOutliers
+*/
+void QCPStatisticalBox::setOutlierPen(const QPen &pen)
+{
+  mOutlierPen = pen;
+}
+
+/*!
+  Sets the scatter style of the outlier data points.
+  
+  \see setOutlierSize, setOutlierPen, setOutliers
+*/
+void QCPStatisticalBox::setOutlierStyle(QCP::ScatterStyle style)
+{
+  mOutlierStyle = style;
+}
+
+/* inherits documentation from base class */
+void QCPStatisticalBox::clearData()
+{
+  setOutliers(QVector<double>());
+  setKey(0);
+  setMinimum(0);
+  setLowerQuartile(0);
+  setMedian(0);
+  setUpperQuartile(0);
+  setMaximum(0);
+}
+
+/* inherits documentation from base class */
+double QCPStatisticalBox::selectTest(const QPointF &pos) const
+{
+  double posKey, posValue;
+  pixelsToCoords(pos, posKey, posValue);
+  // quartile box:
+  QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
+  QCPRange valueRange(mLowerQuartile, mUpperQuartile);
+  if (keyRange.contains(posKey) && valueRange.contains(posValue))
+    return mParentPlot->selectionTolerance()*0.99;
+  
+  // min/max whiskers:
+  if (QCPRange(mMinimum, mMaximum).contains(posValue))
+    return qAbs(mKeyAxis->coordToPixel(mKey)-mKeyAxis->coordToPixel(posKey));
+  
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPStatisticalBox::draw(QCPPainter *painter)
+{
+  QRectF quartileBox;
+  drawQuartileBox(painter, &quartileBox);
+  
+  painter->save();
+  painter->setClipRect(quartileBox, Qt::IntersectClip);
+  drawMedian(painter);
+  painter->restore();
+  
+  drawWhiskers(painter);
+  drawOutliers(painter);
+}
+
+/* inherits documentation from base class */
+void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRect &rect) const
+{
+  // draw filled rect:
+  applyDefaultAntialiasingHint(painter);
+  painter->setPen(mPen);
+  painter->setBrush(mBrush);
+  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
+  r.moveCenter(rect.center());
+  painter->drawRect(r);
+}
+
+/*! \internal
+  
+  Draws the quartile box. \a box is an output parameter that returns the quartile box (in pixel
+  coordinates) which is used to set the clip rect of the painter before calling \ref drawMedian (so
+  the median doesn't draw outside the quartile box).
+*/
+void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
+{
+  QRectF box;
+  box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
+  box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
+  applyDefaultAntialiasingHint(painter);
+  painter->setPen(mainPen());
+  painter->setBrush(mainBrush());
+  painter->drawRect(box);
+  if (quartileBox)
+    *quartileBox = box;
+}
+
+/*! \internal
+  
+  Draws the median line inside the quartile box.
+*/
+void QCPStatisticalBox::drawMedian(QCPPainter *painter) const
+{
+  QLineF medianLine;
+  medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
+  medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
+  applyDefaultAntialiasingHint(painter);
+  painter->setPen(mMedianPen);
+  painter->drawLine(medianLine);
+}
+
+/*! \internal
+  
+  Draws both whisker backbones and bars.
+*/
+void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const
+{
+  QLineF backboneMin, backboneMax, barMin, barMax;
+  backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
+  backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
+  barMax.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMaximum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMaximum));
+  barMin.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMinimum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMinimum));
+  applyErrorBarsAntialiasingHint(painter);
+  painter->setPen(mWhiskerPen);
+  painter->drawLine(backboneMin);
+  painter->drawLine(backboneMax);
+  painter->setPen(mWhiskerBarPen);
+  painter->drawLine(barMin);
+  painter->drawLine(barMax);
+}
+
+/*! \internal
+  
+  Draws the outlier circles.
+*/
+void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const
+{
+  applyScattersAntialiasingHint(painter);
+  painter->setPen(mOutlierPen);
+  painter->setBrush(Qt::NoBrush);
+  for (int i=0; i<mOutliers.size(); ++i)
+  {
+    QPointF dataPoint = coordsToPixels(mKey, mOutliers.at(i));
+    painter->drawScatter(dataPoint.x(), dataPoint.y(), mOutlierSize, mOutlierStyle);
+  }
+}
+
+/* inherits documentation from base class */
+QCPRange QCPStatisticalBox::getKeyRange(bool &validRange, SignDomain inSignDomain) const
+{
+  validRange = mWidth > 0;
+  if (inSignDomain == sdBoth)
+  {
+    return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
+  } else if (inSignDomain == sdNegative)
+  {
+    if (mKey+mWidth*0.5 < 0)
+      return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
+    else if (mKey < 0)
+      return QCPRange(mKey-mWidth*0.5, mKey);
+    else
+    {
+      validRange = false;
+      return QCPRange();
+    }
+  } else if (inSignDomain == sdPositive)
+  {
+    if (mKey-mWidth*0.5 > 0)
+      return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
+    else if (mKey > 0)
+      return QCPRange(mKey, mKey+mWidth*0.5);
+    else
+    {
+      validRange = false;
+      return QCPRange();
+    }
+  }
+  validRange = false;
+  return QCPRange();
+}
+
+/* inherits documentation from base class */
+QCPRange QCPStatisticalBox::getValueRange(bool &validRange, SignDomain inSignDomain) const
+{
+  if (inSignDomain == sdBoth)
+  {
+    double lower = qMin(mMinimum, qMin(mMedian, mLowerQuartile));
+    double upper = qMax(mMaximum, qMax(mMedian, mUpperQuartile));
+    for (int i=0; i<mOutliers.size(); ++i)
+    {
+      if (mOutliers.at(i) < lower)
+        lower = mOutliers.at(i);
+      if (mOutliers.at(i) > upper)
+        upper = mOutliers.at(i);
+    }
+    validRange = upper > lower;
+    return QCPRange(lower, upper);
+  } else
+  {
+    QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
+    values.reserve(mOutliers.size() + 5);
+    values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
+    values << mOutliers;
+    // go through values and find the ones in legal range:
+    bool haveUpper = false;
+    bool haveLower = false;
+    double upper = 0;
+    double lower = 0;
+    for (int i=0; i<values.size(); ++i)
+    {
+      if ((inSignDomain == sdNegative && values.at(i) < 0) ||
+          (inSignDomain == sdPositive && values.at(i) > 0))
+      {
+        if (values.at(i) > upper || !haveUpper)
+        {
+          upper = values.at(i);
+          haveUpper = true;
+        }
+        if (values.at(i) < lower || !haveLower)
+        {
+          lower = values.at(i);
+          haveLower = true;
+        }
+      }
+    }
+    // return the bounds if we found some sensible values:
+    if (haveLower && haveUpper && lower < upper)
+    {
+      validRange = true;
+      return QCPRange(lower, upper);
+    } else
+    {
+      validRange = false;
+      return QCPRange();
+    }
+  }
+}
+
+
+// ================================================================================
+// =================== QCPAbstractItem
+// ================================================================================
+
+/*! \class QCPAbstractItem
+  \brief The abstract base class for all items in a plot.
+  
+  In QCustomPlot, items are supplemental graphical elements that are neither plottables
+  (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus
+  plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each
+  specific item has at least one QCPItemPosition member which controls the positioning. Some items
+  are defined by more than one coordinate and thus have two or more QCPItemPosition members (For
+  example, QCPItemRect has \a topLeft and \a bottomRight).
+  
+  This abstract base class defines a very basic interface like visibility and clipping. Since this
+  class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass
+  yourself to create new items.
+  
+  The built-in items are:
+  <table>
+  <tr><td>QCPItemLine</td><td>A line defined by a start and an end point. May have different ending styles on each side (e.g. arrows).</td></tr>
+  <tr><td>QCPItemStraightLine</td><td>A straight line defined by a start and a direction point. Unlike QCPItemLine, the straight line is infinitely long and has no endings.</td></tr>
+  <tr><td>QCPItemCurve</td><td>A curve defined by start, end and two intermediate control points. May have different ending styles on each side (e.g. arrows).</td></tr>
+  <tr><td>QCPItemRect</td><td>A rectangle</td></tr>
+  <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>
+  <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>
+  <tr><td>QCPItemText</td><td>A text label</td></tr>
+  <tr><td>QCPItemBracket</td><td>A bracket which may be used to reference/highlight certain parts in the plot.</td></tr>
+  <tr><td>QCPItemTracer</td><td>An item that can be attached to a QCPGraph and sticks to its data points, given a key coordinate.</td></tr>
+  </table>
+  
+  \section items-using Using items
+  
+  First you instantiate the item you want to use and add it to the plot:
+  \code
+  QCPItemLine *line = new QCPItemLine(customPlot);
+  customPlot->addItem(line);
+  \endcode
+  by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just
+  set the plot coordinates where the line should start/end:
+  \code
+  line->start->setCoords(-0.1, 0.8);
+  line->end->setCoords(1.1, 0.2);
+  \endcode
+  If we wanted the line to be positioned not in plot coordinates but a different coordinate system,
+  e.g. absolute pixel positions on the QCustomPlot surface, we would have changed the position type
+  like this:
+  \code
+  line->start->setType(QCPItemPosition::ptAbsolute);
+  line->end->setType(QCPItemPosition::ptAbsolute);
+  \endcode
+  Then we can set the coordinates, this time in pixels:
+  \code
+  line->start->setCoords(100, 200);
+  line->end->setCoords(450, 320);
+  \endcode
+  
+  \section items-subclassing Creating own items
+  
+  To create an own item, you implement a subclass of QCPAbstractItem. These are the pure
+  virtual functions, you must implement:
+  \li \ref selectTest
+  \li \ref draw
+  
+  See the documentation of those functions for what they need to do.
+  
+  \subsection items-positioning Allowing the item to be positioned
+  
+  As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall
+  have only one coordinate as its position (as opposed to two like a rect or multiple like a polygon). You then add
+  a public member of type QCPItemPosition like so:
+  
+  \code QCPItemPosition * const myPosition;\endcode
+  
+  the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition
+  instance it points to, can be modified, of course).
+  The initialization of this pointer is made easy with the \ref createPosition function. Just assign
+  the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition
+  takes a string which is the name of the position, typically this is identical to the variable name.
+  For example, the constructor of QCPItemExample could look like this:
+  
+  \code
+  QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :
+    QCPAbstractItem(parentPlot),
+    myPosition(createPosition("myPosition"))
+  {
+    // other constructor code
+  }
+  \endcode
+  
+  \subsection items-drawing The draw function
+  
+  Your implementation of the draw function should check whether the item is visible (\a mVisible)
+  and then draw the item. You can retrieve its position in pixel coordinates from the position
+  member(s) via \ref QCPItemPosition::pixelPoint.
+
+  To optimize performance you should calculate a bounding rect first (don't forget to take the pen
+  width into account), check whether it intersects the \ref clipRect, and only draw the item at all
+  if this is the case.
+  
+  \subsection items-selection The selectTest function
+  
+  Your implementation of the \ref selectTest function may use the helpers \ref distSqrToLine and
+  \ref rectSelectTest. With these, the implementation of the selection test becomes significantly
+  simpler for most items.
+  
+  \subsection anchors Providing anchors
+  
+  Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public
+  member, e.g.
+  
+  \code QCPItemAnchor * const bottom;\endcode
+
+  and create it in the constructor with the \ref createAnchor function, assigning it a name and an
+  anchor id (an integer enumerating all anchors on the item, you may create an own enum for this).
+  Since anchors can be placed anywhere, relative to the item's position(s), your item needs to
+  provide the position of every anchor with the reimplementation of the \ref anchorPixelPoint(int
+  anchorId) function.
+  
+  In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel
+  position when anything attached to the anchor needs to know the coordinates.
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QList<QCPItemPosition*> QCPAbstractItem::positions() const
+  
+  Returns all positions of the item in a list.
+  
+  \see anchors, position
+*/
+
+/*! \fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const
+  
+  Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always
+  also an anchor, the list will also contain the positions of this item.
+  
+  \see positions, anchor
+*/
+
+/* end of documentation of inline functions */
+/* start documentation of pure virtual functions */
+
+/*! \fn double QCPAbstractItem::selectTest(const QPointF &pos) const = 0
+  
+  This function is used to decide whether a click hits an item or not.
+
+  \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
+  shortest pixel distance of this point to the item. If the item is either invisible or the
+  distance couldn't be determined, -1.0 is returned. \ref setSelectable has no influence on the
+  return value of this function.
+
+  If the item is represented not by single lines but by an area like QCPItemRect or QCPItemText, a
+  click inside the area returns a constant value greater zero (typically 99% of the
+  selectionTolerance of the parent QCustomPlot). If the click lies outside the area, this function
+  returns -1.0.
+  
+  Providing a constant value for area objects allows selecting line objects even when they are
+  obscured by such area objects, by clicking close to the lines (i.e. closer than
+  0.99*selectionTolerance).
+  
+  The actual setting of the selection state is not done by this function. This is handled by the
+  parent QCustomPlot when the mouseReleaseEvent occurs.
+  
+  \see setSelected, QCustomPlot::setInteractions
+*/
+
+/*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0
+  \internal
+  
+  Draws this item with the provided \a painter. Called by \ref QCustomPlot::draw on all its
+  visible items.
+  
+  The cliprect of the provided painter is set to the rect returned by \ref clipRect before this
+  function is called. For items this depends on the clipping settings defined by \ref
+  setClipToAxisRect, \ref setClipKeyAxis and \ref setClipValueAxis.
+*/
+
+/* end documentation of pure virtual functions */
+/* start documentation of signals */
+
+/*! \fn void QCPAbstractItem::selectionChanged(bool selected)
+  This signal is emitted when the selection state of this item has changed, either by user interaction
+  or by a direct call to \ref setSelected.
+*/
+
+/* end documentation of signals */
+
+/*!
+  Base class constructor which initializes base class members.
+*/
+QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
+  QCPLayerable(parentPlot),
+  mClipToAxisRect(true),
+  mClipKeyAxis(parentPlot->xAxis),
+  mClipValueAxis(parentPlot->yAxis),
+  mSelectable(true),
+  mSelected(false)
+{
+}
+
+QCPAbstractItem::~QCPAbstractItem()
+{
+  // don't delete mPositions because every position is also an anchor and thus in mAnchors
+  qDeleteAll(mAnchors);
+}
+
+/*!
+  Sets whether the item shall be clipped to the axis rect or whether it shall be visible on the
+  entire QCustomPlot. The axis rect is defined by the clip axes which can be set via \ref
+  setClipAxes or individually with \ref setClipKeyAxis and \ref setClipValueAxis.
+*/
+void QCPAbstractItem::setClipToAxisRect(bool clip)
+{
+  mClipToAxisRect = clip;
+}
+
+/*!
+  Sets both clip axes. Together they define the axis rect that will be used to clip the item
+  when \ref setClipToAxisRect is set to true.
+  
+  \see setClipToAxisRect, setClipKeyAxis, setClipValueAxis
+*/
+void QCPAbstractItem::setClipAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
+{
+  mClipKeyAxis = keyAxis;
+  mClipValueAxis = valueAxis;
+}
+
+/*!
+  Sets the clip key axis. Together with the clip value axis it defines the axis rect that will be
+  used to clip the item when \ref setClipToAxisRect is set to true.
+  
+  \see setClipToAxisRect, setClipAxes, setClipValueAxis
+*/
+void QCPAbstractItem::setClipKeyAxis(QCPAxis *axis)
+{
+  mClipKeyAxis = axis;
+}
+
+/*!
+  Sets the clip value axis. Together with the clip key axis it defines the axis rect that will be
+  used to clip the item when \ref setClipToAxisRect is set to true.
+  
+  \see setClipToAxisRect, setClipAxes, setClipKeyAxis
+*/
+void QCPAbstractItem::setClipValueAxis(QCPAxis *axis)
+{
+  mClipValueAxis = axis;
+}
+
+/*!
+  Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.)
+  
+  However, even when \a selectable was set to false, it is possible to set the selection manually,
+  by calling \ref setSelected directly.
+  
+  \see QCustomPlot::setInteractions, setSelected
+*/
+void QCPAbstractItem::setSelectable(bool selectable)
+{
+  mSelectable = selectable;
+}
+
+/*!
+  Sets whether this item is selected or not. When selected, it might use a different visual
+  appearance (e.g. pen and brush), this depends on the specific item, though.
+
+  The entire selection mechanism for items is handled automatically when \ref
+  QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this function when you
+  wish to change the selection state manually.
+  
+  This function can change the selection state even when \ref setSelectable was set to false.
+  
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+  
+  \see selectTest
+*/
+void QCPAbstractItem::setSelected(bool selected)
+{
+  if (mSelected != selected)
+  {
+    mSelected = selected;
+    emit selectionChanged(mSelected);
+  }
+}
+
+/*!
+  Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by
+  that name, returns 0.
+  
+  This function provides an alternative way to access item positions. Normally, you access
+  positions direcly by their member pointers (which typically have the same variable name as \a
+  name).
+  
+  \see positions, anchor 
+*/
+QCPItemPosition *QCPAbstractItem::position(const QString &name) const
+{
+  for (int i=0; i<mPositions.size(); ++i)
+  {
+    if (mPositions.at(i)->name() == name)
+      return mPositions.at(i);
+  }
+  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
+  return 0;
+}
+
+/*!
+  Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by
+  that name, returns 0.
+  
+  This function provides an alternative way to access item anchors. Normally, you access
+  anchors direcly by their member pointers (which typically have the same variable name as \a
+  name).
+  
+  \see anchors, position 
+*/
+QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
+{
+  for (int i=0; i<mAnchors.size(); ++i)
+  {
+    if (mAnchors.at(i)->name() == name)
+      return mAnchors.at(i);
+  }
+  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
+  return 0;
+}
+
+/*!
+  Returns whether this item has an anchor with the specified \a name.
+  
+  Note that you can check for positions with this function, too, because every position is also an
+  anchor (QCPItemPosition inherits from QCPItemAnchor).
+  
+  \see anchor, position 
+*/
+bool QCPAbstractItem::hasAnchor(const QString &name) const
+{
+  for (int i=0; i<mAnchors.size(); ++i)
+  {
+    if (mAnchors.at(i)->name() == name)
+      return true;
+  }
+  return false;
+}
+
+/*! \internal
+  
+  Returns the rect the visual representation of this item is clipped to. This depends on the
+  current setting of \ref setClipToAxisRect aswell as the clip axes set with \ref setClipAxes.
+  
+  If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport rect is returned.
+  
+  \see draw
+*/
+QRect QCPAbstractItem::clipRect() const
+{
+  if (mClipToAxisRect)
+  {
+    if (mClipKeyAxis && mClipValueAxis)
+      return mClipKeyAxis->axisRect() | mClipValueAxis->axisRect();
+    else if (mClipKeyAxis)
+      return mClipKeyAxis->axisRect();
+    else if (mClipValueAxis)
+      return mClipValueAxis->axisRect();
+  }
+  
+  return mParentPlot->viewport();
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing item lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+  
+  This function takes into account the local setting of the antialiasing flag as well as
+  the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased
+*/
+void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
+}
+
+/*! \internal
+
+  Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
+  end.
+  
+  This function may be used to help with the implementation of the \ref selectTest function for
+  specific items.
+  
+  \note This function is identical to QCPAbstractPlottable::distSqrToLine
+  
+  \see rectSelectTest
+*/
+double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
+{
+  QVector2D a(start);
+  QVector2D b(end);
+  QVector2D p(point);
+  QVector2D v(b-a);
+  
+  double vLengthSqr = v.lengthSquared();
+  if (!qFuzzyIsNull(vLengthSqr))
+  {
+    double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
+    if (mu < 0)
+      return (a-p).lengthSquared();
+    else if (mu > 1)
+      return (b-p).lengthSquared();
+    else
+      return ((a + mu*v)-p).lengthSquared();
+  } else
+    return (a-p).lengthSquared();
+}
+
+/*! \internal
+
+  A convenience function which returns the selectTest value for a specified \a rect and a specified
+  click position \a pos. \a filledRect defines whether a click inside the rect should also be
+  considered a hit or whether only the rect border is sensitive to hits.
+  
+  This function may be used to help with the implementation of the \ref selectTest function for
+  specific items.
+  
+  For example, if your item consists of four rects, call this function four times, once for each
+  rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned
+  values which were greater or equal to zero. (Because this function may return -1.0 when \a pos
+  doesn't hit \a rect at all). If all calls returned -1.0, return -1.0, too, because your item
+  wasn't hit.
+  
+  \see distSqrToLine
+*/
+double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
+{
+  double result = -1;
+
+  // distance to border:
+  QList<QLineF> lines;
+  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
+        << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
+  double minDistSqr = std::numeric_limits<double>::max();
+  for (int i=0; i<lines.size(); ++i)
+  {
+    double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
+    if (distSqr < minDistSqr)
+      minDistSqr = distSqr;
+  }
+  result = qSqrt(minDistSqr);
+  
+  // filled rect, allow click inside to count as hit:
+  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
+  {
+    if (rect.contains(pos))
+      result = mParentPlot->selectionTolerance()*0.99;
+  }
+  return result;
+}
+
+/*! \internal
+
+  Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in
+  item subclasses if they want to provide anchors (QCPItemAnchor).
+  
+  For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor
+  ids and returns the respective pixel points of the specified anchor.
+  
+  \see createAnchor
+*/
+QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
+{
+  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (anchorPixelPos not reimplemented). anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified
+  \a name must be a unique string that is usually identical to the variable name of the position
+  member (This is needed to provide the name based \ref position access to positions).
+  
+  Don't delete positions created by this function manually, as the item will take care of it.
+  
+  Use this function in the constructor (initialization list) of the specific item subclass to
+  create each position member. Don't create QCPItemPositions with \b new yourself, because they
+  won't be registered with the item properly.
+  
+  \see createAnchor
+*/
+QCPItemPosition *QCPAbstractItem::createPosition(const QString &name)
+{
+  if (hasAnchor(name))
+    qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
+  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
+  mPositions.append(newPosition);
+  mAnchors.append(newPosition); // every position is also an anchor
+  newPosition->setType(QCPItemPosition::ptPlotCoords);
+  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
+  newPosition->setCoords(0, 0);
+  return newPosition;
+}
+
+/*! \internal
+
+  Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified
+  \a name must be a unique string that is usually identical to the variable name of the anchor
+  member (This is needed to provide the name based \ref anchor access to anchors).
+  
+  The \a anchorId must be a number identifying the created anchor. It is recommended to create an
+  enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor
+  to identify itself when it calls QCPAbstractItem::anchorPixelPoint. That function then returns
+  the correct pixel coordinates for the passed anchor id.
+  
+  Don't delete anchors created by this function manually, as the item will take care of it.
+  
+  Use this function in the constructor (initialization list) of the specific item subclass to
+  create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they
+  won't be registered with the item properly.
+  
+  \see createPosition
+*/
+QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
+{
+  if (hasAnchor(name))
+    qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
+  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
+  mAnchors.append(newAnchor);
+  return newAnchor;
+}
+
+
+// ================================================================================
+// =================== QCPItemPosition
+// ================================================================================
+
+/*! \class QCPItemPosition
+  \brief Manages the position of an item.
+  
+  Every item has at least one public QCPItemPosition member pointer which provides ways to position the
+  item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two:
+  \a topLeft and \a bottomRight.
+
+  QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type defines
+  how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel coordinates, as
+  plot coordinates of certain axes, etc.
+
+  Further, QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. (Note that every
+  QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent anchor for other
+  positions.) This way you can tie multiple items together. If the QCPItemPosition has a parent, the
+  coordinates set with \ref setCoords are considered to be absolute values in the reference frame of the
+  parent anchor, where (0, 0) means directly ontop of the parent anchor. For example, You could attach
+  the \a start position of a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting
+  point of the line always be centered under the text label, no matter where the text is moved to, or is
+  itself tied to.
+
+  To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPoint. This
+  works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref
+  setPixelPoint transforms the coordinates appropriately, to make the position appear at the specified
+  pixel values.
+*/
+
+/*!
+  Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if
+  you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as
+  explained in the subclassing section of the QCPAbstractItem documentation.
+*/
+QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
+  QCPItemAnchor(parentPlot, parentItem, name),
+  mPositionType(ptAbsolute),
+  mKeyAxis(0),
+  mValueAxis(0),
+  mKey(0),
+  mValue(0),
+  mParentAnchor(0)
+{
+}
+
+QCPItemPosition::~QCPItemPosition()
+{
+  // unregister as parent at children:
+  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
+  //       the setParentAnchor(0) call the correct QCPItemPosition::pixelPos function instead of QCPItemAnchor::pixelPos
+  QList<QCPItemPosition*> currentChildren(mChildren.toList());
+  for (int i=0; i<currentChildren.size(); ++i)
+    currentChildren.at(i)->setParentAnchor(0); // this acts back on this anchor and child removes itself from mChildren
+  // unregister as child in parent:
+  if (mParentAnchor)
+    mParentAnchor->removeChild(this);
+}
+
+/*!
+  Sets the type of the position. The type defines how the coordinates passed to \ref setCoords
+  should be handled and how the QCPItemPosition should behave in the plot. Note that the position
+  type \ref ptPlotCoords is only available (and sensible) when the position has no parent anchor
+  (\ref setParentAnchor).
+  
+  The possible values for \a type can be separated in two main categories:
+
+  \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords
+  and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes.
+  By default, the QCustomPlot's x- and yAxis are used.
+  
+  \li The position is fixed on the QCustomPlot surface, i.e. independant of axis ranges. This
+  corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref ptAxisRectRatio. They
+  differ only in the way the absolute position is described, see the documentation of PositionType
+  for details.
+  
+  \note If the type is changed, the apparent pixel position on the plot is preserved. This means
+  the coordinates as retrieved with coords() and set with \ref setCoords may change in the process.
+*/
+void QCPItemPosition::setType(QCPItemPosition::PositionType type)
+{
+  if (mPositionType != type)
+  {
+    QPointF pixelP = pixelPoint();
+    mPositionType = type;
+    setPixelPoint(pixelP);
+  }
+}
+
+/*!
+  Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now
+  follow any position changes of the anchor. The local coordinate system of positions with a parent
+  anchor always is absolute with (0, 0) being exactly on top of the parent anchor. (Hence the type
+  shouldn't be \ref ptPlotCoords for positions with parent anchors.)
+  
+  if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved
+  during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position
+  will be exactly on top of the parent anchor.
+  
+  To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0.
+  
+  \note If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type
+  is set to \ref ptAbsolute, to keep the position in a valid state.
+*/
+bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
+{
+  // make sure self is not assigned as parent:
+  if (parentAnchor == this)
+  {
+    qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
+    return false;
+  }
+  // make sure no recursive parent-child-relationships are created:
+  QCPItemAnchor *currentParent = parentAnchor;
+  while (currentParent)
+  {
+    if (QCPItemPosition *currentParentPos = dynamic_cast<QCPItemPosition*>(currentParent))
+    {
+      // is a QCPItemPosition, might have further parent, so keep iterating
+      if (currentParentPos == this)
+      {
+        qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
+        return false;
+      }
+      currentParent = currentParentPos->mParentAnchor;
+    } else
+    {
+      // is a QCPItemAnchor, can't have further parent, so just compare parent items
+      if (currentParent->mParentItem == mParentItem)
+      {
+        qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
+        return false;
+      }
+      break;
+    }
+  }
+  
+  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
+  if (!mParentAnchor && mPositionType == ptPlotCoords)
+    setType(ptAbsolute);
+  
+  // save pixel position:
+  QPointF pixelP;
+  if (keepPixelPosition)
+    pixelP = pixelPoint();
+  // unregister at current parent anchor:
+  if (mParentAnchor)
+    mParentAnchor->removeChild(this);
+  // register at new parent anchor:
+  if (parentAnchor)
+    parentAnchor->addChild(this);
+  mParentAnchor = parentAnchor;
+  // restore pixel position under new parent:
+  if (keepPixelPosition)
+    setPixelPoint(pixelP);
+  else
+    setCoords(0, 0);
+  return true;
+}
+
+/*!
+  Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type
+  (\ref setType).
+  
+  For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position
+  on the QCustomPlot surface where the origin (0, 0) is in the top left corner of the QCustomPlot
+  viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the plot
+  coordinate system defined by the axes set by \ref setAxes. (By default the QCustomPlot's x- and
+  yAxis.)
+
+  \see setPixelPoint
+*/
+void QCPItemPosition::setCoords(double key, double value)
+{
+  mKey = key;
+  mValue = value;
+}
+
+/*! \overload
+
+  Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the
+  meaning of \a value of the \ref setCoords(double key, double value) function.
+*/
+void QCPItemPosition::setCoords(const QPointF &pos)
+{
+  setCoords(pos.x(), pos.y());
+}
+
+/*!
+  Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It
+  includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor).
+
+  \see setPixelPoint
+*/
+QPointF QCPItemPosition::pixelPoint() const
+{
+  switch (mPositionType)
+  {
+    case ptAbsolute:
+    {
+      if (mParentAnchor)
+        return QPointF(mKey, mValue) + mParentAnchor->pixelPoint();
+      else
+        return QPointF(mKey, mValue);
+    }
+    
+    case ptViewportRatio:
+    {
+      if (mParentAnchor)
+      {
+        return QPointF(mKey*mParentPlot->viewport().width(),
+                       mValue*mParentPlot->viewport().height()) + mParentAnchor->pixelPoint();
+      } else
+      {
+        return QPointF(mKey*mParentPlot->viewport().width(),
+                       mValue*mParentPlot->viewport().height()) + mParentPlot->viewport().topLeft();
+      }
+    }
+      
+    case ptAxisRectRatio:
+    {
+      if (mParentAnchor)
+      {
+        return QPointF(mKey*mParentPlot->axisRect().width(),
+                       mValue*mParentPlot->axisRect().height()) + mParentAnchor->pixelPoint();
+      } else
+      {
+        return QPointF(mKey*mParentPlot->axisRect().width(),
+                       mValue*mParentPlot->axisRect().height()) + mParentPlot->axisRect().topLeft();
+      }
+    }
+    
+    case ptPlotCoords:
+    {
+      double x, y;
+      if (mKeyAxis && mValueAxis)
+      {
+        // both key and value axis are given, translate key/value to x/y coordinates:
+        if (mKeyAxis->orientation() == Qt::Horizontal)
+        {
+          x = mKeyAxis->coordToPixel(mKey);
+          y = mValueAxis->coordToPixel(mValue);
+        } else
+        {
+          y = mKeyAxis->coordToPixel(mKey);
+          x = mValueAxis->coordToPixel(mValue);
+        }
+      } else if (mKeyAxis)
+      {
+        // only key axis is given, depending on orientation only transform x or y to key coordinate, other stays pixel:
+        if (mKeyAxis->orientation() == Qt::Horizontal)
+        {
+          x = mKeyAxis->coordToPixel(mKey);
+          y = mValue;
+        } else
+        {
+          y = mKeyAxis->coordToPixel(mKey);
+          x = mValue;
+        }
+      } else if (mValueAxis)
+      {
+        // only value axis is given, depending on orientation only transform x or y to value coordinate, other stays pixel:
+        if (mValueAxis->orientation() == Qt::Horizontal)
+        {
+          x = mValueAxis->coordToPixel(mValue);
+          y = mKey;
+        } else
+        {
+          y = mValueAxis->coordToPixel(mValue);
+          x = mKey;
+        }
+      } else
+      {
+        // no axis given, basically the same as if mAnchorType were atNone
+        x = mKey;
+        y = mValue;
+      }
+      return QPointF(x, y);
+    }
+  }
+  return QPointF();
+}
+
+/*!
+  When \ref setType is ptPlotCoords, this function may be used to specify the axes the coordinates set
+  with \ref setCoords relate to.
+*/
+void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
+{
+  mKeyAxis = keyAxis;
+  mValueAxis = valueAxis;
+}
+
+/*!
+  Sets the apparent pixel position. This works no matter what type this QCPItemPosition is or what
+  parent-child situation it is in, as \ref setPixelPoint transforms the coordinates appropriately, to
+  make the position appear at the specified pixel values.
+
+  Only if the type is \ref ptAbsolute and no parent anchor is set, this function is identical to \ref
+  setCoords.
+
+  \see setCoords
+*/
+void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
+{
+  switch (mPositionType)
+  {
+    case ptAbsolute:
+    {
+      if (mParentAnchor)
+        setCoords(pixelPoint-mParentAnchor->pixelPoint());
+      else
+        setCoords(pixelPoint);
+      break;
+    }
+      
+    case ptViewportRatio:
+    {
+      if (mParentAnchor)
+      {
+        QPointF p(pixelPoint-mParentAnchor->pixelPoint());
+        p.rx() /= (double)mParentPlot->viewport().width();
+        p.ry() /= (double)mParentPlot->viewport().height();
+        setCoords(p);
+      } else
+      {
+        QPointF p(pixelPoint-mParentPlot->viewport().topLeft());
+        p.rx() /= (double)mParentPlot->viewport().width();
+        p.ry() /= (double)mParentPlot->viewport().height();
+        setCoords(p);
+      }
+      break;
+    }
+      
+    case ptAxisRectRatio:
+    {
+      if (mParentAnchor)
+      {
+        QPointF p(pixelPoint-mParentAnchor->pixelPoint());
+        p.rx() /= (double)mParentPlot->axisRect().width();
+        p.ry() /= (double)mParentPlot->axisRect().height();
+        setCoords(p);
+      } else
+      {
+        QPointF p(pixelPoint-mParentPlot->axisRect().topLeft());
+        p.rx() /= (double)mParentPlot->axisRect().width();
+        p.ry() /= (double)mParentPlot->axisRect().height();
+        setCoords(p);
+      }
+      break;
+    }
+      
+    case ptPlotCoords:
+    {
+      double newKey, newValue;
+      if (mKeyAxis && mValueAxis)
+      {
+        // both key and value axis are given, translate point to key/value coordinates:
+        if (mKeyAxis->orientation() == Qt::Horizontal)
+        {
+          newKey = mKeyAxis->pixelToCoord(pixelPoint.x());
+          newValue = mValueAxis->pixelToCoord(pixelPoint.y());
+        } else
+        {
+          newKey = mKeyAxis->pixelToCoord(pixelPoint.y());
+          newValue = mValueAxis->pixelToCoord(pixelPoint.x());
+        }
+      } else if (mKeyAxis)
+      {
+        // only key axis is given, depending on orientation only transform x or y to key coordinate, other stays pixel:
+        if (mKeyAxis->orientation() == Qt::Horizontal)
+        {
+          newKey = mKeyAxis->pixelToCoord(pixelPoint.x());
+          newValue = pixelPoint.y();
+        } else
+        {
+          newKey = mKeyAxis->pixelToCoord(pixelPoint.y());
+          newValue = pixelPoint.x();
+        }
+      } else if (mValueAxis)
+      {
+        // only value axis is given, depending on orientation only transform x or y to value coordinate, other stays pixel:
+        if (mValueAxis->orientation() == Qt::Horizontal)
+        {
+          newKey = pixelPoint.y();
+          newValue = mValueAxis->pixelToCoord(pixelPoint.x());
+        } else
+        {
+          newKey = pixelPoint.x();
+          newValue = mValueAxis->pixelToCoord(pixelPoint.y());
+        }
+      } else
+      {
+        // no axis given, basically the same as if mAnchorType were atNone
+        newKey = pixelPoint.x();
+        newValue = pixelPoint.y();
+      }
+      setCoords(newKey, newValue);
+      break;
+    }
+  }
+}
+
+
+// ================================================================================
+// =================== QCPItemStraightLine
+// ================================================================================
+
+/*! \class QCPItemStraightLine
+  \brief A straight line that spans infinitely in both directions
+
+  \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a point1 and \a point2, which define the straight line.
+*/
+
+/*!
+  Creates a straight line item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  point1(createPosition("point1")),
+  point2(createPosition("point2"))
+{
+  point1->setCoords(0, 0);
+  point2->setCoords(1, 1);
+  
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue,2));
+}
+
+QCPItemStraightLine::~QCPItemStraightLine()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line
+  
+  \see setSelectedPen
+*/
+void QCPItemStraightLine::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemStraightLine::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/* inherits documentation from base class */
+double QCPItemStraightLine::selectTest(const QPointF &pos) const
+{
+  if (!mVisible)
+    return -1;
+  
+  return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
+}
+
+/* inherits documentation from base class */
+void QCPItemStraightLine::draw(QCPPainter *painter)
+{
+  QVector2D start(point1->pixelPoint());
+  QVector2D end(point2->pixelPoint());
+  // get visible segment of straight line inside clipRect:
+  double clipPad = mainPen().widthF();
+  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
+  // paint visible segment, if existent:
+  if (!line.isNull())
+  {
+    painter->setPen(mainPen());
+    painter->drawLine(line);
+  }
+}
+
+/*! \internal
+
+  finds the shortest distance of \a point to the straight line defined by the base point \a
+  base and the direction vector \a vec.
+  
+  This is a helper function for \ref selectTest.
+*/
+double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
+{
+  return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
+}
+
+/*! \internal
+
+  Returns the section of the straight line defined by \a base and direction vector \a
+  vec, that is visible in the specified \a rect.
+  
+  This is a helper function for \ref draw.
+*/
+QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
+{
+  double bx, by;
+  double gamma;
+  QLineF result;
+  if (vec.x() == 0 && vec.y() == 0)
+    return result;
+  if (qFuzzyIsNull(vec.x())) // line is vertical
+  {
+    // check top of rect:
+    bx = rect.left();
+    by = rect.top();
+    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
+    if (gamma >= 0 && gamma <= rect.width())
+      result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
+  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
+  {
+    // check left of rect:
+    bx = rect.left();
+    by = rect.top();
+    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
+    if (gamma >= 0 && gamma <= rect.height())
+      result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
+  } else // line is skewed
+  {
+    QList<QVector2D> pointVectors;
+    // check top of rect:
+    bx = rect.left();
+    by = rect.top();
+    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
+    if (gamma >= 0 && gamma <= rect.width())
+      pointVectors.append(QVector2D(bx+gamma, by));
+    // check bottom of rect:
+    bx = rect.left();
+    by = rect.bottom();
+    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
+    if (gamma >= 0 && gamma <= rect.width())
+      pointVectors.append(QVector2D(bx+gamma, by));
+    // check left of rect:
+    bx = rect.left();
+    by = rect.top();
+    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
+    if (gamma >= 0 && gamma <= rect.height())
+      pointVectors.append(QVector2D(bx, by+gamma));
+    // check right of rect:
+    bx = rect.right();
+    by = rect.top();
+    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
+    if (gamma >= 0 && gamma <= rect.height())
+      pointVectors.append(QVector2D(bx, by+gamma));
+    
+    // evaluate points:
+    if (pointVectors.size() == 2)
+    {
+      result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
+    } else if (pointVectors.size() > 2)
+    {
+      // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
+      double distSqrMax = 0;
+      QVector2D pv1, pv2;
+      for (int i=0; i<pointVectors.size()-1; ++i)
+      {
+        for (int k=i+1; k<pointVectors.size(); ++k)
+        {
+          double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
+          if (distSqr > distSqrMax)
+          {
+            pv1 = pointVectors.at(i);
+            pv2 = pointVectors.at(k);
+            distSqrMax = distSqr;
+          }
+        }
+      }
+      result.setPoints(pv1.toPointF(), pv2.toPointF());
+    }
+  }
+  return result;
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemStraightLine::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+
+// ================================================================================
+// =================== QCPItemLine
+// ================================================================================
+
+/*! \class QCPItemLine
+  \brief A line from one point to another
+
+  \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a start and \a end, which define the end points of the line.
+  
+  With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow.
+*/
+
+/*!
+  Creates a line item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  start(createPosition("start")),
+  end(createPosition("end"))
+{
+  start->setCoords(0, 0);
+  end->setCoords(1, 1);
+  
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue,2));
+}
+
+QCPItemLine::~QCPItemLine()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line
+  
+  \see setSelectedPen
+*/
+void QCPItemLine::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemLine::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the line ending style of the head. The head corresponds to the \a end position.
+  
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify
+  a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
+  
+  \see setTail
+*/
+void QCPItemLine::setHead(const QCPLineEnding &head)
+{
+  mHead = head;
+}
+
+/*!
+  Sets the line ending style of the tail. The tail corresponds to the \a start position.
+  
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify
+  a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
+  
+  \see setHead
+*/
+void QCPItemLine::setTail(const QCPLineEnding &tail)
+{
+  mTail = tail;
+}
+
+/* inherits documentation from base class */
+double QCPItemLine::selectTest(const QPointF &pos) const
+{
+  if (!mVisible)
+    return -1;
+  
+  return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
+}
+
+/* inherits documentation from base class */
+void QCPItemLine::draw(QCPPainter *painter)
+{
+  QVector2D startVec(start->pixelPoint());
+  QVector2D endVec(end->pixelPoint());
+  if (startVec.toPoint() == endVec.toPoint())
+    return;
+  // get visible segment of straight line inside clipRect:
+  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
+  clipPad = qMax(clipPad, mainPen().widthF());
+  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
+  // paint visible segment, if existent:
+  if (!line.isNull())
+  {
+    painter->setPen(mainPen());
+    painter->drawLine(line);
+    painter->setBrush(Qt::SolidPattern);
+    if (mTail.style() != QCPLineEnding::esNone)
+      mTail.draw(painter, startVec, startVec-endVec);
+    if (mHead.style() != QCPLineEnding::esNone)
+      mHead.draw(painter, endVec, endVec-startVec);
+  }
+}
+
+/*! \internal
+
+  Returns the section of the line defined by \a start and \a end, that is visible in the specified
+  \a rect.
+  
+  This is a helper function for \ref draw.
+*/
+QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
+{
+  bool containsStart = rect.contains(start.x(), start.y());
+  bool containsEnd = rect.contains(end.x(), end.y());
+  if (containsStart && containsEnd)
+    return QLineF(start.toPointF(), end.toPointF());
+  
+  QVector2D base = start;
+  QVector2D vec = end-start;
+  double bx, by;
+  double gamma, mu;
+  QLineF result;
+  QList<QVector2D> pointVectors;
+
+  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
+  {
+    // check top of rect:
+    bx = rect.left();
+    by = rect.top();
+    mu = (by-base.y())/vec.y();
+    if (mu >= 0 && mu <= 1)
+    {
+      gamma = base.x()-bx + mu*vec.x();
+      if (gamma >= 0 && gamma <= rect.width())
+        pointVectors.append(QVector2D(bx+gamma, by));
+    }
+    // check bottom of rect:
+    bx = rect.left();
+    by = rect.bottom();
+    mu = (by-base.y())/vec.y();
+    if (mu >= 0 && mu <= 1)
+    {
+      gamma = base.x()-bx + mu*vec.x();
+      if (gamma >= 0 && gamma <= rect.width())
+        pointVectors.append(QVector2D(bx+gamma, by));
+    }
+  }
+  if (!qFuzzyIsNull(vec.x())) // line is not vertical
+  {
+    // check left of rect:
+    bx = rect.left();
+    by = rect.top();
+    mu = (bx-base.x())/vec.x();
+    if (mu >= 0 && mu <= 1)
+    {
+      gamma = base.y()-by + mu*vec.y();
+      if (gamma >= 0 && gamma <= rect.height())
+        pointVectors.append(QVector2D(bx, by+gamma));
+    }
+    // check right of rect:
+    bx = rect.right();
+    by = rect.top();
+    mu = (bx-base.x())/vec.x();
+    if (mu >= 0 && mu <= 1)
+    {
+      gamma = base.y()-by + mu*vec.y();
+      if (gamma >= 0 && gamma <= rect.height())
+        pointVectors.append(QVector2D(bx, by+gamma));
+    }
+  }
+  
+  if (containsStart)
+    pointVectors.append(start);
+  if (containsEnd)
+    pointVectors.append(end);
+  
+  // evaluate points:
+  if (pointVectors.size() == 2)
+  {
+    result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
+  } else if (pointVectors.size() > 2)
+  {
+    // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
+    double distSqrMax = 0;
+    QVector2D pv1, pv2;
+    for (int i=0; i<pointVectors.size()-1; ++i)
+    {
+      for (int k=i+1; k<pointVectors.size(); ++k)
+      {
+        double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
+        if (distSqr > distSqrMax)
+        {
+          pv1 = pointVectors.at(i);
+          pv2 = pointVectors.at(k);
+          distSqrMax = distSqr;
+        }
+      }
+    }
+    result.setPoints(pv1.toPointF(), pv2.toPointF());
+  }
+  return result;
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemLine::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+
+// ================================================================================
+// =================== QCPItemEllipse
+// ================================================================================
+
+/*! \class QCPItemEllipse
+  \brief An ellipse
+
+  \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in.
+*/
+
+/*!
+  Creates an ellipse item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  topLeft(createPosition("topLeft")),
+  bottomRight(createPosition("bottomRight")),
+  topLeftRim(createAnchor("topLeftRim", aiTopLeftRim)),
+  top(createAnchor("top", aiTop)),
+  topRightRim(createAnchor("topRightRim", aiTopRightRim)),
+  right(createAnchor("right", aiRight)),
+  bottomRightRim(createAnchor("bottomRightRim", aiBottomRightRim)),
+  bottom(createAnchor("bottom", aiBottom)),
+  bottomLeftRim(createAnchor("bottomLeftRim", aiBottomLeftRim)),
+  left(createAnchor("left", aiLeft))
+{
+  topLeft->setCoords(0, 1);
+  bottomRight->setCoords(1, 0);
+  
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue, 2));
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+}
+
+QCPItemEllipse::~QCPItemEllipse()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the ellipse
+  
+  \see setSelectedPen, setBrush
+*/
+void QCPItemEllipse::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the ellipse when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemEllipse::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to
+  Qt::NoBrush.
+  
+  \see setSelectedBrush, setPen
+*/
+void QCPItemEllipse::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a
+  brush to Qt::NoBrush.
+  
+  \see setBrush
+*/
+void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/* inherits documentation from base class */
+double QCPItemEllipse::selectTest(const QPointF &pos) const
+{
+  double result = -1;
+  QPointF p1 = topLeft->pixelPoint();
+  QPointF p2 = bottomRight->pixelPoint();
+  QPointF center((p1+p2)/2.0);
+  double a = qAbs(p1.x()-p2.x())/2.0;
+  double b = qAbs(p1.y()-p2.y())/2.0;
+  double x = pos.x()-center.x();
+  double y = pos.y()-center.y();
+  
+  // distance to border:
+  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
+  result = qAbs(c-1)*qSqrt(x*x+y*y);
+  // filled ellipse, allow click inside to count as hit:
+  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
+  {
+    if (x*x/(a*a) + y*y/(b*b) <= 1)
+      result = mParentPlot->selectionTolerance()*0.99;
+  }
+  return result;
+}
+
+/* inherits documentation from base class */
+void QCPItemEllipse::draw(QCPPainter *painter)
+{
+  QPointF p1 = topLeft->pixelPoint();
+  QPointF p2 = bottomRight->pixelPoint();
+  if (p1.toPoint() == p2.toPoint())
+    return;
+  QRectF ellipseRect = QRectF(p1, p2).normalized();
+  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
+  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
+  {
+    painter->setPen(mainPen());
+    painter->setBrush(mainBrush());
+    try
+    {
+      painter->drawEllipse(ellipseRect);
+    } catch (...)
+    {
+      qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
+      setVisible(false);
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
+{
+  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
+  switch (anchorId)
+  {
+    case aiTopLeftRim:     return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
+    case aiTop:            return (rect.topLeft()+rect.topRight())*0.5;
+    case aiTopRightRim:    return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
+    case aiRight:          return (rect.topRight()+rect.bottomRight())*0.5;
+    case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
+    case aiBottom:         return (rect.bottomLeft()+rect.bottomRight())*0.5;
+    case aiBottomLeftRim:  return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
+    case aiLeft:           return (rect.topLeft()+rect.bottomLeft())*0.5;;
+  }
+  
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemEllipse::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
+  is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPItemEllipse::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+
+// ================================================================================
+// =================== QCPItemRect
+// ================================================================================
+
+/*! \class QCPItemRect
+  \brief A rectangle
+
+  \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a topLeft and \a bottomRight, which define the rectangle.
+*/
+
+/*!
+  Creates a rectangle item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  topLeft(createPosition("topLeft")),
+  bottomRight(createPosition("bottomRight")),
+  top(createAnchor("top", aiTop)),
+  topRight(createAnchor("topRight", aiTopRight)),
+  right(createAnchor("right", aiRight)),
+  bottom(createAnchor("bottom", aiBottom)),
+  bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
+  left(createAnchor("left", aiLeft))
+{
+  topLeft->setCoords(0, 1);
+  bottomRight->setCoords(1, 0);
+  
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue,2));
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+}
+
+QCPItemRect::~QCPItemRect()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the rectangle
+  
+  \see setSelectedPen, setBrush
+*/
+void QCPItemRect::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the rectangle when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemRect::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to
+  Qt::NoBrush.
+  
+  \see setSelectedBrush, setPen
+*/
+void QCPItemRect::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a
+  brush to Qt::NoBrush.
+  
+  \see setBrush
+*/
+void QCPItemRect::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/* inherits documentation from base class */
+double QCPItemRect::selectTest(const QPointF &pos) const
+{
+  if (!mVisible)
+    return -1;
+  
+  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
+  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
+  return rectSelectTest(rect, pos, filledRect);
+}
+
+/* inherits documentation from base class */
+void QCPItemRect::draw(QCPPainter *painter)
+{
+  QPointF p1 = topLeft->pixelPoint();
+  QPointF p2 = bottomRight->pixelPoint();
+  if (p1.toPoint() == p2.toPoint())
+    return;
+  QRectF rect = QRectF(p1, p2).normalized();
+  double clipPad = mainPen().widthF();
+  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
+  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
+  {
+    painter->setPen(mainPen());
+    painter->setBrush(mainBrush());
+    painter->drawRect(rect);
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
+{
+  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
+  switch (anchorId)
+  {
+    case aiTop:         return (rect.topLeft()+rect.topRight())*0.5;
+    case aiTopRight:    return rect.topRight();
+    case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;
+    case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;
+    case aiBottomLeft:  return rect.bottomLeft();
+    case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;;
+  }
+  
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemRect::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
+  is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPItemRect::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+
+// ================================================================================
+// =================== QCPItemPixmap
+// ================================================================================
+
+/*! \class QCPItemPixmap
+  \brief An arbitrary pixmap
+
+  \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will
+  be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to
+  fit the rectangle or be drawn aligned to the topLeft position.
+  
+  If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown
+  on the right side of the example image), the pixmap will be flipped in the respective
+  orientations.
+*/
+
+/*!
+  Creates a rectangle item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  topLeft(createPosition("topLeft")),
+  bottomRight(createPosition("bottomRight")),
+  top(createAnchor("top", aiTop)),
+  topRight(createAnchor("topRight", aiTopRight)),
+  right(createAnchor("right", aiRight)),
+  bottom(createAnchor("bottom", aiBottom)),
+  bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
+  left(createAnchor("left", aiLeft))
+{
+  topLeft->setCoords(0, 1);
+  bottomRight->setCoords(1, 0);
+  
+  setPen(Qt::NoPen);
+  setSelectedPen(QPen(Qt::blue));
+  setScaled(false, Qt::KeepAspectRatio);
+}
+
+QCPItemPixmap::~QCPItemPixmap()
+{
+}
+
+/*!
+  Sets the pixmap that will be displayed.
+*/
+void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
+{
+  mPixmap = pixmap;
+}
+
+/*!
+  Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a
+  bottomRight positions.
+*/
+void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode)
+{
+  mScaled = scaled;
+  mAspectRatioMode = aspectRatioMode;
+  updateScaledPixmap();
+}
+
+/*!
+  Sets the pen that will be used to draw a border around the pixmap.
+  
+  \see setSelectedPen, setBrush
+*/
+void QCPItemPixmap::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw a border around the pixmap when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemPixmap::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/* inherits documentation from base class */
+double QCPItemPixmap::selectTest(const QPointF &pos) const
+{
+  if (!mVisible)
+    return -1;
+  
+  return rectSelectTest(getFinalRect(), pos, true);
+}
+
+/* inherits documentation from base class */
+void QCPItemPixmap::draw(QCPPainter *painter)
+{
+  bool flipHorz = false;
+  bool flipVert = false;
+  QRect rect = getFinalRect(&flipHorz, &flipVert);
+  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
+  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
+  if (boundingRect.intersects(clipRect()))
+  {
+    updateScaledPixmap(rect, flipHorz, flipVert);
+    painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
+    QPen pen = mainPen();
+    if (pen.style() != Qt::NoPen)
+    {
+      painter->setPen(pen);
+      painter->setBrush(Qt::NoBrush);
+      painter->drawRect(rect);
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
+{
+  bool flipHorz;
+  bool flipVert;
+  QRect rect = getFinalRect(&flipHorz, &flipVert);
+  // we actually want denormal rects (negative width/height) here, so restore
+  // the flipped state:
+  if (flipHorz)
+    rect.adjust(rect.width(), 0, -rect.width(), 0);
+  if (flipVert)
+    rect.adjust(0, rect.height(), 0, -rect.height());
+  
+  switch (anchorId)
+  {
+    case aiTop:         return (rect.topLeft()+rect.topRight())*0.5;
+    case aiTopRight:    return rect.topRight();
+    case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;
+    case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;
+    case aiBottomLeft:  return rect.bottomLeft();
+    case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;;
+  }
+  
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+  
+  Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The
+  parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped
+  horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a
+  bottomRight.)
+  
+  This function only creates the scaled pixmap when the buffered pixmap has a different size than
+  the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does
+  not cause expensive rescaling every time.
+  
+  If scaling is disabled, sets mScaledPixmap to a null QPixmap.
+*/
+void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
+{
+  if (mScaled)
+  {
+    if (finalRect.isNull())
+      finalRect = getFinalRect(&flipHorz, &flipVert);
+    if (finalRect.size() != mScaledPixmap.size())
+    {
+      mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, Qt::SmoothTransformation);
+      if (flipHorz || flipVert)
+        mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
+    }
+  } else if (!mScaledPixmap.isNull())
+    mScaledPixmap = QPixmap();
+}
+
+/*! \internal
+  
+  Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions
+  and scaling settings.
+  
+  The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn
+  flipped horizontally or vertically in the returned rect. (The returned rect itself is always
+  normalized, i.e. the top left corner of the rect is actually further to the top/left than the
+  bottom right corner). This is the case when the item position \a topLeft is further to the
+  bottom/right than \a bottomRight.
+  
+  If scaling is disabled, returns a rect with size of the original pixmap and the top left corner
+  aligned with the item position \a topLeft. The position \a bottomRight is ignored.
+*/
+QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
+{
+  QRect result;
+  bool flipHorz = false;
+  bool flipVert = false;
+  QPoint p1 = topLeft->pixelPoint().toPoint();
+  QPoint p2 = bottomRight->pixelPoint().toPoint();
+  if (p1 == p2)
+    return QRect(p1, QSize(0, 0));
+  if (mScaled)
+  {
+    QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
+    QPoint topLeft = p1;
+    if (newSize.width() < 0)
+    {
+      flipHorz = true;
+      newSize.rwidth() *= -1;
+      topLeft.setX(p2.x());
+    }
+    if (newSize.height() < 0)
+    {
+      flipVert = true;
+      newSize.rheight() *= -1;
+      topLeft.setY(p2.y());
+    }
+    QSize scaledSize = mPixmap.size();
+    scaledSize.scale(newSize, mAspectRatioMode);
+    result = QRect(topLeft, scaledSize);
+  } else
+  {
+    result = QRect(p1, mPixmap.size());
+  }
+  if (flippedHorz)
+    *flippedHorz = flipHorz;
+  if (flippedVert)
+    *flippedVert = flipVert;
+  return result;
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemPixmap::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+
+// ================================================================================
+// =================== QCPItemText
+// ================================================================================
+
+/*! \class QCPItemText
+  \brief A text label
+
+  \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  Its position is defined by the member \a position and the setting of \ref setPositionAlignment.
+  The latter controls which part of the text rect shall be aligned with \a position.
+  
+  The text alignment itself (i.e. left, center, right) can be controlled with \ref
+  setTextAlignment.
+  
+  The text may be rotated around the \a position point with \ref setRotation.
+*/
+
+/*!
+  Creates a text item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemText::QCPItemText(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  position(createPosition("position")),
+  topLeft(createAnchor("topLeft", aiTopLeft)),
+  top(createAnchor("top", aiTop)),
+  topRight(createAnchor("topRight", aiTopRight)),
+  right(createAnchor("right", aiRight)),
+  bottomRight(createAnchor("bottomRight", aiBottomRight)),
+  bottom(createAnchor("bottom", aiBottom)),
+  bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
+  left(createAnchor("left", aiLeft))
+{
+  position->setCoords(0, 0);
+  
+  setRotation(0);
+  setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
+  setPositionAlignment(Qt::AlignCenter);
+  setText("text");
+  
+  setPen(Qt::NoPen);
+  setSelectedPen(Qt::NoPen);
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+  setColor(Qt::black);
+  setSelectedColor(Qt::blue);
+}
+
+QCPItemText::~QCPItemText()
+{
+}
+
+/*!
+  Sets the color of the text.
+*/
+void QCPItemText::setColor(const QColor &color)
+{
+  mColor = color;
+}
+
+/*!
+  Sets the color of the text that will be used when the item is selected.
+*/
+void QCPItemText::setSelectedColor(const QColor &color)
+{
+  mSelectedColor = color;
+}
+
+/*!
+  Sets the pen that will be used do draw a rectangular border around the text. To disable the
+  border, set \a pen to Qt::NoPen.
+  
+  \see setSelectedPen, setBrush, setPadding
+*/
+void QCPItemText::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used do draw a rectangular border around the text, when the item is
+  selected. To disable the border, set \a pen to Qt::NoPen.
+  
+  \see setPen
+*/
+void QCPItemText::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the brush that will be used do fill the background of the text. To disable the
+  background, set \a brush to Qt::NoBrush.
+  
+  \see setSelectedBrush, setPen, setPadding
+*/
+void QCPItemText::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the
+  background, set \a brush to Qt::NoBrush.
+  
+  \see setBrush
+*/
+void QCPItemText::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/*!
+  Sets the font of the text.
+  
+  \see setSelectedFont, setColor
+*/
+void QCPItemText::setFont(const QFont &font)
+{
+  mFont = font;
+}
+
+/*!
+  Sets the font of the text that will be used when the item is selected.
+  
+  \see setFont
+*/
+void QCPItemText::setSelectedFont(const QFont &font)
+{
+  mSelectedFont = font;
+}
+
+/*!
+  Sets the text that will be displayed. Multi-line texts are supported by inserting a line break
+  character, e.g. '\n'.
+  
+  \see setFont, setColor, setTextAlignment
+*/
+void QCPItemText::setText(const QString &text)
+{
+  mText = text;
+}
+
+/*!
+  Sets which point of the text rect shall be aligned with \a position.
+  
+  Examples:
+  \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such
+  that the top of the text rect will be horizontally centered on \a position.
+  \li If \a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \a position will indicate the
+  bottom left corner of the text rect.
+  
+  If you want to control the alignment of (multi-lined) text within the text rect, use \ref
+  setTextAlignment.
+*/
+void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
+{
+  mPositionAlignment = alignment;
+}
+
+/*!
+  Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
+*/
+void QCPItemText::setTextAlignment(Qt::Alignment alignment)
+{
+  mTextAlignment = alignment;
+}
+
+/*!
+  Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated
+  around \a position.
+*/
+void QCPItemText::setRotation(double degrees)
+{
+  mRotation = degrees;
+}
+
+/*!
+  Sets the distance between the border of the text rectangle and the text. The appearance (and
+  visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush.
+*/
+void QCPItemText::setPadding(const QMargins &padding)
+{
+  mPadding = padding;
+}
+
+/* inherits documentation from base class */
+double QCPItemText::selectTest(const QPointF &pos) const
+{
+  if (!mVisible)
+    return -1;
+  
+  // The rect may be rotated, so we transform the actual clicked pos to the rotated
+  // coordinate system, wo we can use the normal rectSelectTest function for non-rotated rects:
+  QPointF positionPixels(position->pixelPoint());
+  QTransform inputTransform;
+  inputTransform.translate(positionPixels.x(), positionPixels.y());
+  inputTransform.rotate(-mRotation);
+  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
+  QPointF rotatedPos = inputTransform.map(pos);
+  QFontMetrics fontMetrics(mFont);
+  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
+  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
+  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
+  textBoxRect.moveTopLeft(textPos.toPoint());
+
+  return rectSelectTest(textBoxRect, rotatedPos, true);
+}
+
+/* inherits documentation from base class */
+void QCPItemText::draw(QCPPainter *painter)
+{
+  QPointF pos(position->pixelPoint());
+  QTransform transform;
+  transform.translate(pos.x(), pos.y());
+  if (!qFuzzyIsNull(mRotation))
+    transform.rotate(mRotation);
+  painter->setFont(mainFont());
+  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
+  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
+  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
+  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
+  textBoxRect.moveTopLeft(textPos.toPoint());
+  double clipPad = mainPen().widthF();
+  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
+  if (transform.mapRect(boundingRect).intersects(clipRect()))
+  {
+    painter->setTransform(transform);
+    if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
+        (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
+    {
+      painter->setPen(mainPen());
+      painter->setBrush(mainBrush());
+      painter->drawRect(textBoxRect);
+    }
+    painter->setBrush(Qt::NoBrush);
+    painter->setPen(QPen(mainColor()));
+    painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemText::anchorPixelPoint(int anchorId) const
+{
+  // get actual rect points (pretty much copied from draw function):
+  QPointF pos(position->pixelPoint());
+  QTransform transform;
+  transform.translate(pos.x(), pos.y());
+  if (!qFuzzyIsNull(mRotation))
+    transform.rotate(mRotation);
+  QFontMetrics fontMetrics(mainFont());
+  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
+  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
+  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
+  textBoxRect.moveTopLeft(textPos.toPoint());
+  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
+  
+  switch (anchorId)
+  {
+    case aiTopLeft:     return rectPoly.at(0);
+    case aiTop:         return (rectPoly.at(0)+rectPoly.at(1))*0.5;
+    case aiTopRight:    return rectPoly.at(1);
+    case aiRight:       return (rectPoly.at(1)+rectPoly.at(2))*0.5;
+    case aiBottomRight: return rectPoly.at(2);
+    case aiBottom:      return (rectPoly.at(2)+rectPoly.at(3))*0.5;
+    case aiBottomLeft:  return rectPoly.at(3);
+    case aiLeft:        return (rectPoly.at(3)+rectPoly.at(0))*0.5;
+  }
+  
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+  
+  Returns the point that must be given to the QPainter::drawText function (which expects the top
+  left point of the text rect), according to the position \a pos, the text bounding box \a rect and
+  the requested \a positionAlignment.
+  
+  For example, if \a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point
+  will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally
+  drawn at that point, the lower left corner of the resulting text rect is at \a pos.
+*/
+QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
+{
+  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
+    return pos;
+  
+  QPointF result = pos; // start at top left
+  if (positionAlignment.testFlag(Qt::AlignHCenter))
+    result.rx() -= rect.width()/2.0;
+  else if (positionAlignment.testFlag(Qt::AlignRight))
+    result.rx() -= rect.width();
+  if (positionAlignment.testFlag(Qt::AlignVCenter))
+    result.ry() -= rect.height()/2.0;
+  else if (positionAlignment.testFlag(Qt::AlignBottom))
+    result.ry() -= rect.height();
+  return result;
+}
+
+/*! \internal
+
+  Returns the font that should be used for drawing text. Returns mFont when the item is not selected
+  and mSelectedFont when it is.
+*/
+QFont QCPItemText::mainFont() const
+{
+  return mSelected ? mSelectedFont : mFont;
+}
+
+/*! \internal
+
+  Returns the color that should be used for drawing text. Returns mColor when the item is not
+  selected and mSelectedColor when it is.
+*/
+QColor QCPItemText::mainColor() const
+{
+  return mSelected ? mSelectedColor : mColor;
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemText::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
+  is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPItemText::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+
+// ================================================================================
+// =================== QCPPainter
+// ================================================================================
+
+/*! \class QCPPainter
+  \brief QPainter subclass used internally
+  
+  This internal class is used to provide some extended functionality e.g. for tweaking position
+  consistency between antialiased and non-antialiased painting and drawing common shapes (like
+  scatter symbols). Further it provides workarounds for QPainter quirks.
+  
+  \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and
+  restore. So while it is possible to pass a QCPPainter instance to a function that expects a
+  QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because
+  it will call the base class implementations of the functions actually hidden by QCPPainter).
+*/
+
+/*!
+  Creates a new QCPPainter instance and sets default values
+*/
+QCPPainter::QCPPainter() :
+  QPainter(),
+  mScaledExportMode(false),
+  mPdfExportMode(false),
+  mIsAntialiasing(false)
+{
+}
+
+/*!
+  Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just
+  like the analogous QPainter constructor, begins painting on \a device immediately.
+*/
+QCPPainter::QCPPainter(QPaintDevice *device) :
+  QPainter(device),
+  mScaledExportMode(false),
+  mPdfExportMode(false),
+  mIsAntialiasing(false)
+{
+}
+
+QCPPainter::~QCPPainter()
+{
+}
+
+/*!
+  Sets the pixmap that will be used to draw scatters with \ref drawScatter, when the style is
+  QCP::ssPixmap.
+*/
+void QCPPainter::setScatterPixmap(const QPixmap pm)
+{
+  mScatterPixmap = pm;
+}
+
+/*!
+  Sets the pen of the painter and applies certain fixes to it, depending on the mode of this
+  QCPPainter.
+  
+  \note this function hides the non-virtual base class implementation.
+*/
+void QCPPainter::setPen(const QPen &pen)
+{
+  QPainter::setPen(pen);
+  if (mScaledExportMode)
+    fixScaledPen();
+}
+
+/*! \overload
+  
+  Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of
+  this QCPPainter.
+  
+  \note this function hides the non-virtual base class implementation.
+*/
+void QCPPainter::setPen(const QColor &color)
+{
+  QPainter::setPen(color);
+  if (mScaledExportMode)
+    fixScaledPen();
+}
+
+/*! \overload
+  
+  Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of
+  this QCPPainter.
+  
+  \note this function hides the non-virtual base class implementation.
+*/
+void QCPPainter::setPen(Qt::PenStyle penStyle)
+{
+  QPainter::setPen(penStyle);
+  if (mScaledExportMode)
+    fixScaledPen();
+}
+
+/*! \overload
+  
+  Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when
+  antialiasing is disabled.
+  
+  \note this function hides the non-virtual base class implementation.
+*/
+void QCPPainter::drawLine(const QLineF &line)
+{
+  if (mIsAntialiasing)
+    QPainter::drawLine(line);
+  else
+    QPainter::drawLine(line.toLine());
+}
+
+/*! 
+  Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint
+  with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between
+  antialiased and non-antialiased painting (Since Qt uses slightly different coordinate systems for
+  AA/Non-AA painting).
+*/
+void QCPPainter::setAntialiasing(bool enabled)
+{
+  if (mPdfExportMode)
+    return;
+  
+  setRenderHint(QPainter::Antialiasing, enabled);
+  if (mIsAntialiasing != enabled)
+  {
+    if (mIsAntialiasing)
+      translate(-0.5, -0.5);
+    else
+      translate(0.5, 0.5);
+    mIsAntialiasing = enabled;
+  }
+}
+
+/*!
+  Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to
+  QPainter, the save/restore functions are reimplemented to also save/restore those members.
+  
+  \note this function hides the non-virtual base class implementation.
+  
+  \see restore
+*/
+void QCPPainter::save()
+{
+  mAntialiasingStack.push(mIsAntialiasing);
+  QPainter::save();
+}
+
+/*!
+  Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to
+  QPainter, the save/restore functions are reimplemented to also save/restore those members.
+  
+  \note this function hides the non-virtual base class implementation.
+  
+  \see save
+*/
+void QCPPainter::restore()
+{
+  if (!mAntialiasingStack.isEmpty())
+    mIsAntialiasing = mAntialiasingStack.pop();
+  else
+    qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
+  QPainter::restore();
+}
+
+/*!
+  Sets whether the painter shall adjust its fixes/workarounds optimized for vectorized pdf export.
+
+  This means for example, that the antialiasing/non-antialiasing fix introduced with \ref
+  setAntialiasing is not used, since PDF is not rastered and thus works with floating point data
+  natively.
+*/
+void QCPPainter::setPdfExportMode(bool enabled)
+{
+  mPdfExportMode = enabled;
+}
+
+/*!
+  Sets whether the painter shall adjust its fixes/workarounds optimized for scaled export to
+  rastered image formats.
+
+  For example this provides a workaround for a QPainter bug that prevents scaling of pen widths for
+  pens with width 0, although the QPainter::NonCosmeticDefaultPen render hint is set.
+*/
+void QCPPainter::setScaledExportMode(bool enabled)
+{
+  mScaledExportMode = enabled;
+}
+
+/*!
+  Provides a workaround for a QPainter bug that prevents scaling of pen widths for pens with width
+  0, although the QPainter::NonCosmeticDefaultPen render hint is set.
+  
+  Changes the pen width from 0 to 1, if appropriate.
+  
+  Does nothing if the QCPPainter is not in scaled export mode (\ref setScaledExportMode).
+*/
+void QCPPainter::fixScaledPen()
+{
+  if (mScaledExportMode && pen().isCosmetic() && qFuzzyIsNull(pen().widthF()))
+  {
+    QPen p = pen();
+    p.setWidth(1);
+    QPainter::setPen(p);
+  }
+}
+
+/*! 
+  Draws a single scatter point with the specified \a style and \a size in pixels at the pixel position \a x and \a y.
+  
+  If the \a style is ssPixmap, make sure to pass the respective pixmap with \ref setScatterPixmap before calling
+  this function.
+*/
+void QCPPainter::drawScatter(double x, double y, double size, QCP::ScatterStyle style)
+{
+  double w = size/2.0;
+  switch (style)
+  {
+    case QCP::ssNone: break;
+    case QCP::ssDot:
+    {
+      drawPoint(QPointF(x, y));
+      break;
+    }
+    case QCP::ssCross:
+    {
+      drawLine(QLineF(x-w, y-w, x+w, y+w));
+      drawLine(QLineF(x-w, y+w, x+w, y-w));
+      break;
+    }
+    case QCP::ssPlus:
+    {
+      drawLine(QLineF(x-w, y, x+w, y));
+      drawLine(QLineF(x, y+w, x, y-w));
+      break;
+    }
+    case QCP::ssCircle:
+    {
+      setBrush(Qt::NoBrush);
+      drawEllipse(QPointF(x,y), w, w);
+      break;
+    }
+    case QCP::ssDisc:
+    {
+      setBrush(QBrush(pen().color()));
+      drawEllipse(QPointF(x,y), w, w);
+      break;
+    }
+    case QCP::ssSquare:
+    {
+      setBrush(Qt::NoBrush);
+      drawRect(QRectF(x-w, y-w, size, size));
+      break;
+    }
+    case QCP::ssDiamond:
+    {
+      setBrush(Qt::NoBrush);
+      drawLine(QLineF(x-w, y, x, y-w));
+      drawLine(QLineF(x, y-w, x+w, y));
+      drawLine(QLineF(x+w, y, x, y+w));
+      drawLine(QLineF(x, y+w, x-w, y));
+      break;
+    }
+    case QCP::ssStar:
+    {
+      drawLine(QLineF(x-w, y, x+w, y));
+      drawLine(QLineF(x, y+w, x, y-w));
+      drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
+      drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
+      break;
+    }
+    case QCP::ssTriangle:
+    {
+      drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
+      drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
+      drawLine(QLineF(x, y-0.977*w, x-w, y+0.755*w));
+      break;
+    }
+    case QCP::ssTriangleInverted:
+    {
+      drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
+      drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
+      drawLine(QLineF(x, y+0.977*w, x-w, y-0.755*w));
+      break;
+    }
+    case QCP::ssCrossSquare:
+    {
+      setBrush(Qt::NoBrush);
+      drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
+      drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
+      drawRect(QRectF(x-w,y-w,size,size));
+      break;
+    }
+    case QCP::ssPlusSquare:
+    {
+      setBrush(Qt::NoBrush);
+      drawLine(QLineF(x-w, y, x+w*0.95, y));
+      drawLine(QLineF(x, y+w, x, y-w));
+      drawRect(QRectF(x-w, y-w, size, size));
+      break;
+    }
+    case QCP::ssCrossCircle:
+    {
+      setBrush(Qt::NoBrush);
+      drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.67, y+w*0.67));
+      drawLine(QLineF(x-w*0.707, y+w*0.67, x+w*0.67, y-w*0.707));
+      drawEllipse(QPointF(x,y), w, w);
+      break;
+    }
+    case QCP::ssPlusCircle:
+    {
+      setBrush(Qt::NoBrush);
+      drawLine(QLineF(x-w, y, x+w, y));
+      drawLine(QLineF(x, y+w, x, y-w));
+      drawEllipse(QPointF(x,y), w, w);
+      break;
+    }
+    case QCP::ssPeace:
+    {
+      setBrush(Qt::NoBrush);
+      drawLine(QLineF(x, y-w, x, y+w));
+      drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
+      drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
+      drawEllipse(QPointF(x,y), w, w);
+      break;
+    }
+    case QCP::ssPixmap:
+    {
+      drawPixmap(x-mScatterPixmap.width()*0.5, y-mScatterPixmap.height()*0.5, mScatterPixmap);
+      // if something in here is changed, adapt QCP::ssPixmap special case in drawLegendIcon(), too
+      break;
+    }
+  }
+}
+
+
+// ================================================================================
+// =================== QCPLineEnding
+// ================================================================================
+
+/*! \class QCPLineEnding
+  \brief Handles the different ending decorations for line-like items
+  
+  \image html QCPLineEnding.png "The various ending styles currently supported"
+  
+  For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine
+  has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.
+ 
+  The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can
+  be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of
+  the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.
+  For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite
+  directions, e.g. "outward". This can be changed by \ref setInverted, which would make the
+  respective arrow point inward.
+  
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify a
+  QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g. \code
+  myItemLine->setHead(QCPLineEnding::esSpikeArrow) \endcode
+*/
+
+/*!
+  Creates a QCPLineEnding instance with default values (style \ref esNone).
+*/
+QCPLineEnding::QCPLineEnding() :
+  mStyle(esNone),
+  mWidth(8),
+  mLength(10),
+  mInverted(false)
+{
+}
+
+/*!
+  Creates a QCPLineEnding instance with the specified values.
+*/
+QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
+  mStyle(style),
+  mWidth(width),
+  mLength(length),
+  mInverted(inverted)
+{
+}
+
+/*!
+  Sets the style of the ending decoration.
+*/
+void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)
+{
+  mStyle = style;
+}
+
+/*!
+  Sets the width of the ending decoration, if the style supports it. On arrows, for example, the
+  width defines the size perpendicular to the arrow's pointing direction.
+  
+  \see setLength
+*/
+void QCPLineEnding::setWidth(double width)
+{
+  mWidth = width;
+}
+
+/*!
+  Sets the length of the ending decoration, if the style supports it. On arrows, for example, the
+  length defines the size in pointing direction.
+  
+  \see setWidth
+*/
+void QCPLineEnding::setLength(double length)
+{
+  mLength = length;
+}
+
+/*!
+  Sets whether the direction of the ending decoration shall be inverted with respect to the natural
+  direction given by the parent item. For example, an arrow decoration will point inward when
+  \a inverted is set to true.
+*/
+void QCPLineEnding::setInverted(bool inverted)
+{
+  mInverted = inverted;
+}
+
+/*! \internal
+  
+  Returns the maximum pixel radius the ending decoration might cover, starting from the position
+  the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item).
+  
+  This is relevant for clipping. Only omit painting of the decoration when the position where the
+  decoration is supposed to be drawn is farther away from the clipping rect than the returned
+  distance.
+*/
+double QCPLineEnding::boundingDistance() const
+{
+  switch (mStyle)
+  {
+    case esNone:
+      return 0;
+      
+    case esFlatArrow:
+    case esSpikeArrow:
+    case esLineArrow:
+      return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
+      
+    case esDisc:
+    case esSquare:
+    case esDiamond:
+    case esBar:
+      return mWidth*1.42; // items that only have a width -> with*sqrt(2)
+  }
+  return 0;
+}
+
+/*! \internal
+  
+  Draws the line ending with the specified \a painter at the position \a pos. The direction of the
+  line ending is controlled with \a dir.
+*/
+void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
+{
+  if (mStyle == esNone)
+    return;
+  
+  QVector2D lengthVec(dir.normalized()*(mInverted ? -1 : 1));
+  if (lengthVec.isNull())
+    lengthVec = QVector2D(1, 0);
+  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
+  lengthVec *= mLength;
+  widthVec *= mWidth*0.5;
+  
+  QPen penBackup = painter->pen();
+  QPen miterPen = penBackup;
+  miterPen.setJoinStyle(Qt::MiterJoin);
+  switch (mStyle)
+  {
+    case esNone: break;
+    case esFlatArrow:
+    {
+      QPointF points[3] = {pos.toPointF(),
+                           (pos-lengthVec+widthVec).toPointF(),
+                           (pos-lengthVec-widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->drawConvexPolygon(points, 3);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esSpikeArrow:
+    {
+      QPointF points[4] = {pos.toPointF(),
+                           (pos-lengthVec+widthVec).toPointF(),
+                           (pos-lengthVec*0.8).toPointF(),
+                           (pos-lengthVec-widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->drawConvexPolygon(points, 4);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esLineArrow:
+    {
+      QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
+                           pos.toPointF(),
+                           (pos-lengthVec-widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->drawPolyline(points, 3);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esDisc:
+    {
+      painter->drawEllipse(pos.toPointF(),  mWidth*0.5, mWidth*0.5);
+      break;
+    }
+    case esSquare:
+    {
+      QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
+      QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
+                           (pos-widthVecPerp-widthVec).toPointF(),
+                           (pos+widthVecPerp-widthVec).toPointF(),
+                           (pos+widthVecPerp+widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->drawConvexPolygon(points, 4);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esDiamond:
+    {
+      QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
+      QPointF points[4] = {(pos-widthVecPerp).toPointF(),
+                           (pos-widthVec).toPointF(),
+                           (pos+widthVecPerp).toPointF(),
+                           (pos+widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->drawConvexPolygon(points, 4);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esBar:
+    {
+      painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
+      break;
+    }
+  }
+}
+
+/*! \internal
+  \overload
+  
+  Draws the line ending. The direction is controlled with the \a angle parameter in radians.
+*/
+void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
+{
+  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
+}
+
+
+// ================================================================================
+// =================== QCPItemCurve
+// ================================================================================
+
+/*! \class QCPItemCurve
+  \brief A curved line from one point to another
+
+  \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has four positions, \a start and \a end, which define the end points of the line, and two
+  control points which define the direction the line exits from the start and the direction from
+  which it approaches the end: \a startDir and \a endDir.
+  
+  With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an
+  arrow.
+  
+  Often it is desirable for the control points to stay at fixed relative positions to the start/end
+  point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start,
+  and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir.
+*/
+
+/*!
+  Creates a curve item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  start(createPosition("start")),
+  startDir(createPosition("startDir")),
+  endDir(createPosition("endDir")),
+  end(createPosition("end"))
+{
+  start->setCoords(0, 0);
+  startDir->setCoords(0.5, 0);
+  endDir->setCoords(0, 0.5);
+  end->setCoords(1, 1);
+  
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue,2));
+}
+
+QCPItemCurve::~QCPItemCurve()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line
+  
+  \see setSelectedPen
+*/
+void QCPItemCurve::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemCurve::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the line ending style of the head. The head corresponds to the \a end position.
+  
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify
+  a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
+  
+  \see setTail
+*/
+void QCPItemCurve::setHead(const QCPLineEnding &head)
+{
+  mHead = head;
+}
+
+/*!
+  Sets the line ending style of the tail. The tail corresponds to the \a start position.
+  
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify
+  a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
+  
+  \see setHead
+*/
+void QCPItemCurve::setTail(const QCPLineEnding &tail)
+{
+  mTail = tail;
+}
+
+/* inherits documentation from base class */
+double QCPItemCurve::selectTest(const QPointF &pos) const
+{
+  if (!mVisible)
+    return -1;
+  
+  QPointF startVec(start->pixelPoint());
+  QPointF startDirVec(startDir->pixelPoint());
+  QPointF endDirVec(endDir->pixelPoint());
+  QPointF endVec(end->pixelPoint());
+
+  QPainterPath cubicPath(startVec);
+  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
+  
+  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
+  double minDistSqr = std::numeric_limits<double>::max();
+  for (int i=1; i<polygon.size(); ++i)
+  {
+    double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
+    if (distSqr < minDistSqr)
+      minDistSqr = distSqr;
+  }
+  return qSqrt(minDistSqr);
+}
+
+/* inherits documentation from base class */
+void QCPItemCurve::draw(QCPPainter *painter)
+{
+  QPointF startVec(start->pixelPoint());
+  QPointF startDirVec(startDir->pixelPoint());
+  QPointF endDirVec(endDir->pixelPoint());
+  QPointF endVec(end->pixelPoint());
+  if (QVector2D(endVec-startVec).length() > 1e10) // too large curves cause crash
+    return;
+
+  QPainterPath cubicPath(startVec);
+  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
+
+  // paint visible segment, if existent:
+  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
+  QRect cubicRect = cubicPath.controlPointRect().toRect();
+  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
+    cubicRect.adjust(0, 0, 1, 1);
+  if (clip.intersects(cubicRect))
+  {
+    painter->setPen(mainPen());
+    painter->drawPath(cubicPath);
+    painter->setBrush(Qt::SolidPattern);
+    if (mTail.style() != QCPLineEnding::esNone)
+      mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
+    if (mHead.style() != QCPLineEnding::esNone)
+      mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
+  }
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemCurve::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+
+// ================================================================================
+// =================== QCPLayer
+// ================================================================================
+
+/*! \class QCPLayer
+  \brief A layer that may contain objects, to control the rendering order
+  
+  The Layering system of QCustomPlot is the mechanism to control the rendering order of the
+  elements inside the plot, e.g. that the grid is drawn behind plottables etc.
+  
+  It is based on the two classes QCPLayer and QCPLayerable. A QCustomPlot contains an ordered list
+  of one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer,
+  QCustomPlot::moveLayer, etc.). The layers are drawn in the order they are in the list.
+
+  A QCPLayer itself contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract
+  base class from which almost all visible objects derive, like axes, grids, graphs, items, etc.
+  
+  By default, QCustomPlot has three layers: "grid", "main" and "axes" (in that order). Initially
+  the QCPGrid instances are on the "grid" layer, so the grid will be drawn beneath the objects on
+  the other two layers. The top layer is "axes" and contains all four axes, so they will be drawn
+  on top. Between these two layers, there is the "main" layer. It is initially empty and set as the
+  current layer (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc.
+  are created on this layer by default, and are thus drawn above the grid but below the axes.
+  
+  Controlling the ordering of objects is easy: Create a new layer in the position you want it to
+  be, e.g. above "main", with QCustomPlot::addLayer. Then set the current layer with
+  QCustomPlot::setCurrentLayer to that new layer and finally create the objects normally. They will
+  be placed on the new layer automatically, due to the current layer setting. Alternatively you
+  could have also ignored the current layer setting and just moved the objects with
+  QCPLayerable::setLayer to the desired layer after creating them.
+  
+  It is also possible to move whole layers. For example, If you want the grid to be shown in front
+  of all plottables/items on the "main" layer, just move it above "main" with
+  QCustomPlot::moveLayer. This way the ordering might now be "main", "grid", "axes", so while the
+  grid will still be beneath the axes, it will now be drawn above plottables/items on "main", as
+  intended.
+  
+  The rendering order within one layer is simply by order of creation. The item created last (or
+  added last to the layer), is drawn on top of all other objects on that layer.  
+  
+  When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below
+  the deleted layer, see QCustomPlot::removeLayer.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QList<QCPLayerable*> QCPLayer::children() const
+  
+  Returns a list of all layerables on this layer. The order corresponds to the rendering order,
+  i.e. layerables with higher indices are drawn above layerables with lower indices.
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates a new QCPLayer instance.
+  
+  Normally you shouldn't directly create layers like this, use QCustomPlot::addLayer instead.
+  
+  \warning It is not checked that \a layerName is actually an unique layer name in \a parentPlot.
+  This check is only performed by QCustomPlot::addLayer.
+*/
+QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
+  mParentPlot(parentPlot),
+  mName(layerName)
+{
+  // Note: no need to make sure layerName doesn't already, because layer
+  // management is done with QCustomPlot functions.
+}
+
+QCPLayer::~QCPLayer()
+{
+}
+
+/*!
+  Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be
+  accessed via QCustomPlot::layer.
+  
+  Layers with greater indices will be drawn above layers with smaller indices.
+*/
+int QCPLayer::index() const
+{
+  return mParentPlot->mLayers.indexOf(const_cast<QCPLayer*>(this));
+}
+
+/*! \internal
+  
+  Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
+  be prepended to the list, i.e. be drawn beneath the other layerables already in the list.
+  
+  This function does not change the \a mLayer member of \a layerable to this layer. (Use
+  QCPLayerable::setLayer to change the layer of an object, not this function.)
+  
+  \see removeChild
+*/
+void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
+{
+  if (!mChildren.contains(layerable))
+  {
+    if (prepend)
+      mChildren.prepend(layerable);
+    else
+      mChildren.append(layerable);
+  } else
+    qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
+}
+
+/*! \internal
+  
+  Removes the \a layerable from the list of this layer.
+  
+  This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer
+  to change the layer of an object, not this function.)
+  
+  \see addChild
+*/
+void QCPLayer::removeChild(QCPLayerable *layerable)
+{
+  if (!mChildren.removeOne(layerable))
+    qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
+}
+
+
+// ================================================================================
+// =================== QCPLayerable
+// ================================================================================
+
+/*! \class QCPLayerable
+  \brief Base class for all objects that can be placed on layers
+  
+  This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid
+  etc.
+
+  Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking
+  the layers accordingly.
+  
+  For details about the layering mechanism, see the QCPLayer documentation.
+*/
+
+/* start documentation of pure virtual functions */
+
+/*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0
+  \internal
+  
+  This function applies the default antialiasing setting to the specified \a painter, using the
+  function \ref applyAntialiasingHint. This is the antialiasing state the painter is in, when \ref
+  draw is called on the layerable. If the layerable has multiple entities whose antialiasing
+  setting may be specified individually, this function should set the antialiasing state of the
+  most prominent entity. In this case however, the \ref draw function usually calls the specialized
+  versions of this function before drawing each entity, effectively overriding the setting of the
+  default antialiasing hint.
+  
+  <b>First example:</b> QCPGraph has multiple entities that have an antialiasing setting: The graph
+  line, fills, scatters and error bars. Those can be configured via QCPGraph::setAntialiased,
+  QCPGraph::setAntialiasedFill, QCPGraph::setAntialiasedScatters etc. Consequently, there isn't
+  only the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's
+  antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and
+  QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw
+  calls the respective specialized applyAntialiasingHint function.
+  
+  <b>Second example:</b> QCPItemLine consists only of a line so there is only one antialiasing
+  setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by
+  all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the
+  respective layerable subclass.) Consequently it only has the normal
+  QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to
+  care about setting any antialiasing states, because the default antialiasing hint is already set
+  on the painter when the \ref draw function is called, and that's the state it wants to draw the
+  line with.
+*/
+
+/*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
+  \internal
+  
+  This function draws the layerable to the specified \a painter.
+  
+  Before this function is called, the painter's antialiasing state is set via \ref
+  applyDefaultAntialiasingHint, see the documentation there. Further, its clipping rectangle was
+  set to \ref clipRect.
+*/
+
+/* end documentation of pure virtual functions */
+
+/*!
+  Creates a new QCPLayerable instance.
+  
+  Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the
+  derived classes.
+*/
+QCPLayerable::QCPLayerable(QCustomPlot *parentPlot) :
+  QObject(0), // rather not bind to parentPlot, incase we want to allow moving of objects between customplots some day
+  mVisible(true),
+  mParentPlot(parentPlot),
+  mLayer(0),
+  mAntialiased(true)
+{
+  if (mParentPlot)
+    setLayer(mParentPlot->currentLayer());
+}
+
+QCPLayerable::~QCPLayerable()
+{
+  if (mLayer)
+  {
+    mLayer->removeChild(this);
+    mLayer = 0;
+  }
+}
+
+/*!
+  Sets the visibility of this layerable object. If an object is not visible, it will not be drawn
+  on the QCustomPlot surface, and user interaction with it (e.g. click/selection) is not possible.
+*/
+void QCPLayerable::setVisible(bool on)
+{
+  mVisible = on;
+}
+
+/*!
+  Sets the \a layer of this layerable object. The object will be placed on top of the other objects
+  already on \a layer.
+  
+  Returns true on success, i.e. if \a layer is a valid layer.
+*/
+bool QCPLayerable::setLayer(QCPLayer *layer)
+{
+  return moveToLayer(layer, false);
+}
+
+/*! \overload
+  Sets the layer of this layerable object by name
+  
+  Returns true on success, i.e. if \a layerName is a valid layer name.
+*/
+bool QCPLayerable::setLayer(const QString &layerName)
+{
+  if (!mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
+    return false;
+  }
+  if (QCPLayer *layer = mParentPlot->layer(layerName))
+  {
+    return setLayer(layer);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
+    return false;
+  }
+}
+
+/*!
+  Sets whether this object will be drawn antialiased or not.
+  
+  Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPLayerable::setAntialiased(bool enabled)
+{
+  mAntialiased = enabled;
+}
+
+/*! \internal
+  
+  Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to
+  the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is
+  false, the object will be appended.
+  
+  Returns true on success, i.e. if \a layer is a valid layer.
+*/
+bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
+{
+  if (!mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
+    return false;
+  }
+  if (layer && layer->parentPlot() != mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
+    return false;
+  }
+  
+  if (mLayer)
+    mLayer->removeChild(this);
+  mLayer = layer;
+  if (mLayer)
+    mLayer->addChild(this, prepend);
+  return true;
+}
+
+/*! \internal
+
+  Sets the QPainter::Antialiasing render hint on the provided \a painter, depending on the
+  \a localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and
+  \ref QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is
+  controlled via \a overrideElement.
+*/
+void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
+{
+  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
+    painter->setAntialiasing(false);
+  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
+    painter->setAntialiasing(true);
+  else
+    painter->setAntialiasing(localAntialiased);
+}
+
+/*! \internal
+  
+  Returns the clipping rectangle of this layerable object. By default, this is the viewport of the parent QCustomPlot.
+  Specific subclasses may reimplement this function to provide different clipping rects.
+  
+  The returned clipping rect is set on the painter before the draw function of the respective
+  object is called.
+*/
+QRect QCPLayerable::clipRect() const
+{
+  if (mParentPlot)
+    return mParentPlot->viewport();
+  else
+    return QRect();
+}
+
+
+// ================================================================================
+// =================== QCPGrid
+// ================================================================================
+
+/*! \class QCPGrid
+  \brief Responsible for drawing the grid of a QCPAxis.
+  
+  This class is tightly bound to QCPAxis. Every axis owns a grid instance internally and uses it to
+  draw the grid. Normally, you don't need to interact with the QCPGrid instance, because QCPAxis
+  reproduces the grid interface in its own interface.
+  
+  The axis and grid drawing was split into two classes to allow them to be placed on different
+  layers (both QCPAxis and QCPGrid inherit from QCPLayerable). So it is possible to have the grid
+  at the background and the axes in the foreground, and any plottables/items in between. This
+  described situation is the default setup, see QCPLayer documentation.  
+*/
+
+/*!
+  Creates a QCPGrid instance and sets default values.
+  
+  You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid
+  internally
+*/
+QCPGrid::QCPGrid(QCPAxis *parentAxis) :
+  QCPLayerable(parentAxis->parentPlot()),
+  mParentAxis(parentAxis)
+{
+  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
+  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
+  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
+  setSubGridVisible(false);
+  setAntialiased(false);
+  setAntialiasedSubGrid(false);
+  setAntialiasedZeroLine(false);
+}
+
+QCPGrid::~QCPGrid()
+{
+}
+
+/*!
+  Sets whether grid lines at sub tick marks are drawn.
+  
+  \see setSubGridPen
+*/
+void QCPGrid::setSubGridVisible(bool visible)
+{
+  mSubGridVisible = visible;
+}
+
+/*!
+  Sets whether sub grid lines are drawn antialiased.
+*/
+void QCPGrid::setAntialiasedSubGrid(bool enabled)
+{
+  mAntialiasedSubGrid = enabled;
+}
+
+/*!
+  Sets whether zero lines are drawn antialiased.
+*/
+void QCPGrid::setAntialiasedZeroLine(bool enabled)
+{
+  mAntialiasedZeroLine = enabled;
+}
+
+/*!
+  Sets the pen with which (major) grid lines are drawn.
+*/
+void QCPGrid::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen with which sub grid lines are drawn.
+*/
+void QCPGrid::setSubGridPen(const QPen &pen)
+{
+  mSubGridPen = pen;
+}
+
+/*!
+  Sets the pen with which zero lines are drawn.
+  
+  Zero lines are lines at coordinate 0 which may be drawn with a different pen than other grid
+  lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen.
+*/
+void QCPGrid::setZeroLinePen(const QPen &pen)
+{
+  mZeroLinePen = pen;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing the major grid lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+  
+  This function takes into account the local setting of the antialiasing flag as well as
+  the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
+  
+  \see setAntialiased
+*/
+void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
+}
+
+/*! \internal
+  
+  Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning
+  over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen).
+  
+  Called by QCustomPlot::draw to draw the grid of an axis.
+*/
+void QCPGrid::draw(QCPPainter *painter)
+{
+  if (!mParentAxis->visible()) return; // also don't draw grid when parent axis isn't visible
+  
+  if (mSubGridVisible)
+    drawSubGridLines(painter);
+  drawGridLines(painter);
+}
+
+/*! \internal
+  
+  Draws the main grid lines and possibly a zero line with the specified painter.
+  
+  This is a helper function called by \ref draw.
+*/
+void QCPGrid::drawGridLines(QCPPainter *painter) const
+{
+  int lowTick = mParentAxis->mLowestVisibleTick;
+  int highTick = mParentAxis->mHighestVisibleTick;
+  double t; // helper variable, result of coordinate-to-pixel transforms
+  if (mParentAxis->orientation() == Qt::Horizontal)
+  {
+    // draw zeroline:
+    int zeroLineIndex = -1;
+    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
+    {
+      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
+      painter->setPen(mZeroLinePen);
+      double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
+      for (int i=lowTick; i <= highTick; ++i)
+      {
+        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
+        {
+          zeroLineIndex = i;
+          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
+          painter->drawLine(QLineF(t, mParentAxis->mAxisRect.bottom(), t, mParentAxis->mAxisRect.top()));
+          break;
+        }
+      }
+    }
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    for (int i=lowTick; i <= highTick; ++i)
+    {
+      if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
+      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
+      painter->drawLine(QLineF(t, mParentAxis->mAxisRect.bottom(), t, mParentAxis->mAxisRect.top()));
+    }
+  } else
+  {
+    // draw zeroline:
+    int zeroLineIndex = -1;
+    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
+    {
+      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
+      painter->setPen(mZeroLinePen);
+      double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
+      for (int i=lowTick; i <= highTick; ++i)
+      {
+        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
+        {
+          zeroLineIndex = i;
+          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
+          painter->drawLine(QLineF(mParentAxis->mAxisRect.left(), t, mParentAxis->mAxisRect.right(), t));
+          break;
+        }
+      }
+    }
+    // draw grid lines:
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    for (int i=lowTick; i <= highTick; ++i)
+    {
+      if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
+      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
+      painter->drawLine(QLineF(mParentAxis->mAxisRect.left(), t, mParentAxis->mAxisRect.right(), t));
+    }
+  }
+}
+
+/*! \internal
+  
+  Draws the sub grid lines with the specified painter.
+  
+  This is a helper function called by \ref draw.
+*/
+void QCPGrid::drawSubGridLines(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
+  double t; // helper variable, result of coordinate-to-pixel transforms
+  painter->setPen(mSubGridPen);
+  if (mParentAxis->orientation() == Qt::Horizontal)
+  {
+    for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
+    {
+      t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x
+      painter->drawLine(QLineF(t, mParentAxis->mAxisRect.bottom(), t, mParentAxis->mAxisRect.top()));
+    }
+  } else
+  {
+    for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
+    {
+      t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y
+      painter->drawLine(QLineF(mParentAxis->mAxisRect.left(), t, mParentAxis->mAxisRect.right(), t));
+    }
+  }
+}
+
+
+// ================================================================================
+// =================== QCPItemAnchor
+// ================================================================================
+
+/*! \class QCPItemAnchor
+  \brief An anchor of an item to which positions can be attached to.
+  
+  An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't
+  control anything on its item, but provides a way to tie other items via their positions to the
+  anchor.
+
+  For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight.
+  Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can
+  attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by
+  calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the
+  QCPItemRect. This way the start of the line will now always follow the respective anchor location
+  on the rect item.
+  
+  Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an
+  anchor to other positions.
+  
+  To learn how to provide anchors in your own item subclasses, see the subclassing section of the
+  QCPAbstractItem documentation.
+*/
+
+/*!
+  Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if
+  you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as
+  explained in the subclassing section of the QCPAbstractItem documentation.
+*/
+QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
+  mParentPlot(parentPlot),
+  mParentItem(parentItem),
+  mAnchorId(anchorId),
+  mName(name)
+{
+}
+
+QCPItemAnchor::~QCPItemAnchor()
+{
+  // unregister as parent at children:
+  QList<QCPItemPosition*> currentChildren(mChildren.toList());
+  for (int i=0; i<currentChildren.size(); ++i)
+    currentChildren.at(i)->setParentAnchor(0); // this acts back on this anchor and child removes itself from mChildren
+}
+
+/*!
+  Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
+  
+  The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the
+  parent item, QCPItemAnchor is just an intermediary.
+*/
+QPointF QCPItemAnchor::pixelPoint() const
+{
+  if (mParentItem)
+  {
+    if (mAnchorId > -1)
+    {
+      return mParentItem->anchorPixelPoint(mAnchorId);
+    } else
+    {
+      qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
+      return QPointF();
+    }
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "no parent item set";
+    return QPointF();
+  }
+}
+
+/*! \internal
+
+  Adds \a pos to the child list of this anchor. This is necessary to notify the children prior to
+  destruction of the anchor.
+  
+  Note that this function does not change the parent setting in \a pos.
+*/
+void QCPItemAnchor::addChild(QCPItemPosition *pos)
+{
+  if (!mChildren.contains(pos))
+    mChildren.insert(pos);
+  else
+    qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
+}
+
+/*! \internal
+
+  Removes \a pos from the child list of this anchor.
+  
+  Note that this function does not change the parent setting in \a pos.
+*/
+void QCPItemAnchor::removeChild(QCPItemPosition *pos)
+{
+  if (!mChildren.remove(pos))
+    qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
+}
+
+
+// ================================================================================
+// =================== QCPItemBracket
+// ================================================================================
+
+/*! \class QCPItemBracket
+  \brief A bracket for referencing/highlighting certain parts in the plot.
+
+  \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a left and \a right, which define the span of the bracket. If \a left is
+  actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the
+  example image.
+  
+  The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket
+  stretches away from the embraced span, can be controlled with \ref setLength.
+  
+  \image html QCPItemBracket-length.png
+  <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
+  bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
+  
+  It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine
+  or QCPItemCurve) or a text label (QCPItemText), to the bracket.
+*/
+
+/*!
+  Creates a bracket item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  left(createPosition("left")),
+  right(createPosition("right")),
+  center(createAnchor("center", aiCenter))
+{
+  left->setCoords(0, 0);
+  right->setCoords(1, 1);
+  
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue, 2));
+  setLength(8);
+  setStyle(bsCalligraphic);
+}
+
+QCPItemBracket::~QCPItemBracket()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the bracket.
+  
+  Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
+  stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
+  \ref setLength, which has a similar effect.
+  
+  \see setSelectedPen
+*/
+void QCPItemBracket::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the bracket when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemBracket::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
+  span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
+  
+  \image html QCPItemBracket-length.png
+  <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
+  bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
+*/
+void QCPItemBracket::setLength(double length)
+{
+  mLength = length;
+}
+
+/*!
+  Sets the style of the bracket, i.e. the shape/visual appearance.
+  
+  \see setPen
+*/
+void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
+{
+  mStyle = style;
+}
+
+/* inherits documentation from base class */
+double QCPItemBracket::selectTest(const QPointF &pos) const
+{
+  if (!mVisible)
+    return -1;
+  
+  QVector2D leftVec(left->pixelPoint());
+  QVector2D rightVec(right->pixelPoint());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return -1;
+  
+  QVector2D widthVec = (rightVec-leftVec)*0.5;
+  QVector2D lengthVec(-widthVec.y(), widthVec.x());
+  lengthVec = lengthVec.normalized()*mLength;
+  QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
+  
+  return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
+}
+
+/* inherits documentation from base class */
+void QCPItemBracket::draw(QCPPainter *painter)
+{
+  QVector2D leftVec(left->pixelPoint());
+  QVector2D rightVec(right->pixelPoint());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return;
+  
+  QVector2D widthVec = (rightVec-leftVec)*0.5;
+  QVector2D lengthVec(-widthVec.y(), widthVec.x());
+  lengthVec = lengthVec.normalized()*mLength;
+  QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
+
+  QPolygon boundingPoly;
+  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
+               << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
+  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
+  if (clip.intersects(boundingPoly.boundingRect()))
+  {
+    painter->setPen(mainPen());
+    switch (mStyle)
+    {
+      case bsSquare:
+      {
+        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
+        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
+        painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        break;
+      }
+      case bsRound:
+      {
+        painter->setBrush(Qt::NoBrush);
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+        path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        painter->drawPath(path);
+        break;
+      }
+      case bsCurly:
+      {
+        painter->setBrush(Qt::NoBrush);
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+        path.cubicTo((centerVec+widthVec*1-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+1*lengthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-0.4*widthVec+1*lengthVec).toPointF(), (centerVec-widthVec*1-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        painter->drawPath(path);
+        break;
+      }
+      case bsCalligraphic:
+      {
+        painter->setPen(Qt::NoPen);
+        painter->setBrush(QBrush(mainPen().color()));
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+        
+        path.cubicTo((centerVec+widthVec*1-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec*1-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        
+        path.cubicTo((centerVec-widthVec*1-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
+        path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec*1-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
+        
+        painter->drawPath(path);
+        break;
+      }
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
+{
+  QVector2D leftVec(left->pixelPoint());
+  QVector2D rightVec(right->pixelPoint());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return leftVec.toPointF();
+  
+  QVector2D widthVec = (rightVec-leftVec)*0.5;
+  QVector2D lengthVec(-widthVec.y(), widthVec.x());
+  lengthVec = lengthVec.normalized()*mLength;
+  QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
+  
+  switch (anchorId)
+  {
+    case aiCenter:
+      return centerVec.toPointF();
+  }
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemBracket::mainPen() const
+{
+    return mSelected ? mSelectedPen : mPen;
+}
+
+
+// ================================================================================
+// =================== QCPItemTracer
+// ================================================================================
+
+/*! \class QCPItemTracer
+  \brief Item that sticks to QCPGraph data points
+
+  \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt
+  the coordinate axes of the graph and update its \a position to be on the graph's data. This means
+  the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a
+  QCPGraph is connected, note that setting the coordinates directly via \a position will have no
+  effect, i.e. be overriden in the next redraw (this is when the coodinate update happens).
+  
+  If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will
+  stay at the respective end of the graph.
+  
+  With \ref setInterpolating you may specify whether the tracer may only stay exactly on data
+  points or whether it interpolates data points linearly, if given a key that lies between two data
+  points of the graph.
+  
+  The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer
+  have no own visual appearance (set the style to \ref tsNone), and just connect other item
+  positions to the tracer \a position (used as an anchor) via \ref
+  QCPItemPosition::setParentAnchor.
+  
+  \note The tracer position is only automatically updated upon redraws. This means when, for
+  example, the data of the graph changes and you immediately afterwards (without a redraw) read the
+  \a position coordinates of the tracer, they will not reflect the updated data of the graph. In
+  this case you should call \ref updatePosition manually, prior to reading the tracer coordinates.
+*/
+
+/*!
+  Creates a tracer item and sets default values.
+  
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  position(createPosition("position")),
+  mGraph(0)
+{
+  position->setCoords(0, 0);
+
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue, 2));
+  setStyle(tsCrosshair);
+  setSize(6);
+  setInterpolating(false);
+  setGraphKey(0);
+}
+
+QCPItemTracer::~QCPItemTracer()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the tracer
+  
+  \see setSelectedPen, setBrush
+*/
+void QCPItemTracer::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the tracer when selected
+  
+  \see setPen, setSelected
+*/
+void QCPItemTracer::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the brush that will be used to draw any fills of the tracer
+  
+  \see setSelectedBrush, setPen
+*/
+void QCPItemTracer::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the brush that will be used to draw any fills of the tracer, when selected.
+  
+  \see setBrush, setSelected
+*/
+void QCPItemTracer::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/*!
+  Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare
+  does, \ref tsCrosshair does not).
+*/
+void QCPItemTracer::setSize(double size)
+{
+  mSize = size;
+}
+
+/*!
+  Sets the style/visual appearance of the tracer.
+  
+  If you only want to use the tracer \a position as an anchor for other items, set \a style to
+  \ref tsNone.
+*/
+void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
+{
+  mStyle = style;
+}
+
+/*!
+  Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type
+  QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph.
+  
+  To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed
+  freely like any other item position. This is the state the tracer will assume when its graph gets
+  deleted while still attached to it.
+  
+  \see setGraphKey
+*/
+void QCPItemTracer::setGraph(QCPGraph *graph)
+{
+  if (graph)
+  {
+    if (graph->parentPlot() == mParentPlot)
+    {
+      position->setType(QCPItemPosition::ptPlotCoords);
+      position->setAxes(graph->keyAxis(), graph->valueAxis());
+      mGraph = graph;
+      updatePosition();
+    } else
+      qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
+  } else
+  {
+    mGraph = 0;
+  }
+}
+
+/*!
+  Sets the key of the graph's data point the tracer will be positioned at. This is the only free
+  cordinate of a tracer when attached to a graph.
+  
+  Depending on \ref setInterpolating, the tracer will be either positioned on the data point
+  closest to \a key, or will stay exactly at \a key and interpolate the value linearly.
+  
+  \see setGraph, setInterpolating
+*/
+void QCPItemTracer::setGraphKey(double key)
+{
+  mGraphKey = key;
+}
+
+/*!
+  Sets whether the value of the graph's data points shall be interpolated, when positioning the
+  tracer.
+  
+  If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on
+  the data point of the graph which is closest to the key, but which is not necessarily exactly
+  there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and
+  the appropriate value will be interpolated from the graph's data points linearly.
+  
+  \see setGraph, setGraphKey
+*/
+void QCPItemTracer::setInterpolating(bool enabled)
+{
+  mInterpolating = enabled;
+}
+
+/* inherits documentation from base class */
+double QCPItemTracer::selectTest(const QPointF &pos) const
+{
+  if (!mVisible || mStyle == tsNone)
+    return -1;
+
+  QPointF center(position->pixelPoint());
+  double w = mSize/2.0;
+  QRect clip = clipRect();
+  switch (mStyle)
+  {
+    case tsNone: return -1;
+    case tsPlus:
+    {
+      if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+        return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
+                          distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
+      break;
+    }
+    case tsCrosshair:
+    {
+      return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
+                        distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
+      break;
+    }
+    case tsCircle:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+      {
+        // distance to border:
+        double centerDist = QVector2D(center-pos).length();
+        double circleLine = w;
+        double result = qAbs(centerDist-circleLine);
+        // filled ellipse, allow click inside to count as hit:
+        if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
+        {
+          if (centerDist <= circleLine)
+            result = mParentPlot->selectionTolerance()*0.99;
+        }
+        return result;
+      }
+      break;
+    }
+    case tsSquare:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+      {
+        QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
+        bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
+        return rectSelectTest(rect, pos, filledRect);
+      }
+      break;
+    }
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPItemTracer::draw(QCPPainter *painter)
+{
+  updatePosition();
+  if (mStyle == tsNone)
+    return;
+
+  painter->setPen(mainPen());
+  painter->setBrush(mainBrush());
+  QPointF center(position->pixelPoint());
+  double w = mSize/2.0;
+  QRect clip = clipRect();
+  switch (mStyle)
+  {
+    case tsNone: return;
+    case tsPlus:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+      {
+        painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
+        painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
+      }
+      break;
+    }
+    case tsCrosshair:
+    {
+      if (center.y() > clip.top() && center.y() < clip.bottom())
+        painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
+      if (center.x() > clip.left() && center.x() < clip.right())
+        painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
+      break;
+    }
+    case tsCircle:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+        painter->drawEllipse(center, w, w);
+      break;
+    }
+    case tsSquare:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+        painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
+      break;
+    }
+  }
+}
+
+/*!
+  If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a
+  position to reside on the graph data, depending on the configured key (\ref setGraphKey).
+  
+  It is called automatically on every redraw and normally doesn't need to be called manually. One
+  exception is when you want to read the tracer coordinates via \a position and are not sure that
+  the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw.
+  In that situation, call this function before accessing \a position, to make sure you don't get
+  out-of-date coordinates.
+  
+  If there is no graph set on this tracer, this function does nothing.
+*/
+void QCPItemTracer::updatePosition()
+{
+  if (mGraph)
+  {
+    if (mParentPlot->hasPlottable(mGraph))
+    {
+      if (mGraph->data()->size() > 1)
+      {
+        QCPDataMap::const_iterator first = mGraph->data()->constBegin();
+        QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
+        if (mGraphKey < first.key())
+          position->setCoords(first.key(), first.value().value);
+        else if (mGraphKey > last.key())
+          position->setCoords(last.key(), last.value().value);
+        else
+        {
+          QCPDataMap::const_iterator it = first;
+          it = mGraph->data()->lowerBound(mGraphKey);
+          if (it != first) // mGraphKey is somewhere between iterators
+          {
+            QCPDataMap::const_iterator prevIt = it-1;
+            if (mInterpolating)
+            {
+              // interpolate between iterators around mGraphKey:
+              double slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
+              position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
+            } else
+            {
+              // find iterator with key closest to mGraphKey:
+              if (mGraphKey < (prevIt.key()+it.key())*0.5)
+                it = prevIt;
+              position->setCoords(it.key(), it.value().value);
+            }
+          } else // mGraphKey is exactly on first iterator
+            position->setCoords(it.key(), it.value().value);
+        }
+      } else if (mGraph->data()->size() == 1)
+      {
+        QCPDataMap::const_iterator it = mGraph->data()->constBegin();
+        position->setCoords(it.key(), it.value().value);
+      } else
+        qDebug() << Q_FUNC_INFO << "graph has no data";
+    } else
+      qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
+  }
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemTracer::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
+  is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPItemTracer::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+
+
+
+
+
+
diff --git a/src/plastimatch/standalone/qcustomplot.h b/src/plastimatch/standalone/qcustomplot.h
new file mode 100644
index 0000000..29b60f0
--- /dev/null
+++ b/src/plastimatch/standalone/qcustomplot.h
@@ -0,0 +1,2171 @@
+/***************************************************************************
+**                                                                        **
+**  QCustomPlot, a simple to use, modern plotting widget for Qt           **
+**  Copyright (C) 2012 Emanuel Eichhammer                                 **
+**                                                                        **
+**  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/.   **
+**                                                                        **
+****************************************************************************
+**           Author: Emanuel Eichhammer                                   **
+**  Website/Contact: http://www.WorksLikeClockwork.com/                   **
+**             Date: 09.06.12                                             **
+****************************************************************************/
+
+/*! \file */
+
+#ifndef QCUSTOMPLOT_H
+#define QCUSTOMPLOT_H
+
+#include <QObject>
+#include <QWidget>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QPixmap>
+#include <QVector>
+#include <QString>
+#include <QPrinter>
+#include <QDateTime>
+#include <QMultiMap>
+#include <QFlags>
+#include <QDebug>
+#include <QVector2D>
+#include <QStack>
+#include <qmath.h>
+#include <limits>
+
+// decl definitions for shared library compilation/usage:
+#if defined(QCUSTOMPLOT_COMPILE_LIBRARY)
+#  define QCP_LIB_DECL Q_DECL_EXPORT
+#elif defined(QCUSTOMPLOT_USE_LIBRARY)
+#  define QCP_LIB_DECL Q_DECL_IMPORT
+#else
+#  define QCP_LIB_DECL
+#endif
+
+class QCustomPlot;
+class QCPLegend;
+class QCPRange;
+class QCPLayerable;
+class QCPAbstractItem;
+class QCPItemPosition;
+class QCPAxis;
+class QCPData;
+
+/*!
+  The QCP Namespace contains general enums and QFlags 
+*/
+namespace QCP
+{
+/*!
+  Defines the symbol used for scatter points.
+  
+  On plottables/items that draw scatters, the sizes of these visualizations (with exception of \ref
+  QCP::ssDot and \ref QCP::ssPixmap) can be controlled with a \a setScatterSize function. Scatters
+  are in general drawn with the main pen set on the plottable/item.
+  
+  \see QCPGraph::setScatterStyle, QCPStatisticalBox::setOutlierStyle
+*/
+enum ScatterStyle { ssNone       ///< no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines)
+                    ,ssDot       ///< a single pixel
+                    ,ssCross     ///< a cross (x)
+                    ,ssPlus      ///< a plus (+)
+                    ,ssCircle    ///< a circle which is not filled
+                    ,ssDisc      ///< a circle which is filled with the color of the pen (not the brush!)
+                    ,ssSquare    ///< a square which is not filled
+                    ,ssDiamond   ///< a diamond which is not filled
+                    ,ssStar      ///< a star with eight arms, i.e. a combination of cross and plus
+                    ,ssTriangle  ///< an equilateral triangle which is not filled, standing on baseline
+                    ,ssTriangleInverted ///< an equilateral triangle which is not filled, standing on corner
+                    ,ssCrossSquare      ///< a square which is not filled, with a cross inside
+                    ,ssPlusSquare       ///< a square which is not filled, with a plus inside
+                    ,ssCrossCircle      ///< a circle which is not filled, with a cross inside
+                    ,ssPlusCircle       ///< a circle which is not filled, with a plus inside
+                    ,ssPeace     ///< a circle which is not filled, with one vertical and two downward diagonal lines
+                    ,ssPixmap    ///< a custom pixmap specified by setScatterPixmap, centered on the data point coordinates
+                  };
+
+/*!
+  Defines what elements of a plot can be forcibly drawn antialiased/not antialiased. If an
+  element is neither forcibly drawn antialiased nor forcibly drawn not antialiased, it is up to
+  the respective element how it is drawn. Typically it provides a \a setAntialiased function for
+  this.
+  
+  \c AntialiasedElements is a flag of or-combined elements of this enum type.
+  
+  \see QCustomPlot::setAntialiasedElements, QCustomPlot::setNotAntialiasedElements
+*/
+enum AntialiasedElement { aeAxes           = 0x0001 ///< <tt>0x0001</tt> Axis base line and tick marks
+                          ,aeGrid          = 0x0002 ///< <tt>0x0002</tt> Grid lines
+                          ,aeSubGrid       = 0x0004 ///< <tt>0x0004</tt> Sub grid lines
+                          ,aeLegend        = 0x0008 ///< <tt>0x0008</tt> Legend box
+                          ,aeLegendItems   = 0x0010 ///< <tt>0x0010</tt> Legend items
+                          ,aePlottables    = 0x0020 ///< <tt>0x0020</tt> Main lines of plottables (excluding error bars, see element \ref aeErrorBars)
+                          ,aeItems         = 0x0040 ///< <tt>0x0040</tt> Main lines of items
+                          ,aeScatters      = 0x0080 ///< <tt>0x0080</tt> Scatter symbols of plottables (excluding scatter symbols of type ssPixmap)
+                          ,aeErrorBars     = 0x0100 ///< <tt>0x0100</tt> Error bars
+                          ,aeFills         = 0x0200 ///< <tt>0x0200</tt> Borders of fills (e.g. under or between graphs)
+                          ,aeZeroLine      = 0x0400 ///< <tt>0x0400</tt> Zero-lines, see \ref QCPAxis::setZeroLinePen
+                          ,aeAll           = 0xFFFF ///< <tt>0xFFFF</tt> All elements
+                          ,aeNone          = 0x0000 ///< <tt>0x0000</tt> No elements
+                        }; 
+Q_DECLARE_FLAGS(AntialiasedElements, AntialiasedElement)
+
+/*!
+  Defines plotting hints that control various aspects of the quality and speed of plotting.
+  \see QCustomPlot::setPlottingHints
+*/
+enum PlottingHint { phNone            = 0x000 ///< <tt>0x000</tt> No hints are set
+                    ,phFastPolylines  = 0x001 ///< <tt>0x001</tt> Graph/Curve lines are drawn with a faster method. This reduces the quality
+                                              ///<                especially of the line segment joins. (Only used for solid line pens.)
+                    ,phForceRepaint   = 0x002 ///< <tt>0x002</tt> causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called. This is set by default
+                                              ///<                on Windows-Systems to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).
+                  };
+Q_DECLARE_FLAGS(PlottingHints, PlottingHint)
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::AntialiasedElements)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::PlottingHints)
+
+class QCP_LIB_DECL QCPData
+{
+public:
+  QCPData();
+  QCPData(double key, double value);
+  double key, value;
+  double keyErrorPlus, keyErrorMinus;
+  double valueErrorPlus, valueErrorMinus;
+};
+Q_DECLARE_TYPEINFO(QCPData, Q_MOVABLE_TYPE);
+
+/*! \typedef QCPDataMap
+  Container for storing QCPData items in a sorted fashion. The key of the map
+  is the key member of the QCPData instance.
+  
+  This is the container in which QCPGraph holds its data.
+  \see QCPData, QCPGraph::setData
+*/
+typedef QMap<double, QCPData> QCPDataMap;
+typedef QMapIterator<double, QCPData> QCPDataMapIterator;
+typedef QMutableMapIterator<double, QCPData> QCPDataMutableMapIterator;
+
+class QCP_LIB_DECL QCPCurveData
+{
+public:
+  QCPCurveData();
+  QCPCurveData(double t, double key, double value);
+  double t, key, value;
+};
+Q_DECLARE_TYPEINFO(QCPCurveData, Q_MOVABLE_TYPE);
+
+/*! \typedef QCPCurveDataMap
+  Container for storing QCPCurveData items in a sorted fashion. The key of the map
+  is the t member of the QCPCurveData instance.
+  
+  This is the container in which QCPCurve holds its data.
+  \see QCPCurveData, QCPCurve::setData
+*/
+
+typedef QMap<double, QCPCurveData> QCPCurveDataMap;
+typedef QMapIterator<double, QCPCurveData> QCPCurveDataMapIterator;
+typedef QMutableMapIterator<double, QCPCurveData> QCPCurveDataMutableMapIterator;
+
+class QCP_LIB_DECL QCPBarData
+{
+public:
+  QCPBarData();
+  QCPBarData(double key, double value);
+  double key, value;
+};
+Q_DECLARE_TYPEINFO(QCPBarData, Q_MOVABLE_TYPE);
+
+/*! \typedef QCPBarDataMap
+  Container for storing QCPBarData items in a sorted fashion. The key of the map
+  is the key member of the QCPBarData instance.
+  
+  This is the container in which QCPBars holds its data.
+  \see QCPBarData, QCPBars::setData
+*/
+typedef QMap<double, QCPBarData> QCPBarDataMap;
+typedef QMapIterator<double, QCPBarData> QCPBarDataMapIterator;
+typedef QMutableMapIterator<double, QCPBarData> QCPBarDataMutableMapIterator;
+
+class QCP_LIB_DECL QCPPainter : public QPainter
+{
+public:
+  QCPPainter();
+  QCPPainter(QPaintDevice *device);
+  ~QCPPainter();
+  
+  // getters:
+  QPixmap scatterPixmap() const { return mScatterPixmap; }
+  bool antialiasing() const { return testRenderHint(QPainter::Antialiasing); }
+  bool pdfExportMode() const { return mPdfExportMode; }
+  bool scaledExportMode() const { return mScaledExportMode; }
+  
+  // setters:
+  void setScatterPixmap(const QPixmap pm);
+  void setAntialiasing(bool enabled);
+  void setPdfExportMode(bool enabled);
+  void setScaledExportMode(bool enabled);
+ 
+  // methods hiding non-virtual base class functions (QPainter bug workarounds):
+  void setPen(const QPen &pen);
+  void setPen(const QColor &color);
+  void setPen(Qt::PenStyle penStyle);
+  void drawLine(const QLineF &line);
+  void drawLine(const QPointF &p1, const QPointF &p2) {drawLine(QLineF(p1, p2));}
+  void save();
+  void restore();
+
+  // helpers:
+  void fixScaledPen();
+  void drawScatter(double x, double y, double size, QCP::ScatterStyle style);
+  
+protected:
+  QPixmap mScatterPixmap;
+  bool mScaledExportMode;
+  bool mPdfExportMode;
+  bool mIsAntialiasing;
+  QStack<bool> mAntialiasingStack;
+};
+
+class QCP_LIB_DECL QCPLineEnding
+{
+public:
+  /*!
+    Defines the type of ending decoration for line-like items, e.g. an arrow.
+    
+    \image html QCPLineEnding.png
+    
+    The width and length of these decorations can be controlled with the functions \ref setWidth
+    and \ref setLength. Some decorations like \ref esDisc, \ref esSquare, \ref esDiamond and \ref esBar only
+    support a width, the length property is ignored.
+    
+    \see QCPItemLine::setHead, QCPItemLine::setTail, QCPItemCurve::setHead, QCPItemCurve::setTail
+  */
+  enum EndingStyle { esNone          ///< No ending decoration
+                     ,esFlatArrow    ///< A filled arrow head with a straight/flat back (a triangle)
+                     ,esSpikeArrow   ///< A filled arrow head with an indented back
+                     ,esLineArrow    ///< A non-filled arrow head with open back
+                     ,esDisc         ///< A filled circle
+                     ,esSquare       ///< A filled square
+                     ,esDiamond      ///< A filled diamond (45� rotated square)
+                     ,esBar          ///< A bar perpendicular to the line
+                   };
+  
+  QCPLineEnding();
+  QCPLineEnding(EndingStyle style, double width=8, double length=10, bool inverted=false);
+  
+  // getters:
+  EndingStyle style() const { return mStyle; }
+  double width() const { return mWidth; }
+  double length() const { return mLength; }
+  bool inverted() const { return mInverted; }
+  
+  // setters:
+  void setStyle(EndingStyle style);
+  void setWidth(double width);
+  void setLength(double length);
+  void setInverted(bool inverted);
+  
+  // non-property methods:
+  double boundingDistance() const;
+  void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const;
+  void draw(QCPPainter *painter, const QVector2D &pos, double angle) const;
+  
+protected:
+  EndingStyle mStyle;
+  double mWidth, mLength;
+  bool mInverted;
+};
+Q_DECLARE_TYPEINFO(QCPLineEnding, Q_MOVABLE_TYPE);
+
+class QCP_LIB_DECL QCPLayer
+{
+public:
+  QCPLayer(QCustomPlot* parentPlot, const QString &layerName);
+  ~QCPLayer();
+  
+  // getters:
+  QCustomPlot *parentPlot() const { return mParentPlot; }
+  QString name() const { return mName; }
+  int index() const;
+  QList<QCPLayerable*> children() const { return mChildren; }
+  
+protected:
+  QCustomPlot *mParentPlot;
+  QString mName;
+  QList<QCPLayerable*> mChildren;
+  
+  void addChild(QCPLayerable *layerable, bool prepend);
+  void removeChild(QCPLayerable *layerable);
+  
+private:
+  Q_DISABLE_COPY(QCPLayer)
+  
+  friend class QCPLayerable;
+};
+
+class QCP_LIB_DECL QCPLayerable : public QObject
+{
+  Q_OBJECT
+public:
+  QCPLayerable(QCustomPlot *parentPlot);
+  ~QCPLayerable();
+  
+  // getters:
+  bool visible() const { return mVisible; }
+  QCustomPlot *parentPlot() const { return mParentPlot; }
+  QCPLayer *layer() const { return mLayer; }
+  bool antialiased() const { return mAntialiased; }
+  
+  // setters:
+  void setVisible(bool on);
+  bool setLayer(QCPLayer *layer);
+  bool setLayer(const QString &layerName);
+  void setAntialiased(bool enabled);
+  
+protected:
+  bool mVisible;
+  QCustomPlot *mParentPlot;
+  QCPLayer *mLayer;
+  bool mAntialiased;
+  
+  // non-property methods:
+  bool moveToLayer(QCPLayer *layer, bool prepend);
+  
+  void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const;
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const = 0;
+  virtual QRect clipRect() const;
+  virtual void draw(QCPPainter *painter) = 0;
+  
+private:
+  Q_DISABLE_COPY(QCPLayerable)
+  
+  friend class QCustomPlot;
+};
+
+class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable
+{
+  Q_OBJECT
+public:
+  QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPAbstractPlottable() {}
+  
+  // getters:
+  QString name() const { return mName; }
+  bool antialiasedFill() const { return mAntialiasedFill; }
+  bool antialiasedScatters() const { return mAntialiasedScatters; }
+  bool antialiasedErrorBars() const { return mAntialiasedErrorBars; }
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  QCPAxis *keyAxis() const { return mKeyAxis; }
+  QCPAxis *valueAxis() const { return mValueAxis; }
+  bool selectable() const { return mSelectable; }
+  bool selected() const { return mSelected; }
+  
+  // setters:
+  void setName(const QString &name);
+  void setAntialiasedFill(bool enabled);
+  void setAntialiasedScatters(bool enabled);
+  void setAntialiasedErrorBars(bool enabled);
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+  void setKeyAxis(QCPAxis *axis);
+  void setValueAxis(QCPAxis *axis);
+  void setSelectable(bool selectable);
+  void setSelected(bool selected);
+
+  // non-property methods:
+  void rescaleAxes(bool onlyEnlarge=false) const;
+  void rescaleKeyAxis(bool onlyEnlarge=false) const;
+  void rescaleValueAxis(bool onlyEnlarge=false) const;
+  virtual void clearData() = 0;
+  virtual double selectTest(const QPointF &pos) const = 0;
+  virtual bool addToLegend();
+  virtual bool removeFromLegend() const;
+  
+signals:
+  void selectionChanged(bool selected);
+  
+protected:
+  /*!
+    Represents negative and positive sign domain for passing to \ref getKeyRange and \ref getValueRange.
+  */
+  enum SignDomain { sdNegative  ///< The negative sign domain, i.e. numbers smaller than zero
+                    ,sdBoth     ///< Both sign domains, including zero, i.e. all (rational) numbers
+                    ,sdPositive ///< The positive sign domain, i.e. numbers greater than zero
+                  };
+  QString mName;
+  bool mAntialiasedFill, mAntialiasedScatters, mAntialiasedErrorBars;
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+  QCPAxis *mKeyAxis, *mValueAxis;
+  bool mSelected, mSelectable;
+  
+  virtual QRect clipRect() const;
+  virtual void draw(QCPPainter *painter) = 0;
+  virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0;
+  virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const = 0;
+  virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const = 0;
+  
+  // painting and coordinate transformation helpers:
+  void coordsToPixels(double key, double value, double &x, double &y) const;
+  const QPointF coordsToPixels(double key, double value) const;
+  void pixelsToCoords(double x, double y, double &key, double &value) const;
+  void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const;
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+  void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  void applyFillAntialiasingHint(QCPPainter *painter) const;
+  void applyScattersAntialiasingHint(QCPPainter *painter) const;
+  void applyErrorBarsAntialiasingHint(QCPPainter *painter) const;
+  
+  // selection test helpers:
+  double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const;
+
+private:
+  Q_DISABLE_COPY(QCPAbstractPlottable)
+  
+  friend class QCustomPlot;
+  friend class QCPPlottableLegendItem;
+};
+
+class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable
+{
+  Q_OBJECT
+public:
+  /*!
+    Defines how the graph's line is represented visually in the plot. The line is drawn with the
+    current pen of the graph (\ref setPen).
+    \see setLineStyle
+  */
+  enum LineStyle { lsNone        ///< data points are not connected with any lines (e.g. data only represented
+                                 ///< with symbols according to the scatter style, see \ref setScatterStyle)
+                  ,lsLine        ///< data points are connected by a straight line
+                  ,lsStepLeft    ///< line is drawn as steps where the step height is the value of the left data point
+                  ,lsStepRight   ///< line is drawn as steps where the step height is the value of the right data point
+                  ,lsStepCenter  ///< line is drawn as steps where the step is in between two data points
+                  ,lsImpulse     ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line
+                 };
+  Q_ENUMS(LineStyle)
+  /*!
+    Defines what kind of error bars are drawn for each data point
+  */
+  enum ErrorType { etNone   ///< No error bars are shown
+                  ,etKey    ///< Error bars for the key dimension of the data point are shown
+                  ,etValue  ///< Error bars for the value dimension of the data point are shown
+                  ,etBoth   ///< Error bars for both key and value dimensions of the data point are shown
+                 };
+  Q_ENUMS(ErrorType)
+  
+  explicit QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPGraph();
+  
+  // getters:
+  const QCPDataMap *data() const { return mData; }
+  LineStyle lineStyle() const { return mLineStyle; }
+  QCP::ScatterStyle scatterStyle() const { return mScatterStyle; }
+  double scatterSize() const { return mScatterSize; }
+  const QPixmap scatterPixmap() const { return mScatterPixmap; }
+  ErrorType errorType() const { return mErrorType; }
+  QPen errorPen() const { return mErrorPen; }
+  double errorBarSize() const { return mErrorBarSize; }
+  bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; }
+  QCPGraph *channelFillGraph() const { return mChannelFillGraph; }
+  
+  // setters:
+  void setData(QCPDataMap *data, bool copy=false);
+  void setData(const QVector<double> &key, const QVector<double> &value);
+  void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError);
+  void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus);
+  void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError);
+  void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus);
+  void setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError);
+  void setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus);
+  void setLineStyle(LineStyle ls);
+  void setScatterStyle(QCP::ScatterStyle ss);
+  void setScatterSize(double size);
+  void setScatterPixmap(const QPixmap &pixmap);
+  void setErrorType(ErrorType errorType);
+  void setErrorPen(const QPen &pen);
+  void setErrorBarSize(double size);
+  void setErrorBarSkipSymbol(bool enabled);
+  void setChannelFillGraph(QCPGraph *targetGraph);
+  
+  // non-property methods:
+  void addData(const QCPDataMap &dataMap);
+  void addData(const QCPData &data);
+  void addData(double key, double value);
+  void addData(const QVector<double> &keys, const QVector<double> &values);
+  void removeDataBefore(double key);
+  void removeDataAfter(double key);
+  void removeData(double fromKey, double toKey);
+  void removeData(double key);
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos) const;
+  using QCPAbstractPlottable::rescaleAxes;
+  using QCPAbstractPlottable::rescaleKeyAxis;
+  using QCPAbstractPlottable::rescaleValueAxis;
+  virtual void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
+  virtual void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
+  virtual void rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
+  
+protected:
+  QCPDataMap *mData;
+  QPen mErrorPen;
+  LineStyle mLineStyle;
+  QCP::ScatterStyle mScatterStyle;
+  double mScatterSize;
+  QPixmap mScatterPixmap;
+  ErrorType mErrorType;
+  double mErrorBarSize;
+  bool mErrorBarSkipSymbol;
+  QCPGraph *mChannelFillGraph;
+
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const;
+
+  // functions to generate plot data points in pixel coordinates:
+  void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
+  // plot style specific functions to generate plot data, used by getPlotData:
+  void getScatterPlotData(QVector<QCPData> *pointData) const;
+  void getLinePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
+  void getStepLeftPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
+  void getStepRightPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
+  void getStepCenterPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
+  void getImpulsePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
+  
+  // helper functions for drawing:
+  void drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const;
+  void drawScatterPlot(QCPPainter *painter, QVector<QCPData> *pointData) const;
+  void drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
+  void drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
+  void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const;
+  
+  // helper functions:
+  void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper, int &count) const;
+  void addFillBasePoints(QVector<QPointF> *lineData) const;
+  void removeFillBasePoints(QVector<QPointF> *lineData) const;
+  QPointF lowerFillBasePoint(double lowerKey) const;
+  QPointF upperFillBasePoint(double upperKey) const;
+  const QPolygonF getChannelFillPolygon(const QVector<QPointF> *lineData) const;
+  int findIndexBelowX(const QVector<QPointF> *data, double x) const;
+  int findIndexAboveX(const QVector<QPointF> *data, double x) const;
+  int findIndexBelowY(const QVector<QPointF> *data, double y) const;
+  int findIndexAboveY(const QVector<QPointF> *data, double y) const;
+  double pointDistance(const QPointF &pixelPoint) const;
+  virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
+  virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
+  
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+class QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable
+{
+  Q_OBJECT
+public:
+  /*!
+    Defines how the curve's line is represented visually in the plot. The line is drawn with the
+    current pen of the curve (\ref setPen).
+    \see setLineStyle
+  */
+  enum LineStyle { lsNone, ///< No line is drawn between data points (e.g. only scatters)
+                   lsLine  ///< Data points are connected with a straight line
+                 };
+  explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPCurve();
+  
+  // getters:
+  const QCPCurveDataMap *data() const { return mData; }
+  QCP::ScatterStyle scatterStyle() const { return mScatterStyle; }
+  double scatterSize() const { return mScatterSize; }
+  QPixmap scatterPixmap() const { return mScatterPixmap; }
+  LineStyle lineStyle() const { return mLineStyle; }
+  
+  // setters:
+  void setData(QCPCurveDataMap *data, bool copy=false);
+  void setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value);
+  void setData(const QVector<double> &key, const QVector<double> &value);
+  void setScatterStyle(QCP::ScatterStyle style);
+  void setScatterSize(double size);
+  void setScatterPixmap(const QPixmap &pixmap);
+  void setLineStyle(LineStyle style);
+  
+  // non-property methods:
+  void addData(const QCPCurveDataMap &dataMap);
+  void addData(const QCPCurveData &data);
+  void addData(double t, double key, double value);
+  void addData(double key, double value);
+  void addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values);
+  void removeDataBefore(double t);
+  void removeDataAfter(double t);
+  void removeData(double fromt, double tot);
+  void removeData(double t);
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos) const;
+  
+protected:
+  QCPCurveDataMap *mData;
+  QCP::ScatterStyle mScatterStyle;
+  double mScatterSize;
+  QPixmap mScatterPixmap;
+  LineStyle mLineStyle;
+  
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const;
+  // drawing helpers:
+  virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const;
+  
+  // helper functions:
+  void getCurveData(QVector<QPointF> *lineData) const;
+  double pointDistance(const QPointF &pixelPoint) const;
+
+  QPointF outsideCoordsToPixels(double key, double value, int region) const;
+  virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
+  
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+class QCP_LIB_DECL QCPBars : public QCPAbstractPlottable
+{
+  Q_OBJECT
+public:
+  explicit QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPBars();
+  
+  // getters:
+  double width() const { return mWidth; }
+  QCPBars *barBelow() const { return mBarBelow; }
+  QCPBars *barAbove() const { return mBarAbove; }
+  const QCPBarDataMap *data() const { return mData; }
+  
+  // setters:
+  void setWidth(double width);
+  void setData(QCPBarDataMap *data, bool copy=false);
+  void setData(const QVector<double> &key, const QVector<double> &value);
+  
+  // non-property methods:
+  void moveBelow(QCPBars *bars);
+  void moveAbove(QCPBars *bars);
+  void addData(const QCPBarDataMap &dataMap);
+  void addData(const QCPBarData &data);
+  void addData(double key, double value);
+  void addData(const QVector<double> &keys, const QVector<double> &values);
+  void removeDataBefore(double key);
+  void removeDataAfter(double key);
+  void removeData(double fromKey, double toKey);
+  void removeData(double key);
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos) const;
+  
+protected:
+  QCPBarDataMap *mData;
+  double mWidth;
+  QCPBars *mBarBelow, *mBarAbove;
+  
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const;
+  
+  QPolygonF getBarPolygon(double key, double value) const;
+  double getBaseValue(double key, bool positive) const;
+  static void connectBars(QCPBars* lower, QCPBars* upper);
+  virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
+  
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+class QCP_LIB_DECL QCPStatisticalBox : public QCPAbstractPlottable
+{
+  Q_OBJECT
+public:
+  explicit QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPStatisticalBox();
+  
+  // getters:
+  double key() const { return mKey; }
+  double minimum() const { return mMinimum; }
+  double lowerQuartile() const { return mLowerQuartile; }
+  double median() const { return mMedian; }
+  double upperQuartile() const { return mUpperQuartile; }
+  double maximum() const { return mMaximum; }
+  QVector<double> outliers() const { return mOutliers; }
+  double width() const { return mWidth; }
+  double whiskerWidth() const { return mWhiskerWidth; }
+  QPen whiskerPen() const { return mWhiskerPen; }
+  QPen whiskerBarPen() const { return mWhiskerBarPen; }
+  QPen medianPen() const { return mMedianPen; }
+  double outlierSize() const { return mOutlierSize; }
+  QPen outlierPen() const { return mOutlierPen; }
+  QCP::ScatterStyle outlierStyle() const { return mOutlierStyle; }
+
+  // setters:
+  void setKey(double key);
+  void setMinimum(double value);
+  void setLowerQuartile(double value);
+  void setMedian(double value);
+  void setUpperQuartile(double value);
+  void setMaximum(double value);
+  void setOutliers(const QVector<double> &values);
+  void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum);
+  void setWidth(double width);
+  void setWhiskerWidth(double width);
+  void setWhiskerPen(const QPen &pen);
+  void setWhiskerBarPen(const QPen &pen);
+  void setMedianPen(const QPen &pen);
+  void setOutlierSize(double pixels);
+  void setOutlierPen(const QPen &pen);
+  void setOutlierStyle(QCP::ScatterStyle style);
+  
+  // non-property methods:
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos) const;
+  
+protected:
+  QVector<double> mOutliers;
+  double mKey, mMinimum, mLowerQuartile, mMedian, mUpperQuartile, mMaximum;
+  double mWidth;
+  double mWhiskerWidth;
+  double mOutlierSize;
+  QPen mWhiskerPen, mWhiskerBarPen, mOutlierPen, mMedianPen;
+  QCP::ScatterStyle mOutlierStyle;
+  
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const;
+  
+  virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const;
+  virtual void drawMedian(QCPPainter *painter) const;
+  virtual void drawWhiskers(QCPPainter *painter) const;
+  virtual void drawOutliers(QCPPainter *painter) const;
+  virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
+  
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+class QCP_LIB_DECL QCPItemAnchor
+{
+public:
+  QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId=-1);
+  virtual ~QCPItemAnchor();
+  
+  QString name() const { return mName; }
+  virtual QPointF pixelPoint() const;
+  
+protected:
+  QCustomPlot *mParentPlot;
+  QCPAbstractItem *mParentItem;
+  int mAnchorId;
+  QString mName;
+  // non-property members:
+  QSet<QCPItemPosition*> mChildren;
+  
+  void addChild(QCPItemPosition* pos); // called from pos when this anchor is set as parent
+  void removeChild(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted
+  
+private:
+  Q_DISABLE_COPY(QCPItemAnchor)
+  
+  friend class QCPItemPosition;
+};
+
+class QCP_LIB_DECL QCPItemPosition : public QCPItemAnchor
+{
+public:
+  /*!
+    Defines the ways an item position can be specified. Thus it defines what the numbers passed to
+    \ref setCoords actually mean.
+    
+    \see setType
+  */
+  enum PositionType { ptAbsolute        ///< Static positioning in pixels, starting from the top left corner of the viewport/widget.
+                      ,ptViewportRatio  ///< Static positioning given by a ratio of the current viewport (coordinates 0 to 1).
+                      ,ptAxisRectRatio  ///< Static positioning given by a ratio of the current axis rect (coordinates 0 to 1).
+                      ,ptPlotCoords     ///< Dynamic positioning at a plot coordinate defined by two axes (see \ref setAxes).
+                    };
+  
+  QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name);
+  virtual ~QCPItemPosition();
+  
+  // getters:
+  PositionType type() const { return mPositionType; }
+  QCPItemAnchor *parentAnchor() const { return mParentAnchor; }
+  double key() const { return mKey; }
+  double value() const { return mValue; }
+  QPointF coords() const { return QPointF(mKey, mValue); }
+  QCPAxis *keyAxis() const { return mKeyAxis; }
+  QCPAxis *valueAxis() const { return mValueAxis; }
+  virtual QPointF pixelPoint() const;
+  
+  // setters:
+  void setType(PositionType type);
+  bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);
+  void setCoords(double key, double value);
+  void setCoords(const QPointF &coords);
+  void setAxes(QCPAxis* keyAxis, QCPAxis* valueAxis);
+  void setPixelPoint(const QPointF &pixelPoint);
+  
+protected:
+  PositionType mPositionType;
+  QCPAxis *mKeyAxis, *mValueAxis;
+  double mKey, mValue;
+  QCPItemAnchor *mParentAnchor;
+  
+private:
+  Q_DISABLE_COPY(QCPItemPosition)
+  
+};
+
+class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable
+{
+  Q_OBJECT
+public:
+  QCPAbstractItem(QCustomPlot *parentPlot);
+  virtual ~QCPAbstractItem();
+  
+  // getters:
+  bool clipToAxisRect() const { return mClipToAxisRect; }
+  QCPAxis *clipKeyAxis() const { return mClipKeyAxis; }
+  QCPAxis *clipValueAxis() const { return mClipValueAxis; }
+  bool selectable() const { return mSelectable; }
+  bool selected() const { return mSelected; }
+  
+  // setters:
+  void setClipToAxisRect(bool clip);
+  void setClipAxes(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  void setClipKeyAxis(QCPAxis *axis);
+  void setClipValueAxis(QCPAxis *axis);
+  void setSelectable(bool selectable);
+  void setSelected(bool selected);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const = 0;
+  QList<QCPItemPosition*> positions() const { return mPositions; }
+  QList<QCPItemAnchor*> anchors() const { return mAnchors; }
+  QCPItemPosition *position(const QString &name) const;
+  QCPItemAnchor *anchor(const QString &name) const;
+  bool hasAnchor(const QString &name) const;
+  
+protected:
+  bool mClipToAxisRect;
+  QCPAxis *mClipKeyAxis, *mClipValueAxis;
+  bool mSelectable, mSelected;
+  QList<QCPItemPosition*> mPositions;
+  QList<QCPItemAnchor*> mAnchors;
+  
+  virtual QRect clipRect() const;
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter) = 0;
+  
+  // helper functions for subclasses:
+  double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const;
+  double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const;
+  
+  // anchor/position interface:
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+  QCPItemPosition *createPosition(const QString &name);
+  QCPItemAnchor *createAnchor(const QString &name, int anchorId);
+  
+signals:
+  void selectionChanged(bool selected);
+  
+private:
+  Q_DISABLE_COPY(QCPAbstractItem)
+  
+  friend class QCustomPlot;
+  friend class QCPItemAnchor;
+};
+
+class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  QCPItemStraightLine(QCustomPlot *parentPlot);
+  virtual ~QCPItemStraightLine();
+  
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  
+  QCPItemPosition * const point1;
+  QCPItemPosition * const point2;
+  
+protected:
+  QPen mPen, mSelectedPen;
+  
+  virtual void draw(QCPPainter *painter);
+  
+  // helper functions:
+  double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const;
+  QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const;
+  QPen mainPen() const;
+};
+
+class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  QCPItemLine(QCustomPlot *parentPlot);
+  virtual ~QCPItemLine();
+  
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QCPLineEnding head() const { return mHead; }
+  QCPLineEnding tail() const { return mTail; }
+  
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setHead(const QCPLineEnding &head);
+  void setTail(const QCPLineEnding &tail);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  
+  QCPItemPosition * const start;
+  QCPItemPosition * const end;
+  
+protected:
+  QPen mPen, mSelectedPen;
+  QCPLineEnding mHead, mTail;
+  
+  virtual void draw(QCPPainter *painter);
+  
+  // helper functions:
+  QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const;
+  QPen mainPen() const;
+};
+
+class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  QCPItemEllipse(QCustomPlot *parentPlot);
+  virtual ~QCPItemEllipse();
+  
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  
+  QCPItemPosition * const topLeft;
+  QCPItemPosition * const bottomRight;
+  QCPItemAnchor * const topLeftRim;
+  QCPItemAnchor * const top;
+  QCPItemAnchor * const topRightRim;
+  QCPItemAnchor * const right;
+  QCPItemAnchor * const bottomRightRim;
+  QCPItemAnchor * const bottom;
+  QCPItemAnchor * const bottomLeftRim;
+  QCPItemAnchor * const left;
+  
+protected:
+  enum AnchorIndex {aiTopLeftRim, aiTop, aiTopRightRim, aiRight, aiBottomRightRim, aiBottom, aiBottomLeftRim, aiLeft};
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+  
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+  
+  // helper functions:
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+};
+
+class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  QCPItemRect(QCustomPlot *parentPlot);
+  virtual ~QCPItemRect();
+  
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  
+  QCPItemPosition * const topLeft;
+  QCPItemPosition * const bottomRight;
+  QCPItemAnchor * const top;
+  QCPItemAnchor * const topRight;
+  QCPItemAnchor * const right;
+  QCPItemAnchor * const bottom;
+  QCPItemAnchor * const bottomLeft;
+  QCPItemAnchor * const left;
+  
+protected:
+  enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+  
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+  
+  // helper functions:
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+};
+
+class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  QCPItemPixmap(QCustomPlot *parentPlot);
+  virtual ~QCPItemPixmap();
+  
+  // getters:
+  QPixmap pixmap() const { return mPixmap; }
+  bool scaled() const { return mScaled; }
+  Qt::AspectRatioMode aspectRatioMode() const { return mAspectRatioMode; }
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  
+  // setters;
+  void setPixmap(const QPixmap &pixmap);
+  void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio);
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  
+  QCPItemPosition * const topLeft;
+  QCPItemPosition * const bottomRight;
+  QCPItemAnchor * const top;
+  QCPItemAnchor * const topRight;
+  QCPItemAnchor * const right;
+  QCPItemAnchor * const bottom;
+  QCPItemAnchor * const bottomLeft;
+  QCPItemAnchor * const left;
+  
+protected:
+  enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};
+  QPixmap mPixmap;
+  QPixmap mScaledPixmap;
+  bool mScaled;
+  Qt::AspectRatioMode mAspectRatioMode;
+  QPen mPen, mSelectedPen;
+  
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+  
+  // helper functions:
+  void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false);
+  QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const;
+  QPen mainPen() const;
+};
+
+class QCP_LIB_DECL QCPItemText : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  QCPItemText(QCustomPlot *parentPlot);
+  virtual ~QCPItemText();
+  
+  // getters:
+  QColor color() const { return mColor; }
+  QColor selectedColor() const { return mSelectedColor; }
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  QFont font() const { return mFont; }
+  QFont selectedFont() const { return mSelectedFont; }
+  QString text() const { return mText; }
+  Qt::Alignment positionAlignment() const { return mPositionAlignment; }
+  Qt::Alignment textAlignment() const { return mTextAlignment; }
+  double rotation() const { return mRotation; }
+  QMargins padding() const { return mPadding; }
+  
+  // setters;
+  void setColor(const QColor &color);
+  void setSelectedColor(const QColor &color);
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+  void setFont(const QFont &font);
+  void setSelectedFont(const QFont &font);
+  void setText(const QString &text);
+  void setPositionAlignment(Qt::Alignment alignment);
+  void setTextAlignment(Qt::Alignment alignment);
+  void setRotation(double degrees);
+  void setPadding(const QMargins &padding);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  
+  QCPItemPosition * const position;
+  QCPItemAnchor * const topLeft;
+  QCPItemAnchor * const top;
+  QCPItemAnchor * const topRight;
+  QCPItemAnchor * const right;
+  QCPItemAnchor * const bottomRight;
+  QCPItemAnchor * const bottom;
+  QCPItemAnchor * const bottomLeft;
+  QCPItemAnchor * const left;
+  
+protected:
+  enum AnchorIndex {aiTopLeft, aiTop, aiTopRight, aiRight, aiBottomRight, aiBottom, aiBottomLeft, aiLeft};
+  QColor mColor, mSelectedColor;
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+  QFont mFont, mSelectedFont;
+  QString mText;
+  Qt::Alignment mPositionAlignment;
+  Qt::Alignment mTextAlignment;
+  double mRotation;
+  QMargins mPadding;
+  
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+  
+  // helper functions:
+  QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const;
+  QFont mainFont() const;
+  QColor mainColor() const;
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+};
+
+class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  QCPItemCurve(QCustomPlot *parentPlot);
+  virtual ~QCPItemCurve();
+  
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QCPLineEnding head() const { return mHead; }
+  QCPLineEnding tail() const { return mTail; }
+  
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setHead(const QCPLineEnding &head);
+  void setTail(const QCPLineEnding &tail);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  
+  QCPItemPosition * const start;
+  QCPItemPosition * const startDir;
+  QCPItemPosition * const endDir;
+  QCPItemPosition * const end;
+  
+protected:
+  QPen mPen, mSelectedPen;
+  QCPLineEnding mHead, mTail;
+  
+  virtual void draw(QCPPainter *painter);
+  
+  // helper functions:
+  QPen mainPen() const;
+};
+
+class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  enum BracketStyle { bsSquare  ///< A brace with angled edges
+                      ,bsRound  ///< A brace with round edges
+                      ,bsCurly  ///< A curly brace
+                      ,bsCalligraphic ///< A curly brace with varying stroke width giving a calligraphic impression
+  };
+
+  QCPItemBracket(QCustomPlot *parentPlot);
+  virtual ~QCPItemBracket();
+  
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  double length() const { return mLength; }
+  BracketStyle style() const { return mStyle; }
+  
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setLength(double length);
+  void setStyle(BracketStyle style);
+  
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  
+  QCPItemPosition * const left;
+  QCPItemPosition * const right;
+  QCPItemAnchor * const center;
+  
+protected:
+  enum AnchorIndex {aiCenter};
+  QPen mPen, mSelectedPen;
+  double mLength;
+  BracketStyle mStyle;
+  
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+  
+  // helper functions:
+  QPen mainPen() const;
+};
+
+class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem
+{
+  Q_OBJECT
+public:
+  /*!
+    The different visual appearances a tracer item can have. Some styles size may be controlled with \ref setSize.
+    
+    \see setStyle
+  */
+  enum TracerStyle { tsNone        ///< The tracer is not visible
+                     ,tsPlus       ///< A plus shaped crosshair with limited size
+                     ,tsCrosshair  ///< A plus shaped crosshair which spans the complete axis rect
+                     ,tsCircle     ///< A circle
+                     ,tsSquare     ///< A square
+                   };
+  Q_ENUMS(TracerStyle)
+
+  QCPItemTracer(QCustomPlot *parentPlot);
+  virtual ~QCPItemTracer();
+
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  double size() const { return mSize; }
+  TracerStyle style() const { return mStyle; }
+  QCPGraph *graph() const { return mGraph; }
+  double graphKey() const { return mGraphKey; }
+  bool interpolating() const { return mInterpolating; }
+
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+  void setSize(double size);
+  void setStyle(TracerStyle style);
+  void setGraph(QCPGraph *graph);
+  void setGraphKey(double key);
+  void setInterpolating(bool enabled);
+
+  // non-property methods:
+  virtual double selectTest(const QPointF &pos) const;
+  void updatePosition();
+
+  QCPItemPosition * const position;
+
+protected:
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+  double mSize;
+  TracerStyle mStyle;
+  QCPGraph *mGraph;
+  double mGraphKey;
+  bool mInterpolating;
+
+  virtual void draw(QCPPainter *painter);
+
+  // helper functions:
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+};
+
+class QCP_LIB_DECL QCPRange
+{
+public:
+  double lower, upper;
+  QCPRange();
+  QCPRange(double lower, double upper);
+  double size() const;
+  double center() const;
+  void normalize();
+  QCPRange sanitizedForLogScale() const;
+  QCPRange sanitizedForLinScale() const;
+  bool contains(double value) const;
+  
+  static bool validRange(double lower, double upper);
+  static bool validRange(const QCPRange &range);
+  static const double minRange; //1e-280;
+  static const double maxRange; //1e280;
+};
+Q_DECLARE_TYPEINFO(QCPRange, Q_MOVABLE_TYPE);
+
+class QCP_LIB_DECL QCPAbstractLegendItem : public QObject
+{
+  Q_OBJECT
+public:
+  QCPAbstractLegendItem(QCPLegend *parent);
+  virtual ~QCPAbstractLegendItem() {}
+  
+  // getters:
+  bool antialiased() const { return mAntialiased; }
+  QFont font() const { return mFont; }
+  QColor textColor() const { return mTextColor; }
+  QFont selectedFont() const { return mSelectedFont; }
+  QColor selectedTextColor() const { return mSelectedTextColor; }
+  bool selectable() const { return mSelectable; }
+  bool selected() const { return mSelected; }
+  
+  // setters:
+  void setAntialiased(bool enabled);
+  void setFont(const QFont &font);
+  void setTextColor(const QColor &color);
+  void setSelectedFont(const QFont &font);
+  void setSelectedTextColor(const QColor &color);
+  void setSelectable(bool selectable);
+  void setSelected(bool selected);
+  
+signals:
+  void selectionChanged(bool selected);
+  
+protected:
+  QCPLegend *mParentLegend;
+  bool mAntialiased;
+  QFont mFont;
+  QColor mTextColor;
+  QFont mSelectedFont;
+  QColor mSelectedTextColor;
+  bool mSelectable, mSelected;
+  
+  virtual void draw(QCPPainter *painter, const QRect &rect) const = 0;
+  virtual QSize size(const QSize &targetSize) const = 0;
+  void applyAntialiasingHint(QCPPainter *painter) const;
+  
+private:
+  Q_DISABLE_COPY(QCPAbstractLegendItem)
+  
+  friend class QCPLegend;
+};
+
+class QCP_LIB_DECL QCPPlottableLegendItem : public QCPAbstractLegendItem
+{
+  Q_OBJECT
+public:
+  QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable);
+  virtual ~QCPPlottableLegendItem() {}
+  
+  // getters:
+  QCPAbstractPlottable *plottable() { return mPlottable; }
+  bool textWrap() const { return mTextWrap; }
+  
+  // setters:
+  void setTextWrap(bool wrap);
+  
+protected:
+  QCPAbstractPlottable *mPlottable;
+  bool mTextWrap;
+  
+  QPen getIconBorderPen() const;
+  QColor getTextColor() const;
+  QFont getFont() const;
+
+  virtual void draw(QCPPainter *painter, const QRect &rect) const;
+  virtual QSize size(const QSize &targetSize) const;
+};
+
+class QCP_LIB_DECL QCPLegend : public QCPLayerable
+{
+  Q_OBJECT
+public:
+  /*!
+    Defines where the legend is positioned inside the QCustomPlot axis rect.
+  */
+  enum PositionStyle { psManual       ///< Position is not changed automatically. Set manually via \ref setPosition
+                      ,psTopLeft      ///< Legend is positioned in the top left corner of the axis rect with distance to the border corresponding to the currently set top and left margins
+                      ,psTop          ///< Legend is horizontally centered at the top of the axis rect with distance to the border corresponding to the currently set top margin
+                      ,psTopRight     ///< Legend is positioned in the top right corner of the axis rect with distance to the border corresponding to the currently set top and right margins
+                      ,psRight        ///< Legend is vertically centered at the right of the axis rect with distance to the border corresponding to the currently set right margin
+                      ,psBottomRight  ///< Legend is positioned in the bottom right corner of the axis rect with distance to the border corresponding to the currently set bottom and right margins
+                      ,psBottom       ///< Legend is horizontally centered at the bottom of the axis rect with distance to the border corresponding to the currently set bottom margin
+                      ,psBottomLeft   ///< Legend is positioned in the bottom left corner of the axis rect with distance to the border corresponding to the currently set bottom and left margins
+                      ,psLeft         ///< Legend is vertically centered at the left of the axis rect with distance to the border corresponding to the currently set left margin
+                     };
+  Q_ENUMS(PositionStyle)
+  
+  /*!
+    Defines the selectable parts of a legend
+  */
+  enum SelectablePart { spNone       = 0      ///< None
+                       ,spLegendBox  = 0x001  ///< The legend box (frame)
+                       ,spItems      = 0x002  ///< Legend items individually (see \ref selectedItems)
+                      };
+  Q_ENUMS(SelectablePart)
+  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
+  
+  explicit QCPLegend(QCustomPlot *parentPlot);
+  virtual ~QCPLegend();
+  
+  // getters:
+  QPen borderPen() const { return mBorderPen; }
+  QBrush brush() const { return mBrush; }
+  QFont font() const { return mFont; }
+  QColor textColor() const { return mTextColor; }
+  PositionStyle positionStyle() const { return mPositionStyle; }
+  QPoint position() const { return mPosition; }
+  bool autoSize() const { return mAutoSize; }
+  QSize size() const { return mSize; }
+  QSize minimumSize() const { return mMinimumSize; }
+  int paddingLeft() const { return mPaddingLeft; }
+  int paddingRight() const { return mPaddingRight; }
+  int paddingTop() const { return mPaddingTop; }
+  int paddingBottom() const { return mPaddingBottom; }
+  int marginLeft() const { return mMarginLeft; }
+  int marginRight() const { return mMarginRight; }
+  int marginTop() const { return mMarginTop; }
+  int marginBottom() const { return mMarginBottom; }
+  int itemSpacing() const { return mItemSpacing; }
+  QSize iconSize() const { return mIconSize; }
+  int iconTextPadding() const { return mIconTextPadding; }
+  QPen iconBorderPen() const { return mIconBorderPen; }
+  SelectableParts selectable() const { return mSelectable; }
+  SelectableParts selected() const { return mSelected; }
+  QPen selectedBorderPen() const { return mSelectedBorderPen; }
+  QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  QFont selectedFont() const { return mSelectedFont; }
+  QColor selectedTextColor() const { return mSelectedTextColor; }
+  
+  // setters:
+  void setBorderPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setFont(const QFont &font);
+  void setTextColor(const QColor &color);
+  void setPositionStyle(PositionStyle legendPositionStyle);
+  void setPosition(const QPoint &pixelPosition);
+  void setAutoSize(bool on);
+  void setSize(const QSize &size);
+  void setSize(int width, int height);
+  void setMinimumSize(const QSize &size);
+  void setMinimumSize(int width, int height);
+  void setPaddingLeft(int padding);
+  void setPaddingRight(int padding);
+  void setPaddingTop(int padding);
+  void setPaddingBottom(int padding);
+  void setPadding(int left, int right, int top, int bottom);
+  void setMarginLeft(int margin);
+  void setMarginRight(int margin);
+  void setMarginTop(int margin);
+  void setMarginBottom(int margin);
+  void setMargin(int left, int right, int top, int bottom);
+  void setItemSpacing(int spacing);
+  void setIconSize(const QSize &size);
+  void setIconSize(int width, int height);
+  void setIconTextPadding(int padding);
+  void setIconBorderPen(const QPen &pen);
+  void setSelectable(const SelectableParts &selectable);
+  void setSelected(const SelectableParts &selected);
+  void setSelectedBorderPen(const QPen &pen);
+  void setSelectedIconBorderPen(const QPen &pen);
+  void setSelectedBrush(const QBrush &brush);
+  void setSelectedFont(const QFont &font);
+  void setSelectedTextColor(const QColor &color);
+
+  // non-property methods:
+  QCPAbstractLegendItem *item(int index) const;
+  QCPPlottableLegendItem *itemWithPlottable(const QCPAbstractPlottable *plottable) const;
+  int itemCount() const;
+  bool hasItem(QCPAbstractLegendItem *item) const;
+  bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const;
+  bool addItem(QCPAbstractLegendItem *item);
+  bool removeItem(int index);
+  bool removeItem(QCPAbstractLegendItem *item);
+  void clearItems();
+  QList<QCPAbstractLegendItem*> selectedItems() const;
+  void reArrange();
+  
+  bool selectTestLegend(const QPointF &pos) const;
+  QCPAbstractLegendItem *selectTestItem(const QPoint pos) const;
+  
+signals:
+  void selectionChanged(QCPLegend::SelectableParts selection);
+  
+protected:
+  // simple properties with getters and setters:
+  QPen mBorderPen, mIconBorderPen;
+  QBrush mBrush;
+  QFont mFont;
+  QColor mTextColor;
+  QPoint mPosition;
+  QSize mSize, mMinimumSize, mIconSize;
+  PositionStyle mPositionStyle;
+  bool mAutoSize;
+  int mPaddingLeft, mPaddingRight, mPaddingTop, mPaddingBottom;
+  int mMarginLeft, mMarginRight, mMarginTop, mMarginBottom;
+  int mItemSpacing, mIconTextPadding;
+  SelectableParts mSelected, mSelectable;
+  QPen mSelectedBorderPen, mSelectedIconBorderPen;
+  QBrush mSelectedBrush;
+  QFont mSelectedFont;
+  QColor mSelectedTextColor;
+  
+  // internal or not explicitly exposed properties:
+  QList<QCPAbstractLegendItem*> mItems;
+  QMap<QCPAbstractLegendItem*, QRect> mItemBoundingBoxes;
+  
+  virtual void updateSelectionState();
+  virtual bool handleLegendSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
+  // introduced methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter);
+  virtual void calculateAutoSize();
+  virtual void calculateAutoPosition();
+  
+  // drawing helpers:
+  QPen getBorderPen() const;
+  QBrush getBrush() const;
+  
+private:
+  Q_DISABLE_COPY(QCPLegend)
+  
+  friend class QCustomPlot;
+  friend class QCPAbstractLegendItem;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPLegend::SelectableParts)
+
+class QCP_LIB_DECL QCPGrid : public QCPLayerable
+{
+  Q_OBJECT
+public:
+  QCPGrid(QCPAxis *parentAxis);
+  ~QCPGrid();
+  
+  // getters:
+  bool subGridVisible() const { return mSubGridVisible; }
+  bool antialiasedSubGrid() const { return mAntialiasedSubGrid; }
+  bool antialiasedZeroLine() const { return mAntialiasedZeroLine; }
+  QPen pen() const { return mPen; }
+  QPen subGridPen() const { return mSubGridPen; }
+  QPen zeroLinePen() const { return mZeroLinePen; }
+  
+  // setters:
+  void setSubGridVisible(bool visible);
+  void setAntialiasedSubGrid(bool enabled);
+  void setAntialiasedZeroLine(bool enabled);
+  void setPen(const QPen &pen);
+  void setSubGridPen(const QPen &pen);
+  void setZeroLinePen(const QPen &pen);
+  
+protected:
+  QCPAxis *mParentAxis;
+  bool mSubGridVisible;
+  bool mAntialiasedSubGrid, mAntialiasedZeroLine;
+  QPen mPen, mSubGridPen, mZeroLinePen;
+  
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter);
+  // drawing helpers:
+  void drawGridLines(QCPPainter *painter) const;
+  void drawSubGridLines(QCPPainter *painter) const;
+  
+  friend class QCPAxis;
+};
+
+class QCP_LIB_DECL QCPAxis : public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(AxisType axisType READ axisType WRITE setAxisType)
+  Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType)
+  Q_PROPERTY(double scaleLogBase READ scaleLogBase WRITE setScaleLogBase)
+  Q_PROPERTY(QRect axisRect READ axisRect WRITE setAxisRect)
+  Q_PROPERTY(QCPRange range READ range WRITE setRange)
+  Q_PROPERTY(bool grid READ grid WRITE setGrid)
+  Q_PROPERTY(bool subGrid READ subGrid WRITE setSubGrid)
+  Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks)
+  Q_PROPERTY(int autoTickCount READ autoTickCount WRITE setAutoTickCount)
+  Q_PROPERTY(bool autoTickLabels READ autoTickLabels WRITE setAutoTickLabels)
+  Q_PROPERTY(bool autoTickStep READ autoTickStep WRITE setAutoTickStep)
+  Q_PROPERTY(bool autoSubTicks READ autoSubTicks WRITE setAutoSubTicks)
+  Q_PROPERTY(bool ticks READ ticks WRITE setTicks)
+  Q_PROPERTY(bool tickLabels READ tickLabels WRITE setTickLabels)
+  Q_PROPERTY(int tickLabelPadding READ tickLabelPadding WRITE setTickLabelPadding)
+  Q_PROPERTY(LabelType tickLabelType READ tickLabelType WRITE setTickLabelType)
+  Q_PROPERTY(QFont tickLabelFont READ tickLabelFont WRITE setTickLabelFont)
+  Q_PROPERTY(double tickLabelRotation READ tickLabelRotation WRITE setTickLabelRotation)
+  Q_PROPERTY(QString dateTimeFormat READ dateTimeFormat WRITE setDateTimeFormat)
+  Q_PROPERTY(QString numberFormat READ numberFormat WRITE setNumberFormat)
+  Q_PROPERTY(double tickStep READ tickStep WRITE setTickStep)
+  Q_PROPERTY(QVector<double> tickVector READ tickVector WRITE setTickVector)
+  Q_PROPERTY(QVector<QString> tickVectorLabels READ tickVectorLabels WRITE setTickVectorLabels)
+  Q_PROPERTY(int subTickCount READ subTickCount WRITE setSubTickCount)
+  Q_PROPERTY(QPen basePen READ basePen WRITE setBasePen)
+  Q_PROPERTY(QPen gridPen READ gridPen WRITE setGridPen)
+  Q_PROPERTY(QPen subGridPen READ subGridPen WRITE setSubGridPen)
+  Q_PROPERTY(QPen tickPen READ tickPen WRITE setTickPen)
+  Q_PROPERTY(QPen subTickPen READ subTickPen WRITE setSubTickPen)
+  Q_PROPERTY(QFont labelFont READ labelFont WRITE setLabelFont)
+  Q_PROPERTY(QString label READ label WRITE setLabel)
+  Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)
+  /// \endcond
+public:
+  /*!
+    Defines at which side of the axis rect the axis will appear. This also affects how the tick
+    marks are drawn, on which side the labels are placed etc.
+    \see setAxisType
+  */
+  enum AxisType { atLeft    ///< Axis is vertical and on the left side of the axis rect of the parent QCustomPlot
+                  ,atRight  ///< Axis is vertical and on the right side of the axis rect of the parent QCustomPlot
+                  ,atTop    ///< Axis is horizontal and on the top side of the axis rect of the parent QCustomPlot
+                  ,atBottom ///< Axis is horizontal and on the bottom side of the axis rect of the parent QCustomPlot
+                };
+  Q_ENUMS(AxisType)
+  /*!
+    When automatic tick label generation is enabled (\ref setAutoTickLabels), defines how the
+    numerical value (coordinate) of the tick position is translated into a string that will be
+    drawn at the tick position.
+    \see setTickLabelType
+  */
+  enum LabelType { ltNumber    ///< Tick coordinate is regarded as normal number and will be displayed as such. (see \ref setNumberFormat)
+                   ,ltDateTime ///< Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC, see QDateTime::toTime_t) and will be displayed and formatted as such. (see \ref setDateTimeFormat)
+                 };
+  Q_ENUMS(LabelType)
+  /*!
+    Defines the scale of an axis.
+    \see setScaleType
+  */
+  enum ScaleType { stLinear       ///< Normal linear scaling
+                   ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power (see \ref setScaleLogBase).
+                 };
+  Q_ENUMS(ScaleType)
+  /*!
+    Defines the selectable parts of an axis.
+    \see setSelectable, setSelected
+  */
+  enum SelectablePart { spNone        = 0      ///< None of the selectable parts
+                        ,spAxis       = 0x001  ///< The axis backbone and tick marks
+                        ,spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)
+                        ,spAxisLabel  = 0x004  ///< The axis label
+                      };
+  Q_ENUMS(SelectablePart)
+  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
+  
+  explicit QCPAxis(QCustomPlot *parentPlot, AxisType type);
+  virtual ~QCPAxis();
+      
+  // getters:
+  AxisType axisType() const { return mAxisType; }
+  QRect axisRect() const { return mAxisRect; }
+  ScaleType scaleType() const { return mScaleType; }
+  double scaleLogBase() const { return mScaleLogBase; }
+  const QCPRange range() const { return mRange; }
+  bool rangeReversed() const { return mRangeReversed; }
+  bool antialiasedGrid() const { return mGrid->antialiased(); }
+  bool antialiasedSubGrid() const { return mGrid->antialiasedSubGrid(); }
+  bool antialiasedZeroLine() const { return mGrid->antialiasedZeroLine(); }
+  bool grid() const { return mGrid->visible(); }
+  bool subGrid() const { return mGrid->subGridVisible(); }
+  bool autoTicks() const { return mAutoTicks; }
+  int autoTickCount() const { return mAutoTickCount; }
+  bool autoTickLabels() const { return mAutoTickLabels; }
+  bool autoTickStep() const { return mAutoTickStep; }
+  bool autoSubTicks() const { return mAutoSubTicks; }
+  bool ticks() const { return mTicks; }
+  bool tickLabels() const { return mTickLabels; }
+  int tickLabelPadding() const { return mTickLabelPadding; }
+  LabelType tickLabelType() const { return mTickLabelType; }
+  QFont tickLabelFont() const { return mTickLabelFont; }
+  QColor tickLabelColor() const { return mTickLabelColor; }
+  double tickLabelRotation() const { return mTickLabelRotation; }
+  QString dateTimeFormat() const { return mDateTimeFormat; }
+  QString numberFormat() const;
+  int numberPrecision() const { return mNumberPrecision; }
+  double tickStep() const { return mTickStep; }
+  QVector<double> tickVector() const { return mTickVector; }
+  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
+  int tickLengthIn() const { return mTickLengthIn; }
+  int tickLengthOut() const { return mTickLengthOut; }
+  int subTickCount() const { return mSubTickCount; }
+  int subTickLengthIn() const { return mSubTickLengthIn; }
+  int subTickLengthOut() const { return mSubTickLengthOut; }
+  QPen basePen() const { return mBasePen; }
+  QPen gridPen() const { return mGrid->pen(); }
+  QPen subGridPen() const { return mGrid->subGridPen(); }
+  QPen zeroLinePen() const { return mGrid->zeroLinePen(); }
+  QPen tickPen() const { return mTickPen; }
+  QPen subTickPen() const { return mSubTickPen; }
+  QFont labelFont() const { return mLabelFont; }
+  QColor labelColor() const { return mLabelColor; }
+  QString label() const { return mLabel; }
+  int labelPadding() const { return mLabelPadding; }
+  int padding() const { return mPadding; }
+  SelectableParts selected() const { return mSelected; }
+  SelectableParts selectable() const { return mSelectable; }
+  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
+  QFont selectedLabelFont() const { return mSelectedLabelFont; }
+  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }
+  QColor selectedLabelColor() const { return mSelectedLabelColor; }
+  QPen selectedBasePen() const { return mSelectedBasePen; }
+  QPen selectedTickPen() const { return mSelectedTickPen; }
+  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
+  
+  // setters:
+  void setScaleType(ScaleType type);
+  void setScaleLogBase(double base);
+  void setRange(double lower, double upper);
+  void setRange(double position, double size, Qt::AlignmentFlag alignment);
+  void setRangeLower(double lower);
+  void setRangeUpper(double upper);
+  void setRangeReversed(bool reversed);
+  void setAntialiasedGrid(bool enabled);
+  void setAntialiasedSubGrid(bool enabled);
+  void setAntialiasedZeroLine(bool enabled);
+  void setGrid(bool show);
+  void setSubGrid(bool show);
+  void setAutoTicks(bool on);
+  void setAutoTickCount(int approximateCount);
+  void setAutoTickLabels(bool on);
+  void setAutoTickStep(bool on);
+  void setAutoSubTicks(bool on);
+  void setTicks(bool show);
+  void setTickLabels(bool show);
+  void setTickLabelPadding(int padding);
+  void setTickLabelType(LabelType type);
+  void setTickLabelFont(const QFont &font);
+  void setTickLabelColor(const QColor &color);
+  void setTickLabelRotation(double degrees);
+  void setDateTimeFormat(const QString &format);
+  void setNumberFormat(const QString &formatCode);
+  void setNumberPrecision(int precision);
+  void setTickStep(double step);
+  void setTickVector(const QVector<double> &vec);
+  void setTickVectorLabels(const QVector<QString> &vec);
+  void setTickLength(int inside, int outside=0);
+  void setSubTickCount(int count);
+  void setSubTickLength(int inside, int outside=0);
+  void setBasePen(const QPen &pen);
+  void setGridPen(const QPen &pen);
+  void setSubGridPen(const QPen &pen);
+  void setZeroLinePen(const QPen &pen);
+  void setTickPen(const QPen &pen);
+  void setSubTickPen(const QPen &pen);
+  void setLabelFont(const QFont &font);
+  void setLabelColor(const QColor &color);
+  void setLabel(const QString &str);
+  void setLabelPadding(int padding);
+  void setPadding(int padding);
+  void setSelectedTickLabelFont(const QFont &font);
+  void setSelectedLabelFont(const QFont &font);
+  void setSelectedTickLabelColor(const QColor &color);
+  void setSelectedLabelColor(const QColor &color);
+  void setSelectedBasePen(const QPen &pen);
+  void setSelectedTickPen(const QPen &pen);
+  void setSelectedSubTickPen(const QPen &pen);
+  
+  // non-property methods:
+  Qt::Orientation orientation() const { return mOrientation; }
+  void moveRange(double diff);
+  void scaleRange(double factor, double center);
+  void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0);
+  double pixelToCoord(double value) const;
+  double coordToPixel(double value) const;
+  SelectablePart selectTest(const QPointF &pos) const;
+  
+public slots:
+  // slot setters:
+  void setRange(const QCPRange &range);
+  void setSelectable(const QCPAxis::SelectableParts &selectable);
+  void setSelected(const QCPAxis::SelectableParts &selected);
+  
+signals:
+  void ticksRequest();
+  void rangeChanged(const QCPRange &newRange);
+  void selectionChanged(QCPAxis::SelectableParts selection);
+
+protected:
+  // simple properties with getters and setters:
+  QVector<double> mTickVector;
+  QVector<QString> mTickVectorLabels;
+  QCPRange mRange;
+  QString mDateTimeFormat;
+  QString mLabel;
+  QRect mAxisRect;
+  QPen mBasePen, mTickPen, mSubTickPen;
+  QFont mTickLabelFont, mLabelFont;
+  QColor mTickLabelColor, mLabelColor;
+  LabelType mTickLabelType;
+  ScaleType mScaleType;
+  AxisType mAxisType;
+  double mTickStep;
+  double mScaleLogBase, mScaleLogBaseLogInv;
+  int mSubTickCount, mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut;
+  int mAutoTickCount;
+  int mTickLabelPadding, mLabelPadding, mPadding;
+  double mTickLabelRotation;
+  bool mTicks, mTickLabels, mAutoTicks, mAutoTickLabels, mAutoTickStep, mAutoSubTicks;
+  bool mRangeReversed;
+  SelectableParts mSelectable, mSelected;
+  QFont mSelectedTickLabelFont, mSelectedLabelFont;
+  QColor mSelectedTickLabelColor, mSelectedLabelColor;
+  QPen mSelectedBasePen, mSelectedTickPen, mSelectedSubTickPen;
+  QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;
+  
+  // internal or not explicitly exposed properties:
+  QCPGrid *mGrid;
+  QVector<double> mSubTickVector;
+  QChar mExponentialChar, mPositiveSignChar;
+  int mNumberPrecision;
+  char mNumberFormatChar;
+  bool mNumberBeautifulPowers, mNumberMultiplyCross;
+  Qt::Orientation mOrientation;
+  int mLowestVisibleTick, mHighestVisibleTick;
+  
+  // internal setters:
+  void setAxisType(AxisType type);
+  void setAxisRect(const QRect &rect);
+  
+  // introduced methods:
+  virtual void setupTickVectors();
+  virtual void generateAutoTicks();
+  virtual int calculateAutoSubTickCount(double tickStep) const;
+  virtual int calculateMargin() const;
+  virtual bool handleAxisSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
+  
+  // drawing:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter); 
+  virtual void drawTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize);
+  virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;
+  
+  // basic non virtual helpers:
+  void visibleTickBounds(int &lowIndex, int &highIndex) const;
+  double baseLog(double value) const;
+  double basePow(double value) const;
+  
+  // helpers to get the right pen/font depending on selection state:
+  QPen getBasePen() const;
+  QPen getTickPen() const;
+  QPen getSubTickPen() const;
+  QFont getTickLabelFont() const;
+  QFont getLabelFont() const;
+  QColor getTickLabelColor() const;
+  QColor getLabelColor() const;
+  
+private:
+  Q_DISABLE_COPY(QCPAxis)
+  
+  friend class QCustomPlot;
+  friend class QCPGrid;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::SelectableParts)
+
+class QCP_LIB_DECL QCustomPlot : public QWidget
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QString title READ title WRITE setTitle)
+  Q_PROPERTY(QRect axisRect READ axisRect WRITE setAxisRect)
+  Q_PROPERTY(int marginLeft READ marginLeft WRITE setMarginLeft)
+  Q_PROPERTY(int marginRight READ marginRight WRITE setMarginRight)
+  Q_PROPERTY(int marginTop READ marginTop WRITE setMarginTop)
+  Q_PROPERTY(int marginBottom READ marginBottom WRITE setMarginBottom)
+  Q_PROPERTY(int autoMargin READ autoMargin WRITE setAutoMargin)
+  Q_PROPERTY(QColor color READ color WRITE setColor)
+  Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag)
+  Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom)
+  /// \endcond
+public:
+  /*!
+    Defines the mouse interactions possible with QCustomPlot
+    
+    \c Interactions is a flag of or-combined elements of this enum type.
+    \see setInteractions, setInteraction
+  */
+  enum Interaction { iRangeDrag         = 0x001 ///< <tt>0x001</tt> Axis ranges are draggable (see \ref setRangeDrag, \ref setRangeDragAxes)
+                     ,iRangeZoom        = 0x002 ///< <tt>0x002</tt> Axis ranges are zoomable with the mouse wheel (see \ref setRangeZoom, \ref setRangeZoomAxes)
+                     ,iMultiSelect      = 0x004 ///< <tt>0x004</tt> The user can select multiple objects by holding the modifier set by \ref setMultiSelectModifier while clicking
+                     ,iSelectTitle      = 0x008 ///< <tt>0x008</tt> The plot title is selectable
+                     ,iSelectPlottables = 0x010 ///< <tt>0x010</tt> Plottables are selectable
+                     ,iSelectAxes       = 0x020 ///< <tt>0x020</tt> Axes are selectable (or parts of them, see QCPAxis::setSelectable)
+                     ,iSelectLegend     = 0x040 ///< <tt>0x040</tt> Legends are selectable (or their child items, see QCPLegend::setSelectable)
+                     ,iSelectItems      = 0x080 ///< <tt>0x080</tt> Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref QCPAbstractItem)
+                   };
+  Q_ENUMS(Interaction)
+  Q_DECLARE_FLAGS(Interactions, Interaction)
+  /*!
+    Defines how a layer should be inserted relative to a specified other layer.
+
+    \see addLayer, moveLayer
+  */
+  enum LayerInsertMode { limBelow  ///< Layer is inserted below other layer
+                         ,limAbove ///< Layer is inserted above other layer
+                   };
+  Q_ENUMS(LayerInsertMode)
+  
+  explicit QCustomPlot(QWidget *parent = 0);
+  virtual ~QCustomPlot();
+  
+  // getters:
+  QString title() const { return mTitle; }
+  QFont titleFont() const { return mTitleFont; }
+  QColor titleColor() const { return mTitleColor; }
+  QRect axisRect() const { return mAxisRect; }
+  QRect viewport() const { return mViewport; }
+  int marginLeft() const { return mMarginLeft; }
+  int marginRight() const { return mMarginRight; }
+  int marginTop() const { return mMarginTop; }
+  int marginBottom() const { return mMarginBottom; }
+  bool autoMargin() const { return mAutoMargin; }
+  QColor color() const { return mColor; }
+  Qt::Orientations rangeDrag() const { return mRangeDrag; }
+  Qt::Orientations rangeZoom() const { return mRangeZoom; }
+  QCPAxis *rangeDragAxis(Qt::Orientation orientation);
+  QCPAxis *rangeZoomAxis(Qt::Orientation orientation);
+  double rangeZoomFactor(Qt::Orientation orientation);
+  QCP::AntialiasedElements antialiasedElements() const { return mAntialiasedElements; }
+  QCP::AntialiasedElements notAntialiasedElements() const { return mNotAntialiasedElements; }
+  bool autoAddPlottableToLegend() const { return mAutoAddPlottableToLegend; }
+  QPixmap axisBackground() const { return mAxisBackground; }
+  bool axisBackgroundScaled() const { return mAxisBackgroundScaled; }
+  Qt::AspectRatioMode axisBackgroundScaledMode() const { return mAxisBackgroundScaledMode; }
+  const Interactions interactions() const { return mInteractions; }
+  int selectionTolerance() const { return mSelectionTolerance; }
+  QFont selectedTitleFont() const { return mSelectedTitleFont; }
+  QColor selectedTitleColor() const { return mSelectedTitleColor; }
+  bool titleSelected() const { return mTitleSelected; }
+  bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; }
+  QCP::PlottingHints plottingHints() const { return mPlottingHints; }
+  Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; }
+
+  // setters:
+  void setTitle(const QString &title);
+  void setTitleFont(const QFont &font);
+  void setTitleColor(const QColor &color);
+  void setAxisRect(const QRect &arect);
+  void setMarginLeft(int margin);
+  void setMarginRight(int margin);
+  void setMarginTop(int margin);
+  void setMarginBottom(int margin);
+  void setMargin(int left, int right, int top, int bottom);
+  void setAutoMargin(bool enabled);
+  void setColor(const QColor &color);
+  void setRangeDrag(Qt::Orientations orientations);
+  void setRangeZoom(Qt::Orientations orientations);
+  void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical);
+  void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical);
+  void setRangeZoomFactor(double horizontalFactor, double verticalFactor);
+  void setRangeZoomFactor(double factor);
+  void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements);
+  void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true);
+  void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements);
+  void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true);
+  void setAutoAddPlottableToLegend(bool on);
+  void setAxisBackground(const QPixmap &pm);
+  void setAxisBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
+  void setAxisBackgroundScaled(bool scaled);
+  void setAxisBackgroundScaledMode(Qt::AspectRatioMode mode);
+  void setInteractions(const Interactions &interactions);
+  void setInteraction(const Interaction &interaction, bool enabled=true);
+  void setSelectionTolerance(int pixels);
+  void setSelectedTitleFont(const QFont &font);
+  void setSelectedTitleColor(const QColor &color);
+  void setTitleSelected(bool selected);
+  void setNoAntialiasingOnDrag(bool enabled);
+  void setPlottingHints(const QCP::PlottingHints &hints);
+  void setPlottingHint(QCP::PlottingHint hint, bool enabled=true);
+  void setMultiSelectModifier(Qt::KeyboardModifier modifier);
+  
+  // non-property methods:
+  // plottable interface:
+  QCPAbstractPlottable *plottable(int index);
+  QCPAbstractPlottable *plottable();
+  bool addPlottable(QCPAbstractPlottable *plottable);
+  bool removePlottable(QCPAbstractPlottable *plottable);
+  bool removePlottable(int index);
+  int clearPlottables();
+  int plottableCount() const;
+  QList<QCPAbstractPlottable*> selectedPlottables() const;
+  QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false) const;
+  bool hasPlottable(QCPAbstractPlottable *plottable) const;
+
+  // specialized interface for QCPGraph:
+  QCPGraph *graph(int index) const;
+  QCPGraph *graph() const;
+  QCPGraph *addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0);
+  bool removeGraph(QCPGraph *graph);
+  bool removeGraph(int index);
+  int clearGraphs();
+  int graphCount() const;
+  QList<QCPGraph*> selectedGraphs() const;
+  
+  // item interface:
+  QCPAbstractItem *item(int index) const;
+  QCPAbstractItem *item() const;
+  bool addItem(QCPAbstractItem* item);
+  bool removeItem(QCPAbstractItem *item);
+  bool removeItem(int index);
+  int clearItems();
+  int itemCount() const;
+  QList<QCPAbstractItem*> selectedItems() const;
+  QCPAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const;
+  
+  // layer interface:
+  QCPLayer *layer(const QString &name) const;
+  QCPLayer *layer(int index) const;
+  QCPLayer *currentLayer() const;
+  bool setCurrentLayer(const QString &name);
+  bool setCurrentLayer(QCPLayer *layer);
+  int layerCount() const;
+  bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove);
+  bool removeLayer(QCPLayer *layer);
+  bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove);
+  
+  QList<QCPAxis*> selectedAxes() const;
+  QList<QCPLegend*> selectedLegends() const;
+  void setupFullAxesBox();
+  bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0);
+  bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
+  bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
+  bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0);
+  bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1);
+
+  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
+  QCPLegend *legend;
+  
+public slots:
+  void deselectAll();
+  void replot();
+  void rescaleAxes();
+  
+signals:
+  void mouseDoubleClick(QMouseEvent *event);
+  void mousePress(QMouseEvent *event);
+  void mouseMove(QMouseEvent *event);
+  void mouseRelease(QMouseEvent *event);
+  void mouseWheel(QWheelEvent *event);
+  
+  void plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event);
+  void plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event);
+  void itemClick(QCPAbstractItem *item, QMouseEvent *event);
+  void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event);
+  void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);
+  void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);
+  void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event);
+  void legendDoubleClick(QCPLegend *legend,  QCPAbstractLegendItem *item, QMouseEvent *event);
+  void titleClick(QMouseEvent *event);
+  void titleDoubleClick(QMouseEvent *event);
+  
+  void selectionChangedByUser();
+  void beforeReplot();
+  void afterReplot();
+  
+protected:
+  QString mTitle;
+  QFont mTitleFont, mSelectedTitleFont;
+  QColor mTitleColor, mSelectedTitleColor;
+  QRect mViewport;
+  QRect mAxisRect;
+  int mMarginLeft, mMarginRight, mMarginTop, mMarginBottom;
+  bool mAutoMargin, mAutoAddPlottableToLegend;
+  QColor mColor;
+  QList<QCPAbstractPlottable*> mPlottables;
+  QList<QCPGraph*> mGraphs; // extra list of items also in mPlottables that are of type QCPGraph
+  QList<QCPAbstractItem*> mItems;
+  QList<QCPLayer*> mLayers;
+  Qt::Orientations mRangeDrag, mRangeZoom;
+  QCPAxis *mRangeDragHorzAxis, *mRangeDragVertAxis, *mRangeZoomHorzAxis, *mRangeZoomVertAxis;
+  double mRangeZoomFactorHorz, mRangeZoomFactorVert;
+  bool mDragging;
+  QCP::AntialiasedElements mAntialiasedElements, mNotAntialiasedElements;
+  QPixmap mAxisBackground;
+  bool mAxisBackgroundScaled;
+  Qt::AspectRatioMode mAxisBackgroundScaledMode;
+  Interactions mInteractions;
+  int mSelectionTolerance;
+  bool mTitleSelected;
+  QRect mTitleBoundingBox;
+  bool mNoAntialiasingOnDrag;
+  // not explicitly exposed properties:
+  QPixmap mPaintBuffer;
+  QPoint mDragStart;
+  QCPRange mDragStartHorzRange, mDragStartVertRange;
+  QPixmap mScaledAxisBackground;
+  bool mReplotting;
+  QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;
+  QCPLayer *mCurrentLayer;
+  QCP::PlottingHints mPlottingHints;
+  Qt::KeyboardModifier mMultiSelectModifier;
+  
+  // reimplemented methods:
+  virtual QSize minimumSizeHint() const;
+  virtual void paintEvent(QPaintEvent *event);
+  virtual void resizeEvent(QResizeEvent *event);
+  virtual void mouseDoubleClickEvent(QMouseEvent *event);
+  virtual void mousePressEvent(QMouseEvent *event);
+  virtual void mouseMoveEvent(QMouseEvent *event);
+  virtual void mouseReleaseEvent(QMouseEvent *event);
+  virtual void wheelEvent(QWheelEvent *event);
+  // event helpers:
+  virtual bool handlePlottableSelection(QMouseEvent *event, bool additiveSelection, bool &modified);  
+  virtual bool handleItemSelection(QMouseEvent *event, bool additiveSelection, bool &modified);  
+  virtual bool handleAxisSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
+  virtual bool handleTitleSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
+  
+  // introduced methods:
+  virtual void draw(QCPPainter *painter);
+  virtual void drawAxisBackground(QCPPainter *painter);
+  
+  // helpers:
+  void updateAxisRect();
+  bool selectTestTitle(const QPointF &pos) const;
+  friend class QCPLegend;
+  friend class QCPAxis;
+  friend class QCPLayer;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCustomPlot::Interactions)
+
+#endif // QCUSTOMPLOT_H
diff --git a/src/plastimatch/standalone/qt_util.cxx b/src/plastimatch/standalone/qt_util.cxx
new file mode 100644
index 0000000..8fedc72
--- /dev/null
+++ b/src/plastimatch/standalone/qt_util.cxx
@@ -0,0 +1,1436 @@
+#include "qt_util.h"
+#include "itkImage.h"
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+
+#include "itkImageDuplicator.h"
+#include "itkImageSliceConstIteratorWithIndex.h"
+#include "itkImageSliceIteratorWithIndex.h"
+#include "itkImageLinearConstIteratorWithIndex.h"
+
+#include <QtGlobal> //for qRound
+#include <QString> //for qRound
+#include <QtGui/QMainWindow>
+#include <QStandardItemModel>
+#include "gamma_gui.h"
+#include <QDir>
+#include <QMessageBox>
+
+void QUTIL::Set2DTo3D(FloatImage2DType::Pointer& spSrcImg2D, UShortImageType::Pointer& spTargetImg3D, int idx, enPLANE iDirection)
+{
+    if (!spSrcImg2D || !spTargetImg3D) //Target image should be also ready.
+        return;
+
+    int idxHor, idxVer, idxZ;
+
+    switch (iDirection)
+    {
+    case PLANE_AXIAL:
+        idxHor = 0;
+        idxVer = 1;
+        idxZ = 2;
+        break;
+    case PLANE_FRONTAL:
+        idxHor = 0;
+        idxVer = 2;
+        idxZ = 1;
+        break;
+    case PLANE_SAGITTAL:
+        idxHor = 1;
+        idxVer = 2;
+        idxZ = 0;
+        break;
+    }
+
+    FloatImage2DType::SizeType imgDim2D = spSrcImg2D->GetBufferedRegion().GetSize();
+    FloatImage2DType::SpacingType spacing2D = spSrcImg2D->GetSpacing();
+    FloatImage2DType::PointType origin2D = spSrcImg2D->GetOrigin();
+
+    UShortImageType::SizeType imgDim3D = spTargetImg3D->GetBufferedRegion().GetSize();
+    UShortImageType::SpacingType spacing3D = spTargetImg3D->GetSpacing();
+    UShortImageType::PointType origin3D = spTargetImg3D->GetOrigin();
+
+    //Filtering
+    if (imgDim2D[0] != imgDim3D[idxHor] ||
+        imgDim2D[1] != imgDim3D[idxVer] || idx < 0 || idx >= imgDim3D[idxZ])
+    {
+        cout << "Error: image dimensions is not matching" << endl;
+        cout << "2D= " << imgDim2D << endl;
+        cout << "3D= " << imgDim3D << endl;
+        return;
+    }
+    /*int width = imgDim[idxHor];
+    int height  = imgDim[idxVer];*/
+
+
+
+    //itk::ImageRegionConstIteratorWithIndex<FloatImage2DType> it_2D (spSrcImg2D, spSrcImg2D->GetRequestedRegion());
+    itk::ImageRegionConstIterator<FloatImage2DType> it_2D(spSrcImg2D, spSrcImg2D->GetRequestedRegion());
+    itk::ImageSliceIteratorWithIndex<UShortImageType> it_3D(spTargetImg3D, spTargetImg3D->GetRequestedRegion());
+
+    it_3D.SetFirstDirection(idxHor);
+    it_3D.SetSecondDirection(idxVer);
+    it_3D.GoToBegin();
+
+    int zSize = imgDim3D[idxZ];
+
+    it_2D.GoToBegin();
+
+    float fVal2D = 0.0;
+    unsigned short outputVal = 0;
+
+    for (int i = 0; i< zSize && !it_3D.IsAtEnd(); i++)
+    {
+        /*QFileInfo crntFileInfo(arrYKImage[i].m_strFilePath);
+        QString crntFileName = crntFileInfo.fileName();
+        QString crntPath = strSavingFolder + "/" + crntFileName;*/
+        //Search matching slice using slice iterator for m_spProjCTImg  
+        if (i == idx)
+        {
+            while (!it_3D.IsAtEndOfSlice())
+            {
+                while (!it_3D.IsAtEndOfLine())
+                {
+                    fVal2D = it_2D.Get();
+
+                    if (fVal2D < 0.0)
+                        outputVal = 0;
+                    else if (fVal2D > 65535.0)
+                        outputVal = 65535;
+                    else
+                        outputVal = (unsigned short)qRound(fVal2D);
+
+                    it_3D.Set(outputVal);
+                    //float tmpVal = (float)(it_3D.Get()); //in proj image case, this is intensity
+                    //it_2D.Set(tmpVal);		  
+                    ++it_2D;
+                    ++it_3D;
+                }//while2
+                it_3D.NextLine();
+            }//while1
+            break;
+        }
+        //
+        it_3D.NextSlice();
+    }//end of for
+}
+
+
+void QUTIL::Get2DFrom3DByIndex(UShortImageType::Pointer& spSrcImg3D, UShortImage2DType::Pointer& spTargetImg2D, int idx, enPLANE iDirection)
+{
+    if (!spSrcImg3D)
+        return;
+
+    int idxHor, idxVer, idxZ;
+
+    switch (iDirection)
+    {
+    case PLANE_AXIAL:
+        idxHor = 0;
+        idxVer = 1;
+        idxZ = 2;
+        break;
+    case PLANE_FRONTAL:
+        idxHor = 0;
+        idxVer = 2;
+        idxZ = 1;
+        break;
+    case PLANE_SAGITTAL:
+        idxHor = 1;
+        idxVer = 2;
+        idxZ = 0;
+        break;
+    }
+
+    //Create 2D target image based on geometry of 3D
+    UShortImageType::SizeType imgDim = spSrcImg3D->GetBufferedRegion().GetSize();
+    UShortImageType::SpacingType spacing = spSrcImg3D->GetSpacing();
+    UShortImageType::PointType origin = spSrcImg3D->GetOrigin();
+
+    int width = imgDim[idxHor];
+    int height = imgDim[idxVer];
+    int zSize = imgDim[idxZ];
+    //cout << "Get2DFrom3D zSize = " << zSize << endl;
+
+    if (idx < 0 || idx >= zSize)
+    {
+        cout << "Error! idx is out of the range" << endl;
+        return;
+    }
+
+    UShortImage2DType::IndexType idxStart;
+    idxStart[0] = 0;
+    idxStart[1] = 0;
+
+    UShortImage2DType::SizeType size2D;
+    size2D[0] = imgDim[idxHor];
+    size2D[1] = imgDim[idxVer];
+
+    UShortImage2DType::SpacingType spacing2D;
+    spacing2D[0] = spacing[idxHor];
+    spacing2D[1] = spacing[idxVer];
+
+    UShortImage2DType::PointType origin2D;
+    //  origin2D[0] = origin[idxHor];
+    //  origin2D[1] = origin[idxVer];
+    origin2D[0] = size2D[0] * spacing2D[0] / -2.0;
+    origin2D[1] = size2D[1] * spacing2D[1] / -2.0;
+
+    UShortImage2DType::RegionType region;
+    region.SetSize(size2D);
+    region.SetIndex(idxStart);
+
+    //spTargetImg2D is supposed to be empty.
+    if (spTargetImg2D)
+    {
+        cout << "something is here in target image. is it gonna be overwritten?" << endl;
+    }
+
+    spTargetImg2D = UShortImage2DType::New();
+    spTargetImg2D->SetRegions(region);
+    spTargetImg2D->SetSpacing(spacing2D);
+    spTargetImg2D->SetOrigin(origin2D);
+
+    spTargetImg2D->Allocate();
+    spTargetImg2D->FillBuffer(0);
+
+    //cout << "src size = " << spSrcImg3D->GetRequestedRegion().GetSize() << " " << endl;
+    //cout << "target image size = " << spTargetImg2D->GetRequestedRegion().GetSize() << " " << endl;
+
+
+    itk::ImageSliceConstIteratorWithIndex<UShortImageType> it_3D(spSrcImg3D, spSrcImg3D->GetRequestedRegion());    
+    itk::ImageRegionIterator<UShortImage2DType> it_2D(spTargetImg2D, spTargetImg2D->GetRequestedRegion());
+
+    it_3D.SetFirstDirection(idxHor);
+    it_3D.SetSecondDirection(idxVer);
+
+    it_3D.GoToBegin();
+    it_2D.GoToBegin();
+
+
+    for (int i = 0; i< zSize && !it_3D.IsAtEnd(); i++)
+    {
+        /*QFileInfo crntFileInfo(arrYKImage[i].m_strFilePath);
+        QString crntFileName = crntFileInfo.fileName();
+        QString crntPath = strSavingFolder + "/" + crntFileName;*/
+        //Search matching slice using slice iterator for m_spProjCTImg	
+        //cout << "Get2DFrom3D: Slide= " << i  << " ";
+
+        if (i == idx)
+        {
+            while (!it_3D.IsAtEndOfSlice()) //Error here why?
+            {
+                while (!it_3D.IsAtEndOfLine())
+                {
+                    float tmpVal = (float)(it_3D.Get()); //in proj image case, this is intensity
+                    it_2D.Set(tmpVal);
+                    ++it_2D;
+                    ++it_3D;
+                }//while2
+                it_3D.NextLine();
+            }//while1
+            break;
+        }	// end if 
+        it_3D.NextSlice();
+    }	//end of for
+
+    //cout << "cnt = " << cnt << " TotCnt " << cntTot << endl;
+    /*YK16GrayImage tmpYK;
+    tmpYK.UpdateFromItkImageFloat(spTargetImg2D);
+    QString str = QString("D:\\testYK\\InsideFunc_%1.raw").arg(idx);
+    tmpYK.SaveDataAsRaw(str.toLocal8Bit().constData());*/
+}
+
+void QUTIL::Get2DFrom3DByIndex(FloatImageType::Pointer& spSrcImg3D, FloatImage2DType::Pointer& spTargetImg2D, int idx, enPLANE iDirection)
+{
+    if (!spSrcImg3D)
+        return;
+
+    int idxHor, idxVer, idxZ;
+
+    switch (iDirection)
+    {
+    case PLANE_AXIAL:
+        idxHor = 0;
+        idxVer = 1;
+        idxZ = 2;
+        break;
+    case PLANE_FRONTAL:
+        idxHor = 0;
+        idxVer = 2;
+        idxZ = 1;
+        break;
+    case PLANE_SAGITTAL:
+        idxHor = 1;
+        idxVer = 2;
+        idxZ = 0;
+        break;
+    }
+
+    //Create 2D target image based on geometry of 3D
+    FloatImageType::SizeType imgDim = spSrcImg3D->GetBufferedRegion().GetSize();
+    FloatImageType::SpacingType spacing = spSrcImg3D->GetSpacing();
+    FloatImageType::PointType origin = spSrcImg3D->GetOrigin();
+
+    int width = imgDim[idxHor];
+    int height = imgDim[idxVer];
+    int zSize = imgDim[idxZ];
+    //cout << "Get2DFrom3D zSize = " << zSize << endl;
+
+    if (idx < 0 || idx >= zSize)
+    {
+        cout << "Error! idx is out of the range" << endl;
+        return;
+    }
+
+    FloatImage2DType::IndexType idxStart;
+    idxStart[0] = 0;
+    idxStart[1] = 0;
+
+    FloatImage2DType::SizeType size2D;
+    size2D[0] = imgDim[idxHor];
+    size2D[1] = imgDim[idxVer];
+
+    FloatImage2DType::SpacingType spacing2D;
+    spacing2D[0] = spacing[idxHor];
+    spacing2D[1] = spacing[idxVer];
+
+    FloatImage2DType::PointType origin2D;
+    //  origin2D[0] = origin[idxHor];
+    //  origin2D[1] = origin[idxVer];
+    origin2D[0] = size2D[0] * spacing2D[0] / -2.0;
+    origin2D[1] = size2D[1] * spacing2D[1] / -2.0;
+
+    FloatImage2DType::RegionType region;
+    region.SetSize(size2D);
+    region.SetIndex(idxStart);
+
+    //spTargetImg2D is supposed to be empty.
+    if (spTargetImg2D)
+    {
+        cout << "something is here in target image. is it gonna be overwritten?" << endl;
+    }
+
+    spTargetImg2D = FloatImage2DType::New();
+    spTargetImg2D->SetRegions(region);
+    spTargetImg2D->SetSpacing(spacing2D);
+    spTargetImg2D->SetOrigin(origin2D);
+
+    spTargetImg2D->Allocate();
+    spTargetImg2D->FillBuffer(0);
+
+    //cout << "src size = " << spSrcImg3D->GetRequestedRegion().GetSize() << " " << endl;
+    //cout << "target image size = " << spTargetImg2D->GetRequestedRegion().GetSize() << " " << endl;
+
+
+    itk::ImageSliceConstIteratorWithIndex<FloatImageType> it_3D(spSrcImg3D, spSrcImg3D->GetRequestedRegion());
+    itk::ImageRegionIterator<FloatImage2DType> it_2D(spTargetImg2D, spTargetImg2D->GetRequestedRegion());
+
+    it_3D.SetFirstDirection(idxHor);
+    it_3D.SetSecondDirection(idxVer);
+
+    it_3D.GoToBegin();
+    it_2D.GoToBegin();
+
+
+    for (int i = 0; i< zSize && !it_3D.IsAtEnd(); i++)
+    {
+        /*QFileInfo crntFileInfo(arrYKImage[i].m_strFilePath);
+        QString crntFileName = crntFileInfo.fileName();
+        QString crntPath = strSavingFolder + "/" + crntFileName;*/
+        //Search matching slice using slice iterator for m_spProjCTImg	
+        //cout << "Get2DFrom3D: Slide= " << i  << " ";
+
+        if (i == idx)
+        {
+            while (!it_3D.IsAtEndOfSlice()) //Error here why?
+            {
+                while (!it_3D.IsAtEndOfLine())
+                {
+                    float tmpVal = (float)(it_3D.Get()); //in proj image case, this is intensity
+                    it_2D.Set(tmpVal);
+                    ++it_2D;
+                    ++it_3D;
+                }//while2
+                it_3D.NextLine();
+            }//while1
+            break;
+        }	// end if 
+        it_3D.NextSlice();
+    }	//end of for
+}
+
+void QUTIL::Get2DFrom3DByPosition(UShortImageType::Pointer& spSrcImg3D, UShortImage2DType::Pointer& spTargImg2D, enPLANE iDirection, double pos, double& finalPos)
+{
+    if (!spSrcImg3D)
+        return;
+
+    int idxHor, idxVer, idxZ;
+
+    switch (iDirection)
+    {
+    case PLANE_AXIAL:
+        idxHor = 0;
+        idxVer = 1;
+        idxZ = 2;
+        break;
+    case PLANE_SAGITTAL:
+        idxHor = 1;
+        idxVer = 2;
+        idxZ = 0;
+        break;
+    case PLANE_FRONTAL:
+        idxHor = 0;
+        idxVer = 2;
+        idxZ = 1;
+        break;
+
+    }
+
+    //Create 2D target image based on geometry of 3D
+    UShortImageType::SizeType imgDim = spSrcImg3D->GetBufferedRegion().GetSize();
+    UShortImageType::SpacingType spacing = spSrcImg3D->GetSpacing();
+    UShortImageType::PointType origin = spSrcImg3D->GetOrigin();
+
+    int width = imgDim[idxHor];
+    int height = imgDim[idxVer];
+    int zSize = imgDim[idxZ];
+    //cout << "Get2DFrom3D zSize = " << zSize << endl
+
+    int iCntSlice = imgDim[idxZ];
+    int iReqSlice = qRound((pos - origin[idxZ]) / spacing[idxZ]);
+
+    finalPos = iReqSlice* spacing[idxZ] + origin[idxZ];
+
+    
+
+
+    if (iReqSlice < 0 || iReqSlice >= iCntSlice)
+    {
+        //cout << "Error! idx is out of the range" << endl;
+
+        cout << "Error! idx is out of the range" << "iReqSlice= " << iReqSlice <<
+            " iCntSlice= " << iCntSlice << endl;
+        cout << " iDirection = " << iDirection << endl;
+        cout << " pos = " << pos << endl;
+        cout << " origin[idxZ] = " << origin[idxZ] << endl;
+        cout << " spacing[idxZ] = " << spacing[idxZ] << endl;
+
+        return;
+    }
+
+    UShortImage2DType::IndexType idxStart;
+    idxStart[0] = 0;
+    idxStart[1] = 0;
+
+    UShortImage2DType::SizeType size2D;
+    size2D[0] = imgDim[idxHor];
+    size2D[1] = imgDim[idxVer];
+
+    UShortImage2DType::SpacingType spacing2D;
+    spacing2D[0] = spacing[idxHor];
+    spacing2D[1] = spacing[idxVer];
+
+    UShortImage2DType::PointType origin2D;
+    origin2D[0] = origin[idxHor];
+    origin2D[1] = origin[idxVer];
+    //origin2D[0] = size2D[0] * spacing2D[0] / -2.0;
+    //origin2D[1] = size2D[1] * spacing2D[1] / -2.0;
+
+    UShortImage2DType::RegionType region;
+    region.SetSize(size2D);
+    region.SetIndex(idxStart);
+
+    //spTargetImg2D is supposed to be empty.
+    /* if (spTargImg2D)
+     {
+     cout << "something is here in target image. is it gonna be overwritten?" << endl;
+     }*/
+
+    spTargImg2D = UShortImage2DType::New();
+    spTargImg2D->SetRegions(region);
+    spTargImg2D->SetSpacing(spacing2D);
+    spTargImg2D->SetOrigin(origin2D);
+
+    spTargImg2D->Allocate();
+    spTargImg2D->FillBuffer(0);
+
+    itk::ImageSliceConstIteratorWithIndex<UShortImageType> it_3D(spSrcImg3D, spSrcImg3D->GetRequestedRegion());    
+    itk::ImageRegionIterator<UShortImage2DType> it_2D(spTargImg2D, spTargImg2D->GetRequestedRegion());
+
+    it_3D.SetFirstDirection(idxHor);
+    it_3D.SetSecondDirection(idxVer);
+
+    it_3D.GoToBegin();
+    it_2D.GoToBegin();
+    
+    for (int i = 0; i< iCntSlice && !it_3D.IsAtEnd(); i++)
+    {
+        if (i == iReqSlice)
+        {
+            while (!it_3D.IsAtEndOfSlice()) //Error here why?
+            {
+                while (!it_3D.IsAtEndOfLine())
+                {
+                    float tmpVal = (float)(it_3D.Get()); //in proj image case, this is intensity
+                    it_2D.Set(tmpVal);
+                    ++it_2D;
+                    ++it_3D;
+                }//while2
+                it_3D.NextLine();
+            }//while1
+            break;
+        }	// end if 
+        it_3D.NextSlice();
+    }	//end of for  
+    
+}
+
+void QUTIL::Get2DFrom3DByPosition(FloatImageType::Pointer& spSrcImg3D, FloatImage2DType::Pointer& spTargImg2D, enPLANE iDirection, double pos, double& finalPos)
+{
+    if (!spSrcImg3D)
+        return;
+
+    int idxHor, idxVer, idxZ;
+    //bool bUpDownFlip = false;
+
+    switch (iDirection)
+    {
+    case PLANE_AXIAL:
+        idxHor = 0;
+        idxVer = 1;
+        idxZ = 2;
+        break;
+    case PLANE_SAGITTAL:
+        idxHor = 1;
+        idxVer = 2;
+        idxZ = 0;
+        //bUpDownFlip = true;
+        break;
+    case PLANE_FRONTAL:
+        idxHor = 0;
+        idxVer = 2;
+        idxZ = 1;
+        //bUpDownFlip = true;
+        break;  
+    }
+
+    //Create 2D target image based on geometry of 3D
+    FloatImageType::SizeType imgDim = spSrcImg3D->GetBufferedRegion().GetSize();
+    FloatImageType::SpacingType spacing = spSrcImg3D->GetSpacing();
+    FloatImageType::PointType origin = spSrcImg3D->GetOrigin();
+
+    int width = imgDim[idxHor];
+    int height = imgDim[idxVer];
+    int zSize = imgDim[idxZ];
+    //cout << "Get2DFrom3D zSize = " << zSize << endl
+
+    int iCntSlice = imgDim[idxZ];
+    int iReqSlice = qRound((pos - origin[idxZ]) / spacing[idxZ]);
+
+
+    finalPos = iReqSlice* spacing[idxZ] + origin[idxZ];
+
+
+    if (iReqSlice < 0 || iReqSlice >= iCntSlice)
+    {
+        cout << "Error! idx is out of the range" << "iReqSlice= " << iReqSlice <<
+            " iCntSlice= " << iCntSlice << endl;
+        cout << " iDirection = " << iDirection << endl;
+        cout << " pos = " << pos << endl;
+        cout << " origin[idxZ] = " << origin[idxZ] << endl;
+        cout << " spacing[idxZ] = " << spacing[idxZ] << endl;
+
+        return;
+    }
+
+    FloatImage2DType::IndexType idxStart2D;
+    idxStart2D[0] = 0;
+    idxStart2D[1] = 0;
+
+    FloatImage2DType::SizeType size2D;
+    size2D[0] = imgDim[idxHor];
+    size2D[1] = imgDim[idxVer];
+
+    FloatImage2DType::SpacingType spacing2D;
+    spacing2D[0] = spacing[idxHor];
+    spacing2D[1] = spacing[idxVer];
+
+    FloatImage2DType::PointType origin2D;
+    origin2D[0] = origin[idxHor];
+    origin2D[1] = origin[idxVer];
+    //origin2D[0] = size2D[0] * spacing2D[0] / -2.0;
+    //origin2D[1] = size2D[1] * spacing2D[1] / -2.0;
+
+    FloatImage2DType::RegionType region;
+    region.SetSize(size2D);
+    region.SetIndex(idxStart2D);
+
+    //spTargetImg2D is supposed to be empty.
+    /* if (spTargImg2D)
+    {
+    cout << "something is here in target image. is it gonna be overwritten?" << endl;
+    }*/
+
+    spTargImg2D = FloatImage2DType::New();
+    spTargImg2D->SetRegions(region);
+    spTargImg2D->SetSpacing(spacing2D);
+    spTargImg2D->SetOrigin(origin2D);
+
+    spTargImg2D->Allocate();
+    spTargImg2D->FillBuffer(0);
+
+    itk::ImageSliceConstIteratorWithIndex<FloatImageType> it_3D(spSrcImg3D, spSrcImg3D->GetRequestedRegion());
+    itk::ImageRegionIterator<FloatImage2DType> it_2D(spTargImg2D, spTargImg2D->GetRequestedRegion());
+
+    it_3D.SetFirstDirection(idxHor);
+    it_3D.SetSecondDirection(idxVer);
+
+    it_3D.GoToBegin();
+    it_2D.GoToBegin();    
+
+    for (int i = 0; i< iCntSlice && !it_3D.IsAtEnd(); i++)
+    {
+        if (i == iReqSlice)
+        {
+            while (!it_3D.IsAtEndOfSlice()) //Error here why?
+            {
+                while (!it_3D.IsAtEndOfLine())
+                {
+                    float tmpVal = (float)(it_3D.Get()); //in proj image case, this is intensity
+                    it_2D.Set(tmpVal);
+                    ++it_2D;
+                    ++it_3D;
+                }//while2
+                it_3D.NextLine();
+            }//while1
+            break;
+        }	// end if 
+        it_3D.NextSlice();
+    }	//end of for
+}
+
+bool QUTIL::GetProfile1DByPosition(UShortImage2DType::Pointer& spSrcImg2D, vector<QPointF>& vProfile, float fixedPos, enPROFILE_DIRECTON enDirection)
+{
+    if (!spSrcImg2D)
+        return false;
+
+    UShortImage2DType::SizeType imgDim = spSrcImg2D->GetBufferedRegion().GetSize();
+    UShortImage2DType::SpacingType spacing = spSrcImg2D->GetSpacing();
+    UShortImage2DType::PointType origin = spSrcImg2D->GetOrigin();
+
+    int width = imgDim[0];
+    int height = imgDim[1];
+
+    //itk::ImageSliceConstIteratorWithIndex<FloatImage2DType> it_2D(spSrcImg3D, spSrcImg3D->GetRequestedRegion());
+
+    itk::ImageLinearConstIteratorWithIndex<UShortImage2DType> it_2D(spSrcImg2D, spSrcImg2D->GetRequestedRegion());
+        
+     //::SetDirection(unsigned int 	direction)
+
+    if (!vProfile.empty())
+    {
+        vProfile.clear();
+    }
+
+    QPointF curPt;
+    
+    /*int fixedIdx = 0;
+    int movingIdx = 0;*/  
+
+
+    float fValX = 0.0;
+    float fValY = 0.0;
+
+    it_2D.GoToBegin();
+
+    if (enDirection == PRIFLE_HOR)
+    {
+        int fixedY = qRound(fixedPos - origin[1]) / spacing[1];
+        for (int i = 0; i < height; i++)
+        {
+            for (int j = 0; j < width; j++)
+            {
+                if (i == fixedY)
+                {
+                    fValX = (double)(j*spacing[0]) + origin[0];
+                    fValY = (double)(it_2D.Get());
+                    curPt.setX(fValX);
+                    curPt.setY(fValY);
+
+                    vProfile.push_back(curPt);
+                }
+                ++it_2D;
+            }
+            if (it_2D.IsAtEnd())
+                break;
+        }
+    }        
+    else if (enDirection == PRIFLE_VER)
+    {
+        int fixedX = qRound(fixedPos - origin[0]) / spacing[0];                
+        for (int i = 0; i < height; i++)
+        {
+            for (int j = 0; j < width; j++)
+            {
+                if (j == fixedX)
+                {
+                    fValX = (double)(i*spacing[1]) + origin[1]; // ((i - origin[1])*spacing[1]);
+                    fValY = (double)(it_2D.Get());
+                    curPt.setX(fValX);
+                    curPt.setY(fValY);
+
+                    vProfile.push_back(curPt);
+                }
+                ++it_2D;
+            }
+            if (it_2D.IsAtEnd())
+                break;
+        }
+    }
+
+    if (vProfile.empty())
+        return false;
+    
+
+    return true;       
+}
+
+bool QUTIL::GetProfile1DByPosition(FloatImage2DType::Pointer& spSrcImg2D, vector<QPointF>& vProfile, float fixedPos, enPROFILE_DIRECTON enDirection)
+{
+    if (!spSrcImg2D)
+        return false;
+
+    FloatImage2DType::SizeType imgDim = spSrcImg2D->GetBufferedRegion().GetSize();
+    FloatImage2DType::SpacingType spacing = spSrcImg2D->GetSpacing();
+    FloatImage2DType::PointType origin = spSrcImg2D->GetOrigin();
+
+    int width = imgDim[0];
+    int height = imgDim[1];
+
+    //itk::ImageSliceConstIteratorWithIndex<FloatImage2DType> it_2D(spSrcImg3D, spSrcImg3D->GetRequestedRegion());
+
+    itk::ImageLinearConstIteratorWithIndex<FloatImage2DType> it_2D(spSrcImg2D, spSrcImg2D->GetRequestedRegion());
+
+    //::SetDirection(unsigned int 	direction)
+
+    if (!vProfile.empty())
+    {
+        vProfile.clear();
+    }
+
+    QPointF curPt;
+
+    /*int fixedIdx = 0;
+    int movingIdx = 0;*/
+
+
+    float fValX = 0.0;
+    float fValY = 0.0;
+
+    it_2D.GoToBegin();
+
+    if (enDirection == PRIFLE_HOR)
+    {
+        int fixedY = qRound((fixedPos - origin[1]) / spacing[1]);
+        for (int i = 0; i < height; i++)
+        {
+            for (int j = 0; j < width; j++)
+            {
+                if (i == fixedY)
+                {
+                    fValX = (double)(j*spacing[0]) + origin[0];
+                    fValY = (double)(it_2D.Get());
+                    curPt.setX(fValX);
+                    curPt.setY(fValY);
+
+                    vProfile.push_back(curPt);
+                }
+                ++it_2D;
+            }
+            if (it_2D.IsAtEnd())
+                break;
+        }
+    }
+    else if (enDirection == PRIFLE_VER)
+    {
+        //cout << "PRIFLE_VER" << endl;
+        int fixedX = qRound((fixedPos - origin[0]) / spacing[0]);
+
+        //cout << "fixedX= " << fixedX << endl;
+        for (int i = 0; i < height; i++)
+        {
+            for (int j = 0; j < width; j++)
+            {
+                if (j == fixedX)
+                {
+                    fValX = (double)(i*spacing[1]) + origin[1];
+                    fValY = (double)(it_2D.Get());
+                    curPt.setX(fValX);
+                    curPt.setY(fValY);
+
+                    vProfile.push_back(curPt);
+                }
+                ++it_2D;
+            }
+            if (it_2D.IsAtEnd())
+                break;
+        }
+    }
+
+    if (vProfile.empty())
+        return false;
+
+
+    return true;
+}
+
+bool QUTIL::GetProfile1DByIndex(UShortImage2DType::Pointer& spSrcImg2D, vector<QPointF>& vProfile, int fixedIndex, enPROFILE_DIRECTON enDirection)
+{
+    if (!spSrcImg2D)
+        return false;
+
+    UShortImage2DType::SizeType imgDim = spSrcImg2D->GetBufferedRegion().GetSize();
+    UShortImage2DType::SpacingType spacing = spSrcImg2D->GetSpacing();
+    UShortImage2DType::PointType origin = spSrcImg2D->GetOrigin();
+
+    int width = imgDim[0];
+    int height = imgDim[1];
+
+    itk::ImageLinearConstIteratorWithIndex<UShortImage2DType> it_2D(spSrcImg2D, spSrcImg2D->GetRequestedRegion());
+
+    //::SetDirection(unsigned int 	direction)
+
+    if (!vProfile.empty())
+    {
+        vProfile.clear();
+    }
+
+    QPointF curPt;
+
+    /*int fixedIdx = 0;
+    int movingIdx = 0;*/
+
+
+    float fValX = 0.0;
+    float fValY = 0.0;
+
+    it_2D.GoToBegin();
+
+    if (enDirection == PRIFLE_HOR)
+    {
+        int fixedY = fixedIndex;
+        for (int i = 0; i < height; i++)
+        {
+            for (int j = 0; j < width; j++)
+            {
+                if (i == fixedY)
+                {
+                    fValX = (double)((j - origin[0])*spacing[0]);
+                    fValY = (double)(it_2D.Get());
+                    curPt.setX(fValX);
+                    curPt.setY(fValY);
+
+                    vProfile.push_back(curPt);
+                }
+                ++it_2D;
+            }
+            if (it_2D.IsAtEnd())
+                break;
+        }
+    }
+    else if (enDirection == PRIFLE_VER)
+    {
+        int fixedX = fixedIndex;
+
+        for (int i = 0; i < height; i++)
+        {
+            for (int j = 0; j < width; j++)
+            {
+                if (j == fixedX)
+                {
+                    fValX = (double)((i - origin[1])*spacing[1]);
+                    fValY = (double)(it_2D.Get());
+                    curPt.setX(fValX);
+                    curPt.setY(fValY);
+
+                    vProfile.push_back(curPt);
+                }
+                ++it_2D;
+            }
+            if (it_2D.IsAtEnd())
+                break;
+        }
+    }
+
+    if (vProfile.empty())
+        return false;
+
+
+    return true;
+
+}
+
+bool QUTIL::GetProfile1DByIndex(FloatImage2DType::Pointer& spSrcImg2D, vector<QPointF>& vProfile, int fixedIndex, enPROFILE_DIRECTON enDirection)
+{
+    if (!spSrcImg2D)
+        return false;
+
+    FloatImage2DType::SizeType imgDim = spSrcImg2D->GetBufferedRegion().GetSize();
+    FloatImage2DType::SpacingType spacing = spSrcImg2D->GetSpacing();
+    FloatImage2DType::PointType origin = spSrcImg2D->GetOrigin();
+
+    int width = imgDim[0];
+    int height = imgDim[1];    
+
+    itk::ImageLinearConstIteratorWithIndex<FloatImage2DType> it_2D(spSrcImg2D, spSrcImg2D->GetRequestedRegion());
+
+    //::SetDirection(unsigned int 	direction)
+
+    if (!vProfile.empty())
+    {
+        vProfile.clear();
+    }
+
+    QPointF curPt;
+
+    /*int fixedIdx = 0;
+    int movingIdx = 0;*/
+
+
+    float fValX = 0.0;
+    float fValY = 0.0;
+
+    it_2D.GoToBegin();
+
+    if (enDirection == PRIFLE_HOR)
+    {
+        int fixedY = fixedIndex;
+        for (int i = 0; i < height; i++)
+        {
+            for (int j = 0; j < width; j++)
+            {
+                if (i == fixedY)
+                {
+                    fValX = (double)((j - origin[0])*spacing[0]);
+                    fValY = (double)(it_2D.Get());
+                    curPt.setX(fValX);
+                    curPt.setY(fValY);
+
+                    vProfile.push_back(curPt);
+                }
+                ++it_2D;
+            }
+            if (it_2D.IsAtEnd())
+                break;
+        }
+    }
+    else if (enDirection == PRIFLE_VER)
+    {
+        int fixedX = fixedIndex;
+
+        for (int i = 0; i < height; i++)
+        {
+            for (int j = 0; j < width; j++)
+            {
+                if (j == fixedX)
+                {
+                    fValX = (double)((i - origin[1])*spacing[1]);
+                    fValY = (double)(it_2D.Get());
+                    curPt.setX(fValX);
+                    curPt.setY(fValY);
+
+                    vProfile.push_back(curPt);
+                }
+                ++it_2D;
+            }
+            if (it_2D.IsAtEnd())
+                break;
+        }
+    }
+
+    if (vProfile.empty())
+        return false;
+
+
+    return true;
+
+}
+
+void QUTIL::LoadFloatImage2D(const char* filePath, FloatImage2DType::Pointer& spTargImg2D)
+{
+    typedef itk::ImageFileReader<FloatImage2DType> ReaderType;
+    ReaderType::Pointer reader = ReaderType::New();
+
+    QString strPath = filePath;
+
+    if (strPath.length() < 1)
+        return;
+
+    reader->SetFileName(strPath.toLocal8Bit().constData());
+    reader->Update();
+
+    spTargImg2D = reader->GetOutput();
+}
+
+void QUTIL::LoadFloatImage3D(const char* filePath, FloatImageType::Pointer& spTargImg3D)
+{
+    typedef itk::ImageFileReader<FloatImageType> ReaderType;
+    ReaderType::Pointer reader = ReaderType::New();//error!
+
+    QString strPath = filePath;
+
+
+    //strPath = "D:\\RD_Beam2\\RD_Beam1_comp.mha";
+    if (strPath.length() < 1)
+        return;
+
+    reader->SetFileName(strPath.toLocal8Bit().constData());
+    reader->Update();
+
+    spTargImg3D = reader->GetOutput();
+}
+
+void QUTIL::SaveFloatImage2D(const char* filePath, FloatImage2DType::Pointer& spSrcImg2D)
+{
+    if (!spSrcImg2D)
+        return;
+
+    QString strPath = filePath;
+
+    if (strPath.length() < 1)
+        return;
+
+    typedef itk::ImageFileWriter<FloatImage2DType> WriterType;
+    WriterType::Pointer writer = WriterType::New();
+
+    writer->SetFileName(strPath.toLocal8Bit().constData());
+    writer->SetUseCompression(true); //not exist in original code (rtkfdk)	
+    writer->SetInput(spSrcImg2D);
+    writer->Update();
+
+    cout << "Writing image file was succeeded: " << strPath.toLocal8Bit().constData() << endl;
+}
+
+void QUTIL::SaveFloatImage3D(const char* filePath, FloatImageType::Pointer& spSrcImg3D)
+{
+    if (!spSrcImg3D)
+        return;
+
+    QString strPath = filePath;
+
+    if (strPath.length() < 1)
+        return;
+
+    typedef itk::ImageFileWriter<FloatImageType> WriterType;
+    WriterType::Pointer writer = WriterType::New();
+
+    writer->SetFileName(strPath.toLocal8Bit().constData());
+    writer->SetUseCompression(true); //not exist in original code (rtkfdk)	
+    writer->SetInput(spSrcImg3D);
+    writer->Update();
+
+    cout << "Writing image file was succeeded: " << strPath.toLocal8Bit().constData() << endl;   
+
+}
+
+QStringList QUTIL::LoadTextFile(const char* txtFilePath)
+{
+    QStringList resultStrList;
+
+    ifstream fin;
+    fin.open(txtFilePath);
+
+    if (fin.fail())
+        return resultStrList;
+
+    char str[MAX_LINE_LENGTH];    
+
+    while (!fin.eof())
+    {
+        memset(str, 0, MAX_LINE_LENGTH);
+        fin.getline(str, MAX_LINE_LENGTH);
+        QString tmpStr = QString(str);
+
+        resultStrList.append(tmpStr);
+        //resultStrList.append("\n");
+    }
+   
+    fin.close();
+
+    return resultStrList;
+}
+
+void QUTIL::LoadColorTable(const char* filePath, vector<VEC3D>& vRGBTable)
+{
+    vRGBTable.clear();
+
+    QStringList resultStrList;
+
+    ifstream fin;
+    fin.open(filePath);
+
+    if (fin.fail())
+    {
+        cout << "No such file found: " << filePath << endl;
+        return;
+    }     
+
+    char str[MAX_LINE_LENGTH];
+    VEC3D curRGB;
+    while (!fin.eof())
+    {
+        memset(str, 0, MAX_LINE_LENGTH);
+        fin.getline(str, MAX_LINE_LENGTH);
+        QString tmpStr = QString(str);
+        resultStrList = tmpStr.split("\t");
+
+        if (resultStrList.count() == 3)
+        {
+            curRGB.x = resultStrList.at(0).toFloat();
+            curRGB.y = resultStrList.at(1).toFloat();
+            curRGB.z = resultStrList.at(2).toFloat();
+        }
+        vRGBTable.push_back(curRGB);
+    }
+    fin.close();
+    return;
+}
+
+VEC3D QUTIL::GetRGBValueFromTable(vector<VEC3D>& vRGBTable, float fMinGray, float fMaxGray, float fLookupGray)
+{
+    VEC3D resultRGB = { 0.0, 0.0, 0.0 };
+
+    float width = fMaxGray - fMinGray;
+
+    if (width <= 0)
+        return resultRGB;
+
+    float fractionGray = (fLookupGray - fMinGray) / width;
+
+    int numDiscret = vRGBTable.size();
+
+    if (numDiscret < 1)
+        return resultRGB;
+
+    int lookupIdx = qRound(fractionGray*numDiscret);
+
+    if (lookupIdx < numDiscret)
+    {
+        resultRGB = vRGBTable.at(lookupIdx);
+    }
+    else
+    {
+        resultRGB = vRGBTable.at(numDiscret - 1);
+    }
+    return resultRGB;
+}
+
+QString QUTIL::GetTimeStampDirPath(const QString& curDirPath, const QString& preFix, const QString& endFix)
+{
+    QDate curDate = QDate::currentDate();
+    QString strDateStamp = curDate.toString("YYMMDD");
+    QTime curTime = QTime::currentTime();
+    QString strTimeStamp = curTime.toString("hhmmss");
+    //QDir tmpPlmDir = QDir(curDirPath);
+
+  /*  if (!tmpPlmDir.exists())
+    {
+        cout << "Error! No curDirPath is available." << tmpPlmDir.absolutePath().toLocal8Bit().constData() << endl;
+        return;
+    }*/
+
+    QString strOutput = curDirPath + "/" + preFix + strDateStamp+"_" +strTimeStamp + endFix;
+
+    return strOutput;
+}
+
+
+QString QUTIL::GetTimeStampDirName(const QString& preFix, const QString& endFix)
+{
+    QDate curDate = QDate::currentDate();
+    QString strDateStamp = curDate.toString("yyMMdd");
+    QTime curTime = QTime::currentTime();
+    QString strTimeStamp = curTime.toString("hhmmss");   
+
+    QString strOutput = preFix + strDateStamp + "_" + strTimeStamp + endFix;
+    return strOutput;
+}
+
+void QUTIL::ShowErrorMessage(QString str)
+{
+    QMessageBox msgBox;
+    msgBox.setText(str);
+    msgBox.exec();
+}
+
+void QUTIL::CreateItkDummyImg(FloatImageType::Pointer& spTarget, int sizeX, int sizeY, int sizeZ, float fillVal)
+{
+    FloatImageType::IndexType idxStart;
+    idxStart[0] = 0;
+    idxStart[1] = 0;
+    idxStart[2] = 0;
+
+    FloatImageType::SizeType size3D;
+    size3D[0] = sizeX;
+    size3D[1] = sizeY;
+    size3D[2] = sizeZ;
+
+    FloatImageType::SpacingType spacing3D;
+    spacing3D[0] = 1;
+    spacing3D[1] = 1;
+    spacing3D[2] = 1;
+
+    FloatImageType::PointType origin3D;
+    
+    origin3D[0] = size3D[0] * spacing3D[0] / -2.0;
+    origin3D[1] = size3D[1] * spacing3D[1] / -2.0;
+
+    FloatImageType::RegionType region;
+    region.SetSize(size3D);
+    region.SetIndex(idxStart);
+
+    //spTargetImg2D is supposed to be empty.
+    if (spTarget)
+    {
+        cout << "something is here in target image. is it gonna be overwritten?" << endl;
+    }
+
+    spTarget = FloatImageType::New();
+    spTarget->SetRegions(region);
+    spTarget->SetSpacing(spacing3D);
+    spTarget->SetOrigin(origin3D);
+
+    spTarget->Allocate();
+    spTarget->FillBuffer(fillVal);
+}
+
+void QUTIL::PrintStrList(QStringList& strList)
+{
+    int size = strList.count();
+
+    for (int i = 0; i < size; i++)
+    {
+        cout << strList.at(i).toLocal8Bit().constData() << endl;
+    }
+}
+
+QString QUTIL::GetPathWithEndFix(const QString& curFilePath, const QString& strEndFix)
+{
+    QFileInfo fInfo(curFilePath);
+    /*QString strDirPath = fInfo.absolutePath();
+    QString strBaseName = fInfo.completeBaseName();
+    QString strSuffixName = fInfo.completeSuffix();*/
+
+    QString strResult = fInfo.absolutePath() + "/" + fInfo.completeBaseName() + strEndFix + "." + fInfo.completeSuffix();
+    return strResult;
+}
+
+//
+//void QUTIL::UpdateFloatTable3(vector<QPointF>& vData1, vector<QPointF>& vData2, vector<QPointF>& vData3,
+//    QStandardItemModel* pTableModel, gamma_gui* pParent)
+//{ 
+//    int numOfData = 3;
+//
+//    if (pTableModel != NULL)
+//    {
+//        delete pTableModel;
+//        pTableModel = NULL;
+//    }
+//
+//    int columnSize = 1;
+//    int rowSize1, rowSize2, rowSize3 = 0;
+//
+//    columnSize = numOfData * 2;
+//
+//    rowSize1 = vData1.size();
+//    rowSize2 = vData2.size();
+//    rowSize3 = vData3.size();
+//
+//    int maxRowSize = 0;
+//    if (rowSize1 > rowSize2)
+//    {
+//        if (rowSize1 < rowSize3)
+//            maxRowSize = rowSize3;
+//        else
+//            maxRowSize = rowSize1;
+//
+//    }
+//    else
+//    {
+//        if (rowSize2 < rowSize3)
+//            maxRowSize = rowSize3;
+//        else
+//            maxRowSize = rowSize2;
+//    }
+//
+//    if (maxRowSize == 0)
+//    {
+//        cout << "MaxRowSize is 0" << endl;
+//        return;
+//    }
+//     
+//
+//    pTableModel = new QStandardItemModel(maxRowSize, columnSize, pParent); //2 Rows and 3 Columns
+//    pTableModel->setHorizontalHeaderItem(0, new QStandardItem(QString("x1")));
+//    pTableModel->setHorizontalHeaderItem(1, new QStandardItem(QString("y1")));
+//    pTableModel->setHorizontalHeaderItem(2, new QStandardItem(QString("x2")));
+//    pTableModel->setHorizontalHeaderItem(3, new QStandardItem(QString("y2")));
+//    pTableModel->setHorizontalHeaderItem(4, new QStandardItem(QString("x3")));
+//    pTableModel->setHorizontalHeaderItem(5, new QStandardItem(QString("y3")));    
+//    
+//
+//    for (int i = 0; i < maxRowSize; i++)
+//    {
+//        qreal tmpVal1 = vData1.at(i).x();
+//        qreal tmpVal2 = vData1.at(i).y();
+//
+//        pTableModel->setItem(i, 0, new QStandardItem(QString("%1").arg(tmpVal1)));        
+//        pTableModel->setItem(i, 1, new QStandardItem(QString("%1").arg(tmpVal2)));
+//
+//        if (i < rowSize2)
+//        {
+//
+//            tmpVal1 = vData2.at(i).x();
+//            tmpVal2 = vData2.at(i).y();
+//            pTableModel->setItem(i, 2, new QStandardItem(QString("%1").arg(tmpVal1)));
+//            pTableModel->setItem(i, 3, new QStandardItem(QString("%1").arg(tmpVal2)));
+//        }
+//
+//        if (i < rowSize3)
+//        {
+//            tmpVal1 = vData3.at(i).x();
+//            tmpVal2 = vData3.at(i).y();
+//            pTableModel->setItem(i, 4, new QStandardItem(QString("%1").arg(tmpVal1)));
+//            pTableModel->setItem(i, 5, new QStandardItem(QString("%1").arg(tmpVal2)));
+//        }
+//    }
+//
+//    if (pTableModel == NULL)
+//    {
+//        cout << "weird!" << endl;
+//    }
+//}
+
+
+
+void QUTIL::GenDefaultCommandFile(QString strPathCommandFile, enRegisterOption regiOption)
+{
+    ofstream fout;
+    fout.open(strPathCommandFile.toLocal8Bit().constData());
+
+    if (fout.fail())
+    {
+        cout << "File writing error! " << endl;
+        return;
+    }
+
+    fout << "#Plastimatch command file for registration.txt" << endl;
+    fout << "[GLOBAL]" << endl;
+    fout << "fixed=" << "TBD" << endl;
+    fout << "moving=" << "TBD" << endl;
+
+   /* if (strPathFixedMask.length() > 1)
+    {
+        fout << "fixed_roi=" << "TBD" << endl;
+    }*/
+    fout << "img_out=" << "TBD" << endl;
+    fout << "xform_out=" << "TBD" << endl;
+    fout << endl;
+    
+    //QString strOptim = "mse";    
+    QString optionStr;
+    QStringList optionList;
+
+    switch (regiOption)
+    {
+    case PLAST_RIGID:
+        fout << "[STAGE]" << endl;
+        fout << "xform=" << "rigid" << endl;        
+        fout << "optim=" << "versor" << endl;
+        fout << "impl=" << "itk" << endl;
+        fout << "threading=" << "openmp" << endl;
+        fout << "background_val=" << "-1024" << endl;
+        //fout << "background_val=" << "0" << endl; //-600 in HU //added
+        fout << "max_its=" << "50" << endl;
+        break;
+
+    case PLAST_AFFINE:
+        fout << "[STAGE]" << endl;
+        fout << "xform=" << "rigid" << endl;
+        fout << "optim=" << "versor" << endl;
+        fout << "impl=" << "itk" << endl;
+        fout << "threading=" << "openmp" << endl;
+        fout << "background_val=" << "-1024" << endl;
+        //fout << "background_val=" << "0" << endl; //-600 in HU //added
+        fout << "max_its=" << "50" << endl;
+        fout << endl;
+        break;
+
+    case PLAST_GRADIENT:
+        fout << "#For gradient-based searching, moving image should be smaller than fixed image. So, CBCT image might move rather than CT" << endl;
+
+        optionStr = "0.7, 0.7, 0.7";
+        optionList = optionStr.split(",");
+
+        fout << "[PROCESS]" << endl;
+        fout << "action=adjust" << endl;
+        fout << "# only consider within this  intensity values" << endl;
+        fout << "parms=-inf,0,-1000,-1000,4000,4000,inf,0" << endl;
+        fout << "images=fixed,moving" << endl;
+        fout << endl;
+        fout << "[STAGE]" << endl;
+        fout << "metric=gm" << endl;
+        fout << "xform=translation" << endl;
+        fout << "optim=grid_search" << endl;
+        fout << "gridsearch_min_overlap=" << optionList.at(0).toDouble() << " "
+            << optionList.at(1).toDouble() << " "
+            << optionList.at(2).toDouble() << endl;
+
+        fout << "num_substages=5" << endl;
+        //fout << "debug_dir=" << m_strPathPlastimatch.toLocal8Bit().constData() << endl;
+        break;
+
+    case PLAST_BSPLINE:        
+            fout << "[STAGE]" << endl;
+            fout << "xform=" << "bspline" << endl;
+            fout << "impl=" << "plastimatch" << endl;            
+            fout << "threading=" << "openmp" << endl;
+            fout << "regularization_lambda=" << 0.005 << endl;            
+            fout << "metric=" << "mse" << endl;
+            fout << "max_its=" << 30 << endl;
+            fout << "grid_spac=" << "30" << " " << "30" << " " << "30" << endl;//20 20 20 --> minimum
+            fout << "res=" << "2" << " " << "2" << " " << "1" << endl;
+            fout << "background_val=" << "-1024" << endl; //-600 in HU //added
+           // fout << "img_out=" << "TBD" << endl;
+            fout << endl;
+        break; 
+    }
+    fout.close();    
+}
+
+
+void QUTIL::GetGeometricLimitFloatImg(FloatImageType::Pointer& spFloatImg, VEC3D& limitStart, VEC3D& limitEnd)
+{
+    if (!spFloatImg)
+    {
+        limitStart.x = 0.0;
+        limitStart.y = 0.0;
+        limitStart.z = 0.0;
+        limitEnd.x = 0.0;
+        limitEnd.y = 0.0;
+        limitEnd.z = 0.0;
+        return;
+    }
+     
+    FloatImageType::SizeType imgSize = spFloatImg->GetLargestPossibleRegion().GetSize();
+    FloatImageType::PointType origin = spFloatImg->GetOrigin();
+    FloatImageType::SpacingType spacing = spFloatImg->GetSpacing();
+
+    limitStart.x = origin[0];
+    limitStart.y = origin[1];
+    limitStart.z = origin[2];
+    limitEnd.x = limitStart.x + (imgSize[0] - 1)*spacing[0];
+    limitEnd.y = limitStart.y + (imgSize[1] - 1)*spacing[1];
+    limitEnd.z = limitStart.z + (imgSize[2] - 1)*spacing[2];
+}
diff --git a/src/plastimatch/standalone/qt_util.h b/src/plastimatch/standalone/qt_util.h
new file mode 100644
index 0000000..115c962
--- /dev/null
+++ b/src/plastimatch/standalone/qt_util.h
@@ -0,0 +1,74 @@
+#ifndef QT_UTIL_H
+#define QT_UTIL_H
+
+#include "yk_config.h"
+#include "itk_image_type.h"
+//#include "itkImage.h"
+
+
+#include <QPointF>
+#include <QString>
+#include <QStringList>
+
+using namespace std;
+
+class QStandardItemModel;
+class gamma_gui;
+
+namespace QUTIL{    
+    void Set2DTo3D(FloatImage2DType::Pointer& spSrcImg2D, UShortImageType::Pointer& spTargetImg3D, int idx, enPLANE iDirection);    
+
+    void Get2DFrom3DByIndex(UShortImageType::Pointer& spSrcImg3D, UShortImage2DType::Pointer& spTargetImg2D, int idx, enPLANE iDirection);
+    void Get2DFrom3DByIndex(FloatImageType::Pointer& spSrcImg3D, FloatImage2DType::Pointer& spTargetImg2D, int idx, enPLANE iDirection);
+
+    void Get2DFrom3DByPosition(UShortImageType::Pointer& spSrcImg3D, UShortImage2DType::Pointer& spTargImg2D, enPLANE iDirection, double pos, double& finalPos);
+    void Get2DFrom3DByPosition(FloatImageType::Pointer& spSrcImg3D, FloatImage2DType::Pointer& spTargImg2D, enPLANE iDirection, double pos, double& finalPos);
+        
+    bool GetProfile1DByPosition(UShortImage2DType::Pointer& spSrcImg2D, vector<QPointF>& vProfile, float fixedPos, enPROFILE_DIRECTON enDirection);
+    bool GetProfile1DByPosition(FloatImage2DType::Pointer& spSrcImg2D, vector<QPointF>& vProfile, float fixedPos, enPROFILE_DIRECTON enDirection);
+
+    bool GetProfile1DByIndex(UShortImage2DType::Pointer& spSrcImg2D, vector<QPointF>& vProfile, int fixedIndex, enPROFILE_DIRECTON enDirection);
+    bool GetProfile1DByIndex(FloatImage2DType::Pointer& spSrcImg2D, vector<QPointF>& vProfile, int fixedIndex, enPROFILE_DIRECTON enDirection);
+
+    void LoadFloatImage2D(const char* filePath, FloatImage2DType::Pointer& spTargImg2D);
+    void LoadFloatImage3D(const char* filePath, FloatImageType::Pointer& spTargImg3D);
+
+    void SaveFloatImage2D(const char* filePath, FloatImage2DType::Pointer& spSrcImg2D);
+    void SaveFloatImage3D(const char* filePath, FloatImageType::Pointer& spSrcImg3D);
+
+    QStringList LoadTextFile(const char* txtFilePath);
+
+    void LoadColorTable(const char* filePath, vector<VEC3D>& vRGBTable);
+    VEC3D GetRGBValueFromTable(vector<VEC3D>& vRGBTable, float fMinGray, float fMaxGray, float fLookupGray);
+
+    
+    QString GetTimeStampDirPath(const QString& curDirPath, const QString& preFix = QString(""), const QString& endFix = QString(""));
+
+    QString GetTimeStampDirName(const QString& preFix = QString(""), const QString& endFix = QString(""));
+
+    void ShowErrorMessage(QString str);
+
+    void CreateItkDummyImg(FloatImageType::Pointer& spTarget, int sizeX, int sizeY, int sizeZ, float fillVal);//spacing: 1, origin: 0;
+
+    void PrintStrList(QStringList& strList);
+
+    QString GetPathWithEndFix(const QString& curFilePath, const QString& strEndFix);
+
+    void GenDefaultCommandFile(QString strPathCommandFile, enRegisterOption regiOption);
+
+    void GetGeometricLimitFloatImg (FloatImageType::Pointer& spFloatImg, VEC3D& limitStart, VEC3D& limitEnd);
+
+    //void UpdateTable3(vector<QPointF>& vData1, vector<QPointF>& vData2, vector<QPointF>& vData3, QTableModel* pTableModel, QTableModel* pTableView);
+    //void UpdateFloatTable3(vector<QPointF>& vData1, vector<QPointF>& vData2, vector<QPointF>& vData3,
+        //QStandardItemModel* pTableModel, gamma_gui* pParent);
+
+
+//    typedef itk::ImageFileReader<FloatImageType> ReaderTypeYK;
+
+
+};
+
+#endif // QT_UTIL_H
+
+
+
diff --git a/src/plastimatch/standalone/qyklabel.cpp b/src/plastimatch/standalone/qyklabel.cpp
new file mode 100644
index 0000000..4ef6519
--- /dev/null
+++ b/src/plastimatch/standalone/qyklabel.cpp
@@ -0,0 +1,459 @@
+#include "qyklabel.h"
+#include <QPainter>
+#include "YK16GrayImage.h"
+
+using namespace std;
+
+qyklabel::qyklabel(QWidget *parent)
+	: QLabel(parent)
+{
+	m_pYK16Image = NULL;
+	//this->width();
+	//m_Rt = this->rect();
+
+	//m_Rt.setRect()
+	m_bDrawPoints = true;
+
+	m_iMouseWheelDelta = 0;
+
+	m_bFocusIn = false;
+
+}
+
+qyklabel::~qyklabel()
+{
+}
+
+void qyklabel::mouseMoveEvent( QMouseEvent *ev )
+{
+	if (m_pYK16Image == NULL)
+		return;
+	
+	this->x	= ev->x();
+	this->y = ev->y();
+	emit Mouse_Move();
+}
+
+void qyklabel::mousePressEvent( QMouseEvent *ev )
+{
+	if (m_pYK16Image == NULL)
+		return;
+	this->x	= ev->x();
+	this->y = ev->y();
+
+	if (ev->button() == Qt::LeftButton)
+	{	 
+		emit Mouse_Pressed_Left();
+	}
+	if (ev->button() == Qt::RightButton)
+	{	  
+		emit Mouse_Pressed_Right();
+	}
+}
+
+
+void qyklabel::mouseDoubleClickEvent(QMouseEvent *ev)
+{
+    if (m_pYK16Image == NULL)
+        return;
+
+    if (ev->button() == Qt::LeftButton)
+    {
+        emit Mouse_Left_DoubleClick();
+    }
+    if (ev->button() == Qt::RightButton)
+    {
+        emit Mouse_Right_DoubleClick();
+    }
+}
+
+
+void qyklabel::mouseReleaseEvent( QMouseEvent *ev )
+{
+  if (m_pYK16Image == NULL)
+	return;
+
+  //cout << "Mouse Released from qlabel" << endl;//it worked
+
+  if (ev->button() == Qt::LeftButton)
+  {	
+	emit Mouse_Released_Left();
+  }
+  if (ev->button() == Qt::RightButton)
+  {
+	emit Mouse_Released_Right();  
+  }
+}
+
+
+void qyklabel::wheelEvent( QWheelEvent * event )
+{
+  if (m_pYK16Image == NULL)
+	return;
+
+  m_iMouseWheelDelta = (int)(event->delta()/100.0);
+  emit Mouse_Wheel();
+}
+
+void qyklabel::enterEvent (QEvent *)
+{
+  if (m_pYK16Image == NULL)
+	return;
+
+  m_bFocusIn = true;
+
+  emit FocusIn();
+}
+
+void qyklabel::leaveEvent( QEvent * )
+{  
+	if (m_pYK16Image == NULL)
+		return;	
+
+
+	m_bFocusIn = false;
+
+	emit FocusOut();
+}
+
+//void focusInEvent ( QFocusEvent * ev );
+//void focusOutEvent ( QFocusEvent * ev );
+//
+//void qyklabel::focusInEvent( QFocusEvent * )
+//{
+//  cout <<"focus in" << endl;
+//  emit FocusIn();  
+//}
+//
+//void qyklabel::focusOutEvent( QFocusEvent * )
+//{
+//  cout <<"focus out" << endl;
+//  emit FocusOut();  
+//}
+
+void qyklabel::paintEvent( QPaintEvent * )
+{
+	QPainter painter(this);
+
+	painter.setPen( QPen(Qt::black, 2));
+	QRect TargetRt = this->rect();	
+	painter.drawRect(TargetRt);
+
+	if (m_pYK16Image == NULL)
+		return;
+
+	if (m_pYK16Image->m_iWidth < 1 || m_pYK16Image->m_iHeight<1)
+		return;
+
+        double VH_ratio = 0.0; //if horizontal is longer than vertical        
+
+        bool bHorLonger = false;
+
+        double physHor = 0.0;
+        double physVer = 0.0;
+
+        int labelNewFixedWidth = 0;
+        int labelNewFixedHeight = 0;
+        
+
+        if (m_pYK16Image->m_fSpacingX*m_pYK16Image->m_fSpacingY == 0)
+        {         
+            physHor = (double)m_pYK16Image->m_iWidth;
+            physVer = (double)m_pYK16Image->m_iHeight;
+        }
+        else
+        {
+            physHor = m_pYK16Image->m_iWidth * m_pYK16Image->m_fSpacingX;
+            physVer = m_pYK16Image->m_iHeight* m_pYK16Image->m_fSpacingY;
+        }
+
+        VH_ratio = physVer / physHor;
+
+        if (physHor > physVer)            
+        {
+            bHorLonger = true;           
+            int newFixedHeight = qRound(this->width() * VH_ratio);
+            this->setFixedHeight(newFixedHeight);
+        }
+        else
+        {
+            bHorLonger = false;           
+            int newFixedWidth = qRound(this->height() / VH_ratio);
+            this->setFixedWidth(newFixedWidth);
+        }            
+
+
+	//Calculate ver / hor ratio.
+	/*VH_ratio = m_pYK16Image->m_iHeight / (double) m_pYK16Image->m_iWidth;
+	int newFixedHeight = qRound(this->width() * VH_ratio);
+	this->setFixedHeight(newFixedHeight);*/
+
+	if (m_pYK16Image != NULL)
+	{
+		//QRect imgSrcRect;
+		//imgSrcRect.setRect(0,0,m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight);
+		//painter.drawImage(rect(), m_pYK16Image->m_QImage,imgSrcRect,);
+
+		//int width =  m_pYK16Image->m_QImage.width();
+		//int height =  m_pYK16Image->m_QImage.height();
+
+		//m_pYK16Image->m_QImage.save("C:\\111.png");
+
+		//QImage tmpQImage = QImage("C:\\FromFillPixmap.png");
+
+		//QImage tmpQImage = m_pYK16Image->m_QImage;
+	
+		//painter.drawImage(TargetRt, m_pYK16Image->m_QImage, imgSrcRect, QT::RGB888);
+		//painter.drawImage(TargetRt, m_pYK16Image->m_QImage, imgSrcRect);
+		//painter.drawImage(TargetRt, m_pYK16Image->m_QImage); //it Works!YKTEMP
+                painter.drawImage(TargetRt, m_pYK16Image->m_QImage); //it Works!YKTEMP
+	}
+	
+	if (m_bDrawPoints)
+	{
+		painter.setPen( QPen(Qt::red, 2));
+		vector<QPoint>::iterator it;
+		for (it = m_vPt.begin() ; it != m_vPt.end() ; it++)
+		{
+			painter.drawPoint((*it).x(),(*it).y());
+		}
+	}	
+
+	if (m_pYK16Image->m_bDrawROI)
+	{
+		painter.setPen( QPen(Qt::red, 2));
+		QRect rtDraw; 
+		rtDraw.setTopLeft(Data2View(QPoint(m_pYK16Image->m_rtROI.left(), m_pYK16Image->m_rtROI.top()), this->width(), this->height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight));
+		rtDraw.setBottomRight(Data2View(QPoint(m_pYK16Image->m_rtROI.right(), m_pYK16Image->m_rtROI.bottom()), this->width(), this->height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight));
+
+		painter.drawRect(rtDraw);
+	}
+	//if x, y >0 
+	if (m_pYK16Image->m_bDrawProfileX)
+	{
+		QPoint crntViewPt = Data2View(m_pYK16Image->m_ptProfileProbe, this->width(), this->height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight);
+		painter.setPen( QPen(Qt::red, 1, Qt::DotLine));		
+		painter.drawLine(0, crntViewPt.y(), this->width()-1, crntViewPt.y());
+		
+	}
+	if (m_pYK16Image->m_bDrawProfileY)
+	{
+		QPoint crntViewPt = Data2View(m_pYK16Image->m_ptProfileProbe, this->width(), this->height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight);
+		painter.setPen( QPen(Qt::red, 1, Qt::DotLine));
+		painter.drawLine(crntViewPt.x(), 0 ,crntViewPt.x(), this->height()-1);
+	}
+
+	if (m_pYK16Image->m_bDrawFOVCircle)
+	{
+		painter.setPen( QPen(Qt::yellow, 1, Qt::SolidLine));
+		QPoint crntCenterPt = Data2View(m_pYK16Image->m_ptFOVCenter, this->width(), this->height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight);		
+		int crntRadius = (int)(m_pYK16Image->m_iFOVRadius / (double)m_pYK16Image->m_iWidth * this->width());
+		painter.drawEllipse(crntCenterPt,crntRadius, crntRadius);
+	}
+
+	if (m_pYK16Image->m_bDrawTableLine)
+	{
+		painter.setPen( QPen(Qt::yellow, 1, Qt::SolidLine));
+		int crntTablePosY = (int)(m_pYK16Image->m_iTableTopPos / (double)m_pYK16Image->m_iHeight * this->height());
+		//int crntRadius = (int)(m_pYK16Image->m_iFOVRadius / (double)m_pYK16Image->m_iWidth * this->width());
+		painter.drawLine(0, crntTablePosY ,this->width()-1, crntTablePosY);
+		//painter.drawEllipse(crntCenterPt,crntRadius, crntRadius);
+	}
+
+	if (m_pYK16Image->m_bDrawCrosshair) //objects should be addressed one by one
+	{
+	  painter.setPen( QPen(Qt::yellow, 1, Qt::SolidLine));
+	  //QPoint crosshair;
+
+	  int dispCrossX = (int)(m_pYK16Image->m_ptCrosshair.x() / (double)m_pYK16Image->m_iWidth * this->width());
+	  int dispCrossY = (int)(m_pYK16Image->m_ptCrosshair.y() / (double)m_pYK16Image->m_iHeight * this->height());
+
+	  QPoint ptDispCrosshair = GetViewPtFromDataPt(m_pYK16Image->m_ptCrosshair.x(), m_pYK16Image->m_ptCrosshair.y());
+		//crosshair.setX(crossX);
+		//crosshair.setY(crossY);
+
+		//int crntRadius = (int)(m_pYK16Image->m_iFOVRadius / (double)m_pYK16Image->m_iWidth * this->width());
+	  painter.drawLine(0, ptDispCrosshair.y() ,this->width()-1, ptDispCrosshair.y());
+	  painter.drawLine(ptDispCrosshair.x(), 0, ptDispCrosshair.x(), this->height()-1);
+	}
+
+        if (m_pYK16Image->m_bDrawOverlayText && m_pYK16Image->m_strOverlayText.length() > 1) //objects should be addressed one by one
+        {
+            /*this->setText(m_pYK16Image->m_strOverlayText);
+            this->setFont(QFont("Helvetica", 12, 1, false));
+            this->setAlignment(Qt::AlignBottom);*/
+            int rtWidth = this->width();
+            int rtHeight = this->height() / 7.0;
+            int leftPos = 10;
+            int topPos = qRound(this->height() - rtHeight);
+
+            QRect rt = QRect(leftPos, topPos, rtWidth, rtHeight);
+            painter.setPen(QPen(Qt::white, 3, Qt::SolidLine));
+            painter.drawText(rt, m_pYK16Image->m_strOverlayText);
+        }
+}
+
+void qyklabel::SetBaseImage( YK16GrayImage* pYKImage )
+{
+	if (pYKImage->m_pData != NULL && !pYKImage->m_QImage.isNull()) //YKTEMP
+		m_pYK16Image = pYKImage;
+}
+
+void qyklabel::ConvertAndCopyPoints(vector<QPoint>& vSrcPoint, int iDataWidth, int iDataHeight)
+{
+	m_vPt.clear();	
+
+	int dspWidth = this->width();
+	int dspHeight = this->height();
+
+
+	vector<QPoint>::iterator it;
+
+	for (it = vSrcPoint.begin() ; it != vSrcPoint.end() ; it++)
+	{
+		QPoint tmpDspPt = Data2View((*it),dspWidth, dspHeight, iDataWidth, iDataHeight);
+		m_vPt.push_back(tmpDspPt);
+	}
+}
+
+
+QPoint qyklabel::View2Data(QPoint viewPt, int viewWidth, int viewHeight, int dataWidth, int dataHeight)
+{
+	double fZoomX = viewWidth / (double)dataWidth;
+	double fZoomY = viewHeight / (double)dataHeight;
+
+	QPoint dataPt;
+	dataPt.setX(qRound(viewPt.x() / fZoomX));
+	dataPt.setY(qRound(viewPt.y() / fZoomY));
+
+	return dataPt;
+}
+
+QPoint qyklabel::Data2View(QPoint dataPt, int viewWidth, int viewHeight, int dataWidth, int dataHeight)
+{
+	double fZoomX = viewWidth / (double)dataWidth;
+	double fZoomY = viewHeight / (double)dataHeight;
+
+	QPoint viewPt;
+	viewPt.setX(qRound(dataPt.x() * fZoomX));
+	viewPt.setY(qRound(dataPt.y() * fZoomY));
+
+	return viewPt;
+}
+
+void qyklabel::SetDrawPointToggle(bool bToggle )
+{
+	if (bToggle)
+		m_bDrawPoints = true;
+	else
+		m_bDrawPoints = false;
+
+
+	update();
+}
+
+QPoint qyklabel::GetDataPtFromMousePos()
+{
+  if (m_pYK16Image == NULL)
+	return QPoint(0,0);
+
+  
+  if (m_pYK16Image->m_fZoom == 1.0 && m_pYK16Image->m_iOffsetX == 0 && m_pYK16Image->m_iOffsetY == 0)
+	return View2Data(QPoint(this->x, this->y), width(), height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight);  
+  else
+	return View2DataExt(QPoint(this->x, this->y), width(), height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight, QPoint(m_pYK16Image->m_iOffsetX,m_pYK16Image->m_iOffsetY),m_pYK16Image->m_fZoom );
+
+}
+
+QPoint qyklabel::GetDataPtFromViewPt(int viewPtX, int viewPtY)
+{
+  if (m_pYK16Image == NULL)
+	return QPoint(0,0);  
+
+  if (m_pYK16Image->m_fZoom == 1.0 && m_pYK16Image->m_iOffsetX == 0 && m_pYK16Image->m_iOffsetY == 0)
+	return View2Data(QPoint(viewPtX, viewPtY), width(), height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight);  
+  else
+	return View2DataExt(QPoint(viewPtX, viewPtY), width(), height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight, QPoint(m_pYK16Image->m_iOffsetX,m_pYK16Image->m_iOffsetY),m_pYK16Image->m_fZoom ); 
+}
+
+QPoint qyklabel::GetViewPtFromDataPt(int dataPtX, int dataPtY)
+{
+  if (m_pYK16Image == NULL)
+	return QPoint(0,0);  
+
+  if (m_pYK16Image->m_fZoom == 1.0 && m_pYK16Image->m_iOffsetX == 0 && m_pYK16Image->m_iOffsetY == 0)
+	return Data2View(QPoint(dataPtX, dataPtY), width(), height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight);
+  else
+	return Data2ViewExt(QPoint(dataPtX, dataPtY), width(), height(), m_pYK16Image->m_iWidth, m_pYK16Image->m_iHeight, QPoint(m_pYK16Image->m_iOffsetX,m_pYK16Image->m_iOffsetY),m_pYK16Image->m_fZoom ); 
+}
+
+
+
+QPoint qyklabel::View2DataExt(QPoint viewPt, int viewWidth, int viewHeight, int dataWidth, int dataHeight, QPoint ptDataOffset, double fUserZoom)
+{
+  // double fZoomX = viewWidth / (double)dataWidth * fUserZoom; // < 1 
+  //double fZoomY = viewHeight / (double)dataHeight * fUserZoom;  
+
+  
+  //  dataPt.setX(qRound(viewPt.x() * fZoomX  + ptDataOffset.x()));
+  //  dataPt.setY(qRound(viewPt.y() * fZoomY + ptDataOffset.y()));
+  int newWidth = qRound(dataWidth/fUserZoom);
+  int newHeight = qRound(dataHeight/fUserZoom);
+
+  int dataCenterX = ptDataOffset.x() + qRound(dataWidth/2.0);
+  int dataCenterY = ptDataOffset.y() + qRound(dataHeight/2.0);
+
+  int dataLeftTopX = dataCenterX - qRound(newWidth/2.0);//data position
+  int dataLeftTopY = dataCenterY - qRound(newHeight/2.0);	//data position
+
+
+  QPoint dataPt;
+  dataPt.setX(qRound(viewPt.x()*newWidth/(double)viewWidth + dataLeftTopX));
+  dataPt.setY(qRound(viewPt.y()*newHeight/(double)viewHeight + dataLeftTopY)); 
+
+  return dataPt;
+}
+
+QPoint qyklabel::Data2ViewExt( QPoint dataPt, int viewWidth, int viewHeight, int dataWidth, int dataHeight, QPoint ptDataOffset, double fUserZoom )
+{
+  int newWidth = qRound(dataWidth/fUserZoom);
+  int newHeight = qRound(dataHeight/fUserZoom);
+
+  int dataCenterX = ptDataOffset.x() + qRound(dataWidth/2.0);
+  int dataCenterY = ptDataOffset.y() + qRound(dataHeight/2.0);
+
+  int dataLeftTopX = dataCenterX - qRound(newWidth/2.0);//data position
+  int dataLeftTopY = dataCenterY - qRound(newHeight/2.0);	//data position
+
+
+  QPoint viewPt;
+  viewPt.setX(qRound((dataPt.x() - dataLeftTopX) * viewWidth/(double)newWidth));
+  viewPt.setY(qRound((dataPt.y() - dataLeftTopY) * viewHeight/(double)newHeight));  
+
+  return viewPt;
+}
+
+//void qyklabel::keyPressEvent( QKeyEvent* ev )
+//{  
+//  if (!isFocusIn())
+//	return;
+//
+//  int enMovingKey = -1;
+//
+//  if (ev->key() == Qt::Key_Left)
+//	enMovingKey = 0;
+//  else if (ev->key() == Qt::Key_Right)
+//	enMovingKey = 1;
+//  else if (ev->key() == Qt::Key_Up)
+//	enMovingKey = 2;
+//  else if (ev->key() == Qt::Key_Down)
+//	enMovingKey = 3;
+//  else if (ev->key() == Qt::Key_PageUp)
+//	enMovingKey = 4;
+//  else if (ev->key() == Qt::Key_PageUp)
+//	enMovingKey = 5;
+//
+//  cout << enMovingKey << endl;
+//
+//  emit ArrowPressed(enMovingKey);
+//}
\ No newline at end of file
diff --git a/src/plastimatch/standalone/qyklabel.h b/src/plastimatch/standalone/qyklabel.h
new file mode 100644
index 0000000..1aa10df
--- /dev/null
+++ b/src/plastimatch/standalone/qyklabel.h
@@ -0,0 +1,91 @@
+#ifndef QYKLABEL_H
+#define QYKLABEL_H
+
+#include <QLabel>
+#include <QMouseEvent>
+#include <QRect>
+#include <vector>
+//#include <QDebug>
+class YK16GrayImage;
+
+using namespace std;
+class qyklabel : public QLabel
+{
+	Q_OBJECT
+
+public:
+	YK16GrayImage* m_pYK16Image;
+	QRect m_Rt;
+	std::vector<QPoint> m_vPt;
+	bool m_bDrawPoints;
+	int m_iMouseWheelDelta;	
+
+	bool m_bFocusIn;
+
+public:
+	qyklabel(QWidget *parent);
+	~qyklabel();
+
+	bool isFocusIn() {return m_bFocusIn;}
+
+	// virtual function reimplementation	
+	void mousePressEvent(QMouseEvent *ev); //ev->buttons() == Qt::LeftButton
+	void mouseMoveEvent(QMouseEvent *ev); 	
+	void mouseReleaseEvent(QMouseEvent *ev);
+	void wheelEvent(QWheelEvent * event); //this->setText("Delta Value: "+QString::number(event->delta()));
+
+        void mouseDoubleClickEvent(QMouseEvent *ev);
+
+	//void keyPressEvent ( QKeyEvent *ev);
+	//void focusInEvent ( QFocusEvent * ev );
+	//void focusOutEvent ( QFocusEvent * ev );
+
+	void enterEvent (QEvent *);
+	void leaveEvent(QEvent *);
+	int x,y;
+
+	void SetBaseImage(YK16GrayImage* pYKImage);
+	//void ConvertAndCopyPoints(vector<QPoint>& vSrcPoint);
+	void ConvertAndCopyPoints(vector<QPoint>& vSrcPoint, int iDataWidth, int iDataHeight);
+
+	QPoint View2Data(QPoint viewPt, int viewWidth, int viewHeight, int dataWidth, int dataHeight);
+	QPoint View2DataExt(QPoint viewPt, int viewWidth, int viewHeight, int dataWidth, int dataHeight, QPoint ptDataOffset, double fUserZoom);
+	QPoint Data2View(QPoint dataPt, int viewWidth, int viewHeight, int dataWidth, int dataHeight);
+	QPoint Data2ViewExt(QPoint dataPt, int viewWidth, int viewHeight, int dataWidth, int dataHeight, QPoint ptDataOffset, double fUserZoom);
+
+	QPoint GetDataPtFromMousePos(); //Return data position of the mouse position.m_pYK16 image is mandatory
+	QPoint GetDataPtFromViewPt(int viewPtX, int viewPtY);
+
+	QPoint GetViewPtFromDataPt(int dataPtX, int dataPtY);
+
+
+protected:
+	void paintEvent(QPaintEvent *);
+
+signals:	
+	void Mouse_Pressed_Left();
+	void Mouse_Pressed_Right();
+	void Mouse_Move();	
+
+	void Mouse_Released_Left();
+	void Mouse_Released_Right();
+	void Mouse_Wheel();
+
+        void Mouse_Left_DoubleClick();
+        void Mouse_Right_DoubleClick();
+
+	void FocusIn();
+	void FocusOut();
+
+	//void ArrowPressed(int arrowDirection);// 0: Left, 1: Upward, 2: Right, 3: Downward
+	//void OutFromWindow();
+
+
+public slots:
+	void SetDrawPointToggle(bool bToggle);
+
+private:
+	
+};
+
+#endif // QYKLABEL_H
diff --git a/src/plastimatch/standalone/register_gui.cpp b/src/plastimatch/standalone/register_gui.cpp
new file mode 100644
index 0000000..52a19f1
--- /dev/null
+++ b/src/plastimatch/standalone/register_gui.cpp
@@ -0,0 +1,2357 @@
+#include "plm_config.h"
+#include "register_gui.h"
+#include <QString>
+#include <QFileDialog>
+#include <QListView>
+#include <QStandardItemModel>
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <QFile>
+#include <QSettings>
+#include <QTextStream>
+#include <QTimer>
+#include "YKThreadRegi.h"
+
+#include <QtGlobal>
+#include <QDesktopServices>
+#include <QUrl>
+#include <QClipboard>
+#include <QProcess>
+#include <QInputDialog>
+#include <QMessageBox>
+
+#include "qt_util.h"
+
+using namespace std;
+
+register_gui::register_gui(QWidget *parent, Qt::WFlags flags)
+: QMainWindow(parent, flags)
+{
+    ui.setupUi(this);
+    m_pTableModelMain = NULL;       
+    m_pTableModelQue = NULL;
+
+    m_iCurSelRow_Main = -1;
+    m_iCurSelCol_Main = -1;
+
+    m_iCurSelRow_Que = -1;
+    m_iCurSelCol_Que = -1;
+    m_pArrThreadRegi = NULL;
+
+    m_iNumOfThreadAll = DEFAULT_MAXNUM_QUE; 
+
+    InitTableMain(DEFAULT_MAXNUM_MAIN, DEFAULT_NUM_COLUMN_MAIN);
+    InitTableQue(DEFAULT_MAXNUM_QUE, DEFAULT_NUM_COLUMN_QUE);
+
+    /*if (m_pArrThreadRegi != NULL)
+    {
+        DeleteRemainingThreads();
+    }*/
+
+    m_pArrThreadRegi = new YKThreadRegi*[m_iNumOfThreadAll]; //200 threads object, double pointer
+    for (int i = 0; i < m_iNumOfThreadAll; i++)
+    {
+        m_pArrThreadRegi[i] = NULL;
+    }
+
+    m_timerRunSequential = new QTimer(this);
+    m_timerRunMultiThread = new QTimer(this);
+
+    connect(m_timerRunSequential, SIGNAL(timeout()), this, SLOT(SLT_TimerRunSEQ()));
+    connect(m_timerRunMultiThread, SIGNAL(timeout()), this, SLOT(SLT_TimerRunMT()));   
+
+    //Set m_strPathCommandTemplateDir in ~/ AppData / Roaming / plastimatch
+    InitConfig();
+
+    //Write default templates there
+    WriteDefaultTemplateFiles(m_strPathCommandTemplateDir);
+
+    // Read config file.  Save it, to create if the first invokation.
+    ReadDefaultConfig ();
+    WriteDefaultConfig ();
+
+    UpdateCommandFileTemplateList(m_strPathCommandTemplateDir); //from the folder
+}
+
+//make and set m_strPathCommandTemplateDir in ~/AppData/Roaming/plastimatch
+void register_gui::InitConfig()
+{
+    // Set up application configuration location
+    QCoreApplication::setOrganizationName("Plastimatch");
+    QCoreApplication::setOrganizationDomain("plastimatch.org");
+    QCoreApplication::setApplicationName("register_gui");
+
+    // Find location for command file templates.
+    // QT doesn't seem to have an API for getting the
+    // user's application data directory.  So we construct
+    // a hypothetical ini file name, then grab the directory.
+    QSettings tmpSetting(
+        QSettings::IniFormat, /* Make sure we get path, not registry */
+        QSettings::UserScope, /* Get user directory, not system direcory */
+        "Plastimatch",        /* Orginazation name (subfolder within path) */
+        "register_gui"        /* Application name (file name with subfolder) */
+        );
+
+    QString strPathTemplateBase = QFileInfo(tmpSetting.fileName()).absolutePath();//C:\Users\ykp1\AppData\Roaming/plastimatch. however, the directory was not found
+    m_strPathCommandTemplateDir = strPathTemplateBase + "/" + "CommandTemplate";    
+    
+    QDir plm_setting_dir(m_strPathCommandTemplateDir);
+
+    if (!plm_setting_dir.exists())
+    {
+        //plm_setting_dir.mkdir(m_strPathCommandTemplateDir);
+        if (!plm_setting_dir.mkpath(m_strPathCommandTemplateDir)) //The function will create all parent directories necessary to create the directory.
+        {
+            cout << "Error! Cannot make a directory for command file templates. You may not able to use templates for command file." << endl;
+            m_strPathCommandTemplateDir = "";
+        }
+        else
+        {
+            cout << "Command template path: " << m_strPathCommandTemplateDir.toLocal8Bit().constData() << endl;
+        }        
+    }
+}
+
+void register_gui::WriteDefaultTemplateFiles(QString& targetDirPath)
+{
+    QDir curDir(targetDirPath);
+    if (!curDir.exists())
+    {
+        cout << "Error! Target dir doesn't exist: " << targetDirPath.toLocal8Bit().constData() << endl;
+        return;
+    }
+    CreateDefaultCommandFile(PLAST_RIGID);
+    CreateDefaultCommandFile(PLAST_AFFINE);
+    CreateDefaultCommandFile(PLAST_BSPLINE);
+    CreateDefaultCommandFile(PLAST_GRADIENT);
+}
+
+
+
+register_gui::~register_gui()
+{ 
+    if (m_pTableModelMain != NULL)
+    {
+        delete m_pTableModelMain;
+        m_pTableModelMain = NULL;
+    }
+
+    if (m_pTableModelQue != NULL)
+    {
+        delete m_pTableModelQue;
+        m_pTableModelQue = NULL;
+    }
+
+    m_vRegiQue.clear();
+
+    DeleteRemainingThreads();
+}
+
+void register_gui::SLT_SetDefaultDir()
+{    
+    QString dirPath = QFileDialog::getExistingDirectory(this, tr("Open Work Directory"),
+        m_strPathDirDefault, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);   
+
+
+    if (dirPath.length() < 1)
+        return;    
+    
+    dirPath.replace(QString("\\"), QString("/"));
+    SetWorkDir(dirPath);
+
+    WriteDefaultConfig();
+}
+
+
+void register_gui::SLT_SetDefaultViewer()
+{
+    QString strPathExecutable = QFileDialog::getOpenFileName (
+        this, "Open executable file", "",
+#if WIN32
+        "executable file (*.exe);;"
+#endif
+        "all file (*)",
+        0, 0);
+
+    if (strPathExecutable.length() < 1)
+        return;
+
+    SetReadImageApp(strPathExecutable);
+    WriteDefaultConfig();
+}
+
+void register_gui::SetWorkDir(const QString& strPath)
+{
+    QDir dirDefaultDir = QDir(strPath);
+    if (!dirDefaultDir.exists())
+        return;
+    
+    m_strPathDirDefault = strPath;
+    ui.lineEditDefaultDirPath->setText(m_strPathDirDefault);
+}
+
+void register_gui::SetReadImageApp(const QString& strPath)
+{
+    QFileInfo ViewerFileInfo(strPath);
+    if (!ViewerFileInfo.exists())
+        return;
+
+    m_strPathReadImageApp = strPath;
+    ui.lineEditDefaultViewerPath->setText(m_strPathReadImageApp);
+}
+
+
+void register_gui::SetCommandTemplateDir(const QString& strDirPath)
+{
+    QDir dir(strDirPath);
+    
+    if (!dir.exists())
+        return;
+
+    m_strPathCommandTemplateDir = strDirPath;
+
+    //ui.lineEditDefaultViewerPath->setText(m_strPathReadImageApp);
+}
+
+void register_gui::InitTableQue(int rowCnt, int columnCnt)
+{
+    if (m_pTableModelQue != NULL)
+    {
+        delete m_pTableModelQue;
+        m_pTableModelQue = NULL;
+    }
+
+    if (columnCnt < 3)
+        return;
+
+    m_pTableModelQue = new QStandardItemModel(rowCnt, columnCnt, this);
+
+    m_pTableModelQue->setHorizontalHeaderItem(0, new QStandardItem(QString("Fixed image file")));
+    m_pTableModelQue->setHorizontalHeaderItem(1, new QStandardItem(QString("Moving image file")));
+    m_pTableModelQue->setHorizontalHeaderItem(2, new QStandardItem(QString("Command file name")));
+    m_pTableModelQue->setHorizontalHeaderItem(3, new QStandardItem(QString("____Status____")));
+    m_pTableModelQue->setHorizontalHeaderItem(4, new QStandardItem(QString("Processing time (s)")));
+    m_pTableModelQue->setHorizontalHeaderItem(5, new QStandardItem(QString("____Score1____")));
+    m_pTableModelQue->setHorizontalHeaderItem(6, new QStandardItem(QString("____Score2____")));
+
+    ui.tableView_que->setModel(m_pTableModelQue);
+    ui.tableView_que->resizeColumnsToContents();
+
+    QItemSelectionModel *select = ui.tableView_que->selectionModel();
+    //connect(select, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(SLT_SelectionChangedQue(QItemSelection, QItemSelection)));   
+}
+
+void register_gui::InitTableMain(int rowCnt, int columnCnt)
+{
+    if (m_pTableModelMain != NULL)
+    {
+        delete m_pTableModelMain;
+        m_pTableModelMain = NULL;
+    }
+
+    if (columnCnt < 3)
+        return;
+
+    m_pTableModelMain = new QStandardItemModel(rowCnt, columnCnt, this);
+
+    m_pTableModelMain->setHorizontalHeaderItem(0, new QStandardItem(QString("Fixed image file")));
+    m_pTableModelMain->setHorizontalHeaderItem(1, new QStandardItem(QString("Moving image file")));
+    m_pTableModelMain->setHorizontalHeaderItem(2, new QStandardItem(QString("Command file name")));
+    //m_pTableModelMain->setHorizontalHeaderItem(3, new QStandardItem(QString("       Status      ")));
+
+    ui.tableView_main->setModel(m_pTableModelMain);
+    ui.tableView_main->resizeColumnsToContents();
+
+    QItemSelectionModel *select = ui.tableView_main->selectionModel();
+    //connect(select, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(SLT_SelectionChangedMain(QItemSelection, QItemSelection)));  
+}
+
+void register_gui::SLT_LoadFixedFiles()
+{
+    QFileDialog w;    
+    //w.setFileMode(QFileDialog::Directory);//both files and directories are displayed 
+    w.setFileMode(QFileDialog::AnyFile);//both files and directories are displayed 
+    w.setOption(QFileDialog::DontUseNativeDialog, true);
+    QListView *l = w.findChild<QListView*>("listView");
+    if (l) {        
+        l->setSelectionMode(QAbstractItemView::ExtendedSelection);
+    }    
+    w.setDirectory(m_strPathDirDefault);
+    w.exec();
+
+    QStringList listPath = w.selectedFiles();
+    int iCntPaths = listPath.size();
+    
+    if (iCntPaths < 1)
+        return;
+
+    //m_strlistPath_Fixed.clear();
+    //m_strlistBaseName_Fixed.clear();
+    //ui.comboBox_Fixed->clear();
+
+    m_strlistPath_Fixed = listPath;
+
+    /*for (int i = 0; i < iCntPaths; i++)
+    {      
+        QFileInfo tmpInfo = QFileInfo(m_strlistPath_Fixed.at(i));
+        m_strlistBaseName_Fixed.push_back(tmpInfo.fileName());
+    }*/
+
+    UpdateBaseAndComboFromFullPath();
+    UpdateTable_Main(DATA2GUI); //When updating table, also do it for combo boxes
+
+
+ /*   QFileInfo finfo(m_strlistPath_RD_Original_Ref.at(0));
+    QDir crntDir = finfo.absoluteDir();
+    m_strPathInputDir = crntDir.absolutePath();*/
+}
+
+void register_gui::UpdateBaseAndComboFromFullPath() //Base and ComboList
+{    
+    m_strlistBaseName_Fixed.clear();
+    m_strlistBaseName_Moving.clear();
+    m_strlistBaseName_Command.clear();
+
+    ui.comboBox_Fixed->clear();
+    ui.comboBox_Moving->clear();
+    ui.comboBox_Command->clear();
+
+    int iCntFixed = m_strlistPath_Fixed.count();
+    int iCntMoving = m_strlistPath_Moving.count();
+    int iCntCommand = m_strlistPath_Command.count();
+
+    QFileInfo tmpInfo;
+    QString fileName;
+
+    for (int i = 0; i < iCntFixed; i++)
+    {
+        tmpInfo = QFileInfo(m_strlistPath_Fixed.at(i));
+        fileName = tmpInfo.fileName();
+        m_strlistBaseName_Fixed.push_back(fileName);
+        ui.comboBox_Fixed->addItem(fileName);
+    }
+
+    for (int i = 0; i < iCntMoving; i++)
+    {
+        tmpInfo = QFileInfo(m_strlistPath_Moving.at(i));
+        fileName = tmpInfo.fileName();
+        m_strlistBaseName_Moving.push_back(fileName);
+        ui.comboBox_Moving->addItem(fileName);
+    }
+
+    for (int i = 0; i < iCntCommand; i++)
+    {
+        tmpInfo = QFileInfo(m_strlistPath_Command.at(i));
+        fileName = tmpInfo.fileName();
+        m_strlistBaseName_Command.push_back(fileName);
+        ui.comboBox_Command->addItem(fileName);
+    }
+}
+
+void register_gui::SLT_LoadMovingFiles()
+{
+    //include DICOM dir as well
+  //  QStringList tmpList = QFileDialog::getOpenFileNames(this, "Select one or more files to open", m_strPathDirDefault, "image files (*.dcm *.mha *.nrrd)");
+
+    QFileDialog w;
+    w.setFileMode(QFileDialog::AnyFile);//both files and directories are displayed 
+    w.setOption(QFileDialog::DontUseNativeDialog, true);
+    QListView *l = w.findChild<QListView*>("listView");
+    if (l) {
+        l->setSelectionMode(QAbstractItemView::ExtendedSelection);
+    }
+    w.setDirectory(m_strPathDirDefault);
+    w.exec();
+
+    QStringList listPath = w.selectedFiles();
+    int iCntPaths = listPath.size();
+
+    if (iCntPaths < 1)
+        return;
+
+  /*  m_strlistPath_Moving.clear();
+    m_strlistBaseName_Moving.clear();
+    ui.comboBox_Moving->clear();
+*/
+
+    m_strlistPath_Moving = listPath;
+
+ /*   for (int i = 0; i < iCntPaths; i++)
+    {
+        QFileInfo tmpInfo = QFileInfo(m_strlistPath_Moving.at(i));
+        m_strlistBaseName_Moving.push_back(tmpInfo.fileName());
+    }*/
+
+    UpdateBaseAndComboFromFullPath();
+    UpdateTable_Main(DATA2GUI);
+}
+
+void register_gui::SLT_LoadCommandFiles()
+{    
+     QStringList tmpList = QFileDialog::getOpenFileNames(this, "Select one or more files to open",
+         m_strPathDirDefault, "text files (*.txt)");
+
+     int iCntPathList = tmpList.count();
+
+     if (iCntPathList < 1)
+         return;
+
+     /*  m_strlistPath_Command.clear();
+       m_strlistBaseName_Command.clear();
+       ui.comboBox_Command->clear();*/
+     m_strlistPath_Command = tmpList;
+
+   /*  for (int i = 0; i < iCntPathList; i++)
+     {
+         QFileInfo tmpInfo = QFileInfo(m_strlistPath_Command.at(i));
+         m_strlistBaseName_Command.push_back(tmpInfo.fileName());
+     }*/
+     UpdateBaseAndComboFromFullPath();
+     UpdateTable_Main(DATA2GUI);    
+}
+
+void register_gui::EmptyTableModel(QStandardItemModel* pTableModel)
+{
+    int iRowCount = pTableModel->rowCount();
+    int iColCount = pTableModel->columnCount();
+
+    QString strDummy = "";
+    for (int i = 0; i < iRowCount; i++)
+    {
+        for (int j = 0; j < iColCount; j++)
+        {
+            pTableModel->setItem(i, j, new QStandardItem(strDummy));
+        }
+    }
+}
+
+//When updating table, also do it for combo boxes
+void register_gui::UpdateTable_Main(enUpdateDirection updateDirection)
+{   
+  /*  if (m_pTableModel != NULL)
+    {
+        delete m_pTableModel;
+        m_pTableModel = NULL;
+    }*/
+
+    if (m_pTableModelMain == NULL)
+    {
+        cout << "error! Initialize table first " << endl;
+        return;
+    }
+
+    int iCntFixed = m_strlistBaseName_Fixed.count();
+    int iCntMoving = m_strlistBaseName_Moving.count();
+    int iCntCommand = m_strlistBaseName_Command.count();
+
+    int RowCnt = m_pTableModelMain->rowCount();
+    int ColCnt = m_pTableModelMain->columnCount();
+
+    //fixed image
+    if (iCntFixed > RowCnt)
+    {
+        cout << "Data size is larger than table prepared. Enarge the table first to accomodate bigger data" << endl;
+        iCntFixed = RowCnt;
+    }
+
+    //moving image
+    if (iCntMoving > RowCnt)
+    {
+        cout << "Data size is larger than table prepared. Enarge the table first to accomodate bigger data" << endl;
+        iCntMoving = RowCnt;
+    }
+
+    //command file
+    if (iCntCommand > RowCnt)
+    {
+        cout << "Data size is larger than table prepared. Enarge the table first to accomodate bigger data" << endl;
+        iCntCommand = RowCnt;
+    }
+
+    if (updateDirection == DATA2GUI)
+    {
+        /*   if (iCntFixed > 0)
+               ui.comboBox_Fixed->clear();
+
+               if (iCntMoving > 0)
+               ui.comboBox_Moving->clear();
+
+               if (iCntCommand > 0)
+               ui.comboBox_Command->clear();   */
+
+        //Clear the table
+        EmptyTableModel(m_pTableModelMain); //set all text ""
+
+        for (int i = 0; i < iCntFixed; i++)
+        {            
+            QString strFixed = m_strlistBaseName_Fixed.at(i);
+            m_pTableModelMain->setItem(i, 0, new QStandardItem(strFixed));
+          //  ui.comboBox_Fixed->addItem(strFixed);
+        }
+
+        for (int i = 0; i < iCntMoving; i++)
+        {
+            QString strMoving = m_strlistBaseName_Moving.at(i);
+            m_pTableModelMain->setItem(i, 1, new QStandardItem(strMoving));
+          //  ui.comboBox_Moving->addItem(strMoving);
+        }    
+
+        for (int i = 0; i < iCntCommand; i++)
+        {
+            QString strCommand = m_strlistBaseName_Command.at(i);
+            m_pTableModelMain->setItem(i, 2, new QStandardItem(strCommand));
+         //   ui.comboBox_Command->addItem(strCommand);
+        }
+    }
+    else if (updateDirection == GUI2DATA) //mostly, renaming command files
+    {
+        QStandardItem* item = NULL;
+        for (int i = 0; i < iCntFixed; i++)
+        {
+            item = m_pTableModelMain->item(i, 0);
+            m_strlistBaseName_Fixed[i] = item->text();
+        }
+
+        for (int i = 0; i < iCntMoving; i++)
+        {
+            item = m_pTableModelMain->item(i, 1);
+            m_strlistBaseName_Moving[i] = item->text();
+        }
+
+        for (int i = 0; i < iCntCommand; i++)
+        {
+            item = m_pTableModelMain->item(i, 2);
+            m_strlistBaseName_Command[i] = item->text();
+        }
+
+        //Update strlistPath by renaming files. after it is done, update it with DATA2GUI
+
+        UpdateStrListFromBase(m_strlistBaseName_Fixed, m_strlistPath_Fixed);
+        UpdateStrListFromBase(m_strlistBaseName_Moving, m_strlistPath_Moving);
+        UpdateStrListFromBase(m_strlistBaseName_Command, m_strlistPath_Command);
+        
+        //UpdateMainTable(DATA2GUI);
+    }  
+}
+
+void register_gui::UpdateTable_Que() //only Data2Gui direction
+{
+    if (m_pTableModelQue == NULL)
+    {
+        cout << "error! Initialize table first " << endl;
+        return;
+    }
+
+    int iCntData = m_vRegiQue.size();    
+
+    EmptyTableModel(m_pTableModelQue); //set all text ""
+
+    for (int i = 0; i < iCntData; i++)
+    {
+        CRegiQueString RegiQue = m_vRegiQue.at(i);
+
+        m_pTableModelQue->setItem(i, 0, new QStandardItem(RegiQue.GetStrFixed()));
+        m_pTableModelQue->setItem(i, 1, new QStandardItem(RegiQue.GetStrMoving()));
+        m_pTableModelQue->setItem(i, 2, new QStandardItem(RegiQue.GetStrCommand()));
+        m_pTableModelQue->setItem(i, 3, new QStandardItem(RegiQue.GetStrStatus()));
+        m_pTableModelQue->setItem(i, 4, new QStandardItem(RegiQue.GetStrTime()));
+        m_pTableModelQue->setItem(i, 5, new QStandardItem(RegiQue.GetStrScore()));
+        //additional data
+    }    
+}
+void register_gui::UpdateStrListFromBase(QStringList& strListBase, QStringList& strListFull)
+{
+    int iCntBase = strListBase.count();
+    int iCntFull = strListFull.count();
+
+    if (iCntBase*iCntFull == 0)
+        return;
+
+    if (iCntBase != iCntFull)
+    {
+        cout << "Error! count is zero or not matched" << endl;
+        return;
+    }
+
+    QString tmpStrBase;
+    QString tmpStrBaseTrimmed;
+    QString tmpStrPath;        
+    QString strPrevBase;
+    QString strNewPath;
+
+    for (int i = 0; i < iCntFull; i++)
+    {            
+        tmpStrBase = strListBase.at(i);
+        tmpStrPath = strListFull.at(i);
+
+        tmpStrBaseTrimmed = tmpStrBase.trimmed();
+
+        QFileInfo tmpInfo(tmpStrPath);
+        strPrevBase = tmpInfo.fileName();
+
+        if (strPrevBase != tmpStrBase)
+        {
+            if (tmpStrBaseTrimmed.length() < 1)//empty -->delete it in the list, not file
+            {
+                strNewPath = tmpInfo.absolutePath() + "/" + "_invalid_.txt";
+                strListFull[i] = strNewPath;
+            }
+            else
+            {
+                //rename whatever. Actual file name will be changed
+                strNewPath = tmpInfo.absolutePath() + "/" + tmpStrBase;                
+                //if (tmpInfo.exists()) //rename it!
+                //{
+                if (QFile::rename(tmpStrPath, strNewPath))
+                    strListFull[i] = strNewPath;
+                //}
+            }                
+        }
+    }
+
+    QStringList newListFull;
+    //trim the list:        
+    for (int i = 0; i < iCntFull; i++)
+    {
+        tmpStrPath = strListFull.at(i);
+
+        QFileInfo newInfo(tmpStrPath);
+        tmpStrBase = newInfo.fileName();
+
+        if (tmpStrBase.contains("_invalid_"))
+        {
+            //    strListFull.removeAt(i);
+        }
+        else
+            newListFull.push_back(tmpStrPath);
+    }
+
+    strListFull.clear();
+    strListFull = newListFull;
+
+    //UpdateBase from modified full list
+    int iCntNewList = strListFull.count();
+    strListBase.clear();
+
+    for (int i = 0; i < iCntNewList; i++)
+    {
+        tmpStrPath = strListFull.at(i);
+        QFileInfo newInfo2(tmpStrPath);
+        strListBase.push_back(newInfo2.fileName());
+    }
+}
+
+//void register_gui::SLT_InitializeTableWithRowCount()
+//{
+//    int rowCnt = ui.lineEdit_RowCountManual->text().toInt();
+//
+//    if (rowCnt > 0 && rowCnt < 500)
+//    {
+//        InitTableMain(rowCnt, DEFAULT_NUM_COLUMN_MAIN);
+//    }
+//}
+
+void register_gui::AdaptCommandFileToNewPath(QString& strPathCommand,
+    QString& strPathFixed,
+    QString& strPathMoving)
+{    
+    QStringList strListOriginal;
+
+    QString strBaseNameFixed;
+    QString strBaseNameMoving;
+    QString strBaseNameCommand;
+
+    QString strMiddleDirName;
+    QString strPathOutputDir;
+
+    strListOriginal = GetStringListFromFile(strPathCommand);
+    QFileInfo fInfoCommand(strPathCommand);
+    strBaseNameCommand = fInfoCommand.completeBaseName();
+
+    QFileInfo fInfoFixed(strPathFixed);
+    strBaseNameFixed = fInfoFixed.completeBaseName();
+
+    QFileInfo fInfoMoving(strPathMoving);
+    strBaseNameMoving = fInfoMoving.completeBaseName();
+
+    strMiddleDirName = "/" + strBaseNameFixed + "/" + strBaseNameMoving + "/" + strBaseNameCommand + "/";
+    strPathOutputDir = m_strPathDirDefault + strMiddleDirName;
+
+    //cout << strPathOutputDir.toLocal8Bit().constData() << endl;
+    QStringList newList;
+    newList = ModifyCommandFile(strListOriginal, strPathFixed, strPathMoving, strPathOutputDir);
+    //Change fixed and moving according to current path
+
+    //QUTIL::PrintStrList(newList);
+    //Save text file
+    SaveCommandText(strPathCommand, newList);
+}
+
+
+QStringList register_gui::ModifyCommandFile(QStringList& strlistOriginal,
+    QString& strPathFixed,
+    QString& strPathMoving,
+    QString& strPathOut)
+{
+    QStringList resultList;
+
+    int cntList = strlistOriginal.count();
+
+    QString modLine;
+    QString originalLine;
+
+    int iStage = 0;
+
+
+    QString strEndFix;
+    for (int i = 0; i < cntList; i++)
+    {        
+        originalLine = strlistOriginal.at(i);
+        modLine = originalLine;
+
+        if (originalLine.contains("STAGE"))
+        {
+            iStage = iStage + 1;
+            resultList.push_back(modLine);
+            continue;
+        }
+        if (iStage == 0)
+            strEndFix = "_glb";
+        else
+            strEndFix = QString("%1%2").arg("_s").arg(iStage);       
+        
+
+        QStringList listStr = originalLine.split("=");
+        QString firstStr = listStr.at(0);
+
+        if (firstStr.contains("fixed"))
+        {
+            modLine = "fixed=" + strPathFixed;
+        }
+        else if (firstStr.contains("moving"))
+        {
+            modLine = "moving=" + strPathMoving;
+        }
+        else if (firstStr.contains("img_out"))
+        {            
+            modLine = "img_out=" + strPathOut + "output_img" + strEndFix + ".mha";
+        }
+        else if (firstStr.contains("xform_out"))
+        {
+            modLine = "xform_out=" + strPathOut + "output_xform" + strEndFix + ".txt";
+        }     
+
+
+        resultList.push_back(modLine);        
+    }
+
+    return resultList;
+}
+
+QStringList register_gui::GetStringListFromFile(const QString& strFile)
+{
+    QStringList resultStrList;
+
+    QFile file(strFile);
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+        return resultStrList;
+
+    QTextStream in(&file);
+    while (!in.atEnd())
+    {
+        QString line = in.readLine();
+        resultStrList.push_back(line);
+    }
+    file.close();
+
+    return resultStrList;
+}
+
+
+void register_gui::SLT_ReadCommandFile_Main(QModelIndex index)
+{
+    //cout << "Activation occurred" << endl;
+   // cout << index.column() << ", " << index.row() << endl;    
+   //works only when command file is clicked
+
+    int row = index.row();
+    int col = index.column();
+
+    QStringList listCurCommand;
+
+    if (col != 2 || m_strlistPath_Command.count() <= row)
+    {     
+        SetCommandViewerText_Main(listCurCommand);
+        return;
+    }       
+
+    //Get path of command file
+
+    //if it is blank,
+    QStandardItem* item = m_pTableModelMain->itemFromIndex(index);    
+    QString curStr = item->text();
+
+    if (curStr != m_strlistBaseName_Command.at(row))
+    {
+        cout << "File name doesn't match" << endl;
+        cout << curStr.toLocal8Bit().constData() << " vs ";
+        cout << m_strlistBaseName_Command.at(row).toLocal8Bit().constData() << endl;
+        return;
+    }
+
+    //Read the text file and display it
+    QString strPathCommand = m_strlistPath_Command.at(row);
+    ReadCommandFile(strPathCommand, listCurCommand);
+
+    //Update command file viewer
+    SetCommandViewerText_Main(listCurCommand);
+    QString strDisp = curStr + ".txt";
+    ui.label_CurCommandFile->setText(strDisp);
+
+    //ui.plainTextEdit->setEnabled(true);
+    EnableUIForCommandfile(true);
+}
+
+void register_gui::ReadCommandFile(QString& strPathCommandFile, QStringList& strListOutput)
+{   
+    strListOutput.clear();
+
+    QFile file(strPathCommandFile);
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+        return;
+
+    QTextStream in(&file);
+    while (!in.atEnd())
+    {
+        QString line = in.readLine();
+        strListOutput.push_back(line);
+    }
+    file.close();
+}
+
+void register_gui::SLT_ReadCommandFile_Que(QModelIndex index)
+{
+    int row = index.row();
+    int col = index.column();
+
+    QStringList listCurCommand;
+    int iCntQueItem = m_vRegiQue.size();
+
+    if (iCntQueItem <= row)
+    {
+        SetCommandViewerText_Que(listCurCommand); //empty
+        return;
+    }
+
+    ////Get path of command file
+    
+
+    //Read the text file and display it
+    //QString strPathCommand = m_strlistPath_Command.at(row);
+    QString strPathCommand = m_vRegiQue.at(row).m_quePathCommand;
+
+    ReadCommandFile(strPathCommand, listCurCommand);
+
+    /*
+        QFile file(strPathCommand);
+        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+        return;
+
+        QTextStream in(&file);
+        while (!in.atEnd())
+        {
+        QString line = in.readLine();
+        listCurCommand.push_back(line);
+        }
+        file.close();*/
+
+    //Update command file viewer
+    SetCommandViewerText_Que(listCurCommand);
+}
+
+void register_gui::SLT_UpdateFileList()
+{
+    UpdateTable_Main(GUI2DATA);//rename and etc...
+    UpdateBaseAndComboFromFullPath();
+    UpdateTable_Main(DATA2GUI);
+}
+
+//curItem prevItem
+void register_gui::SLT_SelectionChangedMain(QItemSelection curItem, QItemSelection prevItem)
+{
+    //return;
+    //only valid if single selection take place
+    //UpdateMainTable(GUI2DATA);//if something renamed. sometimes it should not change the name    
+
+    QModelIndexList listModelIndexCur = curItem.indexes();    
+    int iCntCur = listModelIndexCur.count();
+
+    if (iCntCur != 1)
+        return;
+
+    QModelIndex mIdx = listModelIndexCur.at(0);
+
+    m_iCurSelRow_Main = mIdx.row();
+    m_iCurSelCol_Main = mIdx.column();    
+
+    if (mIdx.column() == 2)
+        SLT_ReadCommandFile_Main(mIdx);   
+}
+
+
+void register_gui::SLT_ItemClickedMain() //single clicked
+{
+    QModelIndex mIdx = ui.tableView_main->currentIndex();
+
+    m_iCurSelRow_Main = mIdx.row();
+    m_iCurSelCol_Main = mIdx.column();
+
+    if (mIdx.column() == 2)
+        SLT_ReadCommandFile_Main(mIdx);
+}
+
+//void register_gui::SLT_TableItemSelectedMain()
+
+void register_gui::SLT_SelectionChangedQue(QItemSelection curItem, QItemSelection prevItem)
+{    
+    QModelIndexList listModelIndexCur = curItem.indexes();
+    int iCntCur = listModelIndexCur.count(); //7, all cells in a line were selected
+
+    if (iCntCur < 1)
+        return;
+
+    QModelIndex mIdx = listModelIndexCur.at(0);//get first cell
+    m_iCurSelRow_Que = mIdx.row();
+    m_iCurSelCol_Que = mIdx.column();
+
+    SLT_ReadCommandFile_Que(mIdx);
+}
+
+
+void register_gui::SLT_ItemClickedQue() //single clicked
+{
+    QModelIndex mIdx = ui.tableView_que->currentIndex();
+    
+    m_iCurSelRow_Que = mIdx.row();
+    m_iCurSelCol_Que = mIdx.column();
+
+    SLT_ReadCommandFile_Que(mIdx);
+}
+
+
+void register_gui::SetCommandViewerText_Main(QStringList& strList)
+{
+    int iCntLines = strList.count();
+    ui.plainTextEdit->clear();
+    for (int i = 0; i < iCntLines; i++)
+    {
+        ui.plainTextEdit->appendPlainText(strList.at(i));
+    }
+}
+
+void register_gui::SetCommandViewerText_Que(QStringList& strList)
+{
+    int iCntLines = strList.count();
+    ui.plainTextEdit_Que->clear();
+    for (int i = 0; i < iCntLines; i++)
+    {
+        ui.plainTextEdit_Que->appendPlainText(strList.at(i));
+    }
+}
+
+
+//Read current viewer and save it to stringlist
+QStringList register_gui::GetCommandViewerText()
+{
+    QStringList resultList;
+
+    QString strTmp = ui.plainTextEdit->toPlainText();
+    //cout << strTmp.toLocal8Bit().constData() << endl;
+
+    resultList = strTmp.split("\n");
+    return resultList;
+}
+
+void register_gui::SLT_SaveCommandText()
+{    
+    QStringList strList = GetCommandViewerText();//from main
+
+    QString strPath;
+    if (m_iCurSelRow_Main >= 0 && m_iCurSelRow_Main < m_strlistPath_Command.count() && m_iCurSelCol_Main == 2)
+    {
+        strPath = m_strlistPath_Command.at(m_iCurSelRow_Main);
+        SaveCommandText(strPath, strList);
+        SetCommandViewerText_Main(strList); //Check, is this needed??
+        cout << strPath.toLocal8Bit().constData() << ": Text file was saved." << endl;
+    }
+}
+
+void register_gui::SLT_SaveCommandFileAsTemplate() //should not be called when click template list
+{
+    QStringList strList = GetCommandViewerText();//from main editor
+
+    if (strList.count() < 1)
+    {
+        cout << "Error! No text exists." << endl;
+        return;
+    }
+    //1) Popup input box to ask the template name. no need of "Default" prefix
+    //Cancel if no name is input
+    QInputDialog inputDlg;
+    bool ok;
+    QString templateName = QInputDialog::getText(this, "Save a new Template", "Input Command Template Name", QLineEdit::Normal, "template name", &ok);
+
+    if (!ok || templateName.isEmpty())
+        return;   
+
+    //2) Set all the paths to "TBD" to avoid confusion       
+
+    QStringList::iterator it;
+
+    for (it = strList.begin(); it != strList.end(); ++it)
+    {
+        QString strTempLine = (*it);
+
+        if (strTempLine.contains("fixed=") || strTempLine.contains("fixed ="))
+        {
+            strTempLine = "fixed= TBD";
+        }
+        else if (strTempLine.contains("moving=") || strTempLine.contains("moving ="))
+        {
+            strTempLine = "moving= TBD";
+        }
+        else if (strTempLine.contains("fixed_roi=") || strTempLine.contains("fixed_roi ="))
+        {
+            strTempLine = "fixed_roi= TBD";
+        }
+        else if (strTempLine.contains("img_out=") || strTempLine.contains("img_out ="))
+        {
+            strTempLine = "img_out= TBD";
+        }
+        else if (strTempLine.contains("xform_out=") || strTempLine.contains("xform_out ="))
+        {
+            strTempLine = "xform_out= TBD";
+        }
+        (*it) = strTempLine;        
+    }
+
+    //3) Get App Dir Path, m_str...
+    QDir curTemplateDir(m_strPathCommandTemplateDir);
+
+    if (!curTemplateDir.exists())
+    {
+        cout << "Error! No template dir exists" << endl;
+        return;
+    }    
+
+    QString strPathOut = m_strPathCommandTemplateDir + "/" + templateName + ".txt";
+
+    QFileInfo fInfo(strPathOut);
+    if (fInfo.exists())
+    {
+        cout << "Error! The same template already exists. Try with other name" << endl;
+        return;
+    }
+    SaveCommandText(strPathOut, strList);    
+
+    //4) Update template list
+    UpdateCommandFileTemplateList(m_strPathCommandTemplateDir);    
+}
+
+void register_gui::SaveCommandText(QString& strPathCommand, QStringList& strListLines)
+{    
+    ofstream fout;
+    fout.open(strPathCommand.toLocal8Bit().constData());
+    
+    int cnt = strListLines.count();
+
+    for (int i = 0; i < cnt; i++)
+    {
+        fout << strListLines.at(i).toLocal8Bit().constData() << endl;
+    }
+
+    fout.close();    
+}
+
+void register_gui::DeleteRemainingThreads()
+{
+    cout << "Running into a while loop for deleting remaining threads..." ;
+    int cntRunningThread = 1;
+
+    if (m_pArrThreadRegi != NULL)
+    {
+        while (cntRunningThread != 0)
+        {
+            cntRunningThread = 0;
+
+            for (int i = 0; i < m_iNumOfThreadAll; i++)
+            {
+                if (m_pArrThreadRegi[i] != NULL)
+                {
+                    cntRunningThread++;
+
+                    if (!m_pArrThreadRegi[i]->isRunning())
+                    {
+                        delete m_pArrThreadRegi[i];
+                        m_pArrThreadRegi[i] = NULL;
+                        cout << "Thread ID " << i << " has been deleted" << endl;
+                    }
+                }
+            }
+        }
+    }
+
+    delete[] m_pArrThreadRegi;
+    m_pArrThreadRegi = NULL;
+    cout << "Done!" << endl;
+}
+
+void register_gui::SetTableText(int row, int col, QString& inputStr)
+{    
+    m_pTableModelMain->setItem(row, col, new QStandardItem(inputStr));
+}
+
+void register_gui::SLT_AddSingleToQue()
+{
+    QString strFixed, strMoving, strCommand;    
+
+    int curIndex_fixed = ui.comboBox_Fixed->currentIndex();
+    int curIndex_moving = ui.comboBox_Moving->currentIndex();
+    int curIndex_command = ui.comboBox_Command->currentIndex();
+
+    if (curIndex_fixed < 0 ||
+        curIndex_moving < 0 ||
+        curIndex_command < 0)
+        return;
+
+    int RowCnt = m_pTableModelQue->rowCount();
+   // int ColCnt = m_pTableModelQue->columnCount();
+    int curDataSize = m_vRegiQue.size();
+
+    if (curDataSize == RowCnt)
+    {
+        cout << "Error! Que Table is full! Current maximum size = " << RowCnt << " Inquire about it to plastimatch group." << endl;
+        return;
+    }
+
+    //when only valid
+    if (curIndex_fixed < m_strlistPath_Fixed.count() &&
+        curIndex_moving < m_strlistPath_Moving.count() &&
+        curIndex_command < m_strlistPath_Command.count())
+    {
+        strFixed = m_strlistPath_Fixed.at(curIndex_fixed);
+        strMoving = m_strlistPath_Moving.at(curIndex_moving);
+        strCommand = m_strlistPath_Command.at(curIndex_command);        
+
+        if (m_vRegiQue.size() >= DEFAULT_MAXNUM_QUE)
+        {
+            cout << "Error! Maximum number of que items = " << DEFAULT_MAXNUM_QUE << endl;
+            return;
+        }
+
+        AddSingleToQue(strFixed, strMoving, strCommand); //data only
+    }
+    else
+    {
+        cout << "Error. No data exists" << endl;
+    }   
+
+    UpdateTable_Que();
+}
+
+//Read strlistPaths and get the min number of lines to add
+void register_gui::SLT_AddMultipleToQueByLine()
+{
+    int iCntFixed = m_strlistPath_Fixed.count();
+    int iCntMoving = m_strlistPath_Moving.count();
+    int iCntCommand = m_strlistPath_Command.count();
+
+    int minCnt = 9999;
+
+    if (iCntFixed < minCnt)
+        minCnt = iCntFixed;
+    if (iCntMoving < minCnt)
+        minCnt = iCntMoving;
+    if (iCntCommand < minCnt)
+        minCnt = iCntCommand;
+
+    cout << "Minimum number of count = " << minCnt << endl;
+
+    QString strFixed, strMoving, strCommand;
+    for (int i = 0; i < minCnt; i++)
+    {
+        strFixed = m_strlistPath_Fixed.at(i);
+        strMoving = m_strlistPath_Moving.at(i);
+        strCommand = m_strlistPath_Command.at(i);
+
+        if (m_vRegiQue.size() >= DEFAULT_MAXNUM_QUE)
+        {
+            cout << "Error! Maximum number of que items = " << DEFAULT_MAXNUM_QUE << endl;
+            return;
+        }
+
+        AddSingleToQue(strFixed, strMoving, strCommand); //data only
+    }
+    UpdateTable_Que();    
+}
+
+void register_gui::SLT_AddMultipleToQueByPermu()
+{    
+    QString strFixed, strMoving, strCommand;
+
+    int iCntFixed = m_strlistPath_Fixed.count();
+    int iCntMoving = m_strlistPath_Moving.count();
+    int iCntCommand = m_strlistPath_Command.count();
+
+    int iCopyCnt = 0;
+
+    for (int k = 0; k < iCntFixed; k++)
+    {
+        for (int i = 0; i < iCntMoving; i++)
+        {
+            for (int j = 0; j < iCntCommand; j++)
+            {
+                strFixed = m_strlistPath_Fixed.at(k);
+                strMoving = m_strlistPath_Moving.at(i);
+                strCommand = m_strlistPath_Command.at(j);
+
+                QString endFix = "_" + QString::number(iCopyCnt);
+                QString newStrCommandPath;
+                if (iCopyCnt == 0)
+                    newStrCommandPath = strCommand;
+                else
+                {
+                    newStrCommandPath = QUTIL::GetPathWithEndFix(strCommand, endFix);
+                    //copy
+                    QFile::copy(strCommand, newStrCommandPath);
+                }
+
+                if (m_vRegiQue.size() >= DEFAULT_MAXNUM_QUE)
+                {
+                    cout << "Error! Maximum number of que items = " << DEFAULT_MAXNUM_QUE << endl;
+                    return;
+                }
+
+                AddSingleToQue(strFixed, strMoving, newStrCommandPath); //data only
+            }
+            iCopyCnt++;
+        }
+    }
+    UpdateTable_Que();
+}
+
+void register_gui::AddSingleToQue(QString& strPathFixed, QString& strPathMoving, QString& strPathCommand)
+{
+    QFileInfo finfo_fixed, finfo_moving, finfo_command;
+    finfo_fixed = QFileInfo(strPathFixed);
+    finfo_moving = QFileInfo(strPathMoving);
+    finfo_command = QFileInfo(strPathCommand);
+
+    if (finfo_fixed.exists() && finfo_moving.exists() && finfo_command.exists())
+    {
+        AdaptCommandFileToNewPath(strPathCommand, strPathFixed, strPathMoving);//rewrite the text file according to input paths
+        CRegiQueString RegiQue;
+        RegiQue.m_quePathFixed = strPathFixed;
+        RegiQue.m_quePathMoving = strPathMoving;
+        RegiQue.m_quePathCommand = strPathCommand;
+        m_vRegiQue.push_back(RegiQue);
+    }
+    else
+    {
+        cout << "Error! some files don't exist! ExistFlag: fixed-moving-command= " << finfo_fixed.exists() << ", " <<
+            finfo_moving.exists() << ", " <<
+            finfo_command.exists() << endl;
+        return;
+    }
+}
+//
+//void register_gui::SLT_CopySelectionToAll_Command()
+//{
+//    int row = m_iCurSelRow_Main;
+//    if (m_iCurSelCol_Main == 0) //fixed image: copy only memory, not whole image
+//    {
+//        QString strPathFixed = m_strlistPath_Fixed.at(row);
+//
+//        int curFilledCnt = m_strlistPath_Fixed.count();
+//
+//        for (int i = curFilledCnt; i < m_iNumOfTableRow; i++)
+//        {
+//            m_strlistPath_Fixed.push_back(strPathFixed);
+//
+//            QFileInfo tmpInfo = QFileInfo(strPathFixed);
+//            m_strlistBaseName_Fixed.push_back(tmpInfo.fileName());
+//        }
+//    }
+//    else if (m_iCurSelCol_Main == 1) //mvoing image: copy only memory, not whole image
+//    {
+//        QString strPathMoving = m_strlistPath_Moving.at(row);
+//
+//        int curFilledCnt = m_strlistPath_Moving.count();
+//
+//        QFileInfo tmpInfo = QFileInfo(strPathMoving);
+//
+//        for (int i = curFilledCnt; i < m_iNumOfTableRow; i++)
+//        {
+//            m_strlistPath_Moving.push_back(strPathMoving);
+//            m_strlistBaseName_Moving.push_back(tmpInfo.fileName());
+//        }
+//    }
+//    else if (m_iCurSelCol_Main == 2) //command file. copy and rename automatically
+//    {
+//        QString strPathCommand = m_strlistPath_Command.at(row);
+//        int curFilledCnt = m_strlistPath_Command.count();
+//        QFileInfo tmpInfo = QFileInfo(strPathCommand);
+//
+//        for (int i = curFilledCnt; i < m_iNumOfTableRow; i++)
+//        {
+//            QString endFix = QString::number(i + 1);
+//            QString strPathCommandNew = tmpInfo.absolutePath() + "/" + tmpInfo.completeBaseName() + "_" + endFix + "." + tmpInfo.completeSuffix();
+//
+//            QFile::copy(strPathCommand, strPathCommandNew);
+//
+//            m_strlistPath_Command.push_back(strPathCommandNew);
+//
+//            QFileInfo tmpInfo2 = QFileInfo(strPathCommandNew);
+//            m_strlistBaseName_Command.push_back(tmpInfo2.fileName());
+//        }
+//    }
+//
+//
+//    UpdateMainTable(DATA2GUI);
+//}
+
+void register_gui::SLT_CopyCommandFile()
+{
+    int row = m_iCurSelRow_Main;
+
+    if (m_iCurSelCol_Main != 2)
+    {
+        cout << "Error! Select a single cell containing a command file." << endl;
+        return;
+    }     
+
+    if (row >= m_strlistPath_Command.count())
+        return;
+    
+    QString strPathCommand = m_strlistPath_Command.at(row);
+    int curFilledCnt = m_strlistPath_Command.count();
+    QFileInfo tmpInfo = QFileInfo(strPathCommand);    
+
+    int iNumOfFiles = ui.lineEdit_NumOfCopy->text().toInt();
+
+    int iEndIndex = curFilledCnt + iNumOfFiles;
+
+    for (int i = curFilledCnt; i < iEndIndex; i++)
+    {
+        QString endFix = QString::number(i + 1);
+        QString strPathCommandNew = tmpInfo.absolutePath() + "/" + tmpInfo.completeBaseName() + "_" + endFix + "." + tmpInfo.completeSuffix();
+
+        QFile::copy(strPathCommand, strPathCommandNew);
+
+        m_strlistPath_Command.push_back(strPathCommandNew);
+
+        QFileInfo tmpInfo2 = QFileInfo(strPathCommandNew);
+        m_strlistBaseName_Command.push_back(tmpInfo2.fileName());
+    }
+
+    UpdateTable_Main(DATA2GUI); //should be called first!
+    SLT_UpdateFileList();
+}
+
+void register_gui::SLT_ClearCommandFiles()
+{
+    m_strlistPath_Command.clear();
+    //m_strlistBaseName_Command.clear();
+
+    UpdateBaseAndComboFromFullPath();
+    UpdateTable_Main(DATA2GUI);
+}
+
+void register_gui::SLT_SortSelectedColumn()
+{
+    if (m_iCurSelCol_Main == 0) //fixed
+    {
+        m_strlistPath_Fixed.sort();
+    }
+    if (m_iCurSelCol_Main == 1) 
+    {
+        m_strlistPath_Moving.sort();
+    }
+    if (m_iCurSelCol_Main == 2) 
+    {
+        m_strlistPath_Command.sort();
+    }
+
+    UpdateBaseAndComboFromFullPath();
+    UpdateTable_Main(DATA2GUI);
+}
+
+void register_gui::SLT_ClearQueAll()
+{
+    m_vRegiQue.clear();
+    UpdateTable_Que();
+}
+
+void register_gui::SLT_RemoveSelectedQue()
+{
+    int iSelectedRowQue = m_iCurSelRow_Que;
+    if (iSelectedRowQue >= m_vRegiQue.size() || iSelectedRowQue < 0)
+        return;
+
+    m_vRegiQue.erase(m_vRegiQue.begin() + iSelectedRowQue);
+    UpdateTable_Que();
+
+    m_iCurSelRow_Que = -1;
+    m_iCurSelCol_Que = -1;
+}
+
+
+//Thread ID = index of Que table (max = 200)
+void register_gui::SLT_RunSingleSelected()
+{
+    int iSelected = m_iCurSelRow_Que; 
+    if (!RunRegistrationSingle(iSelected))
+    {
+        cout << "Error in RunRegistrationSingle!" << endl;
+    }
+    
+}
+
+bool register_gui::RunRegistrationSingle(int index)
+{
+    if (index >= m_vRegiQue.size() || index < 0)
+        return false;
+
+    // if the status is not waiting, do not.
+    CRegiQueString RegiQue = m_vRegiQue.at(index);
+
+    if (RegiQue.m_iStatus != ST_NOT_STARTED)
+    {
+        if (RegiQue.m_iStatus == ST_DONE)
+        {
+            cout << "Cannot perform the registration. This job is done already." << endl;
+            return false;
+        }
+
+        else if (RegiQue.m_iStatus == ST_PENDING)
+        {
+            cout << "Cannot perform the registration. This job is currently pending." << endl;
+            return false;
+        }
+    }
+    else
+    {
+        QString strPathCommmand = RegiQue.m_quePathCommand;
+        QString strFileCommmand = RegiQue.GetStrCommand();
+        m_pArrThreadRegi[index] = new YKThreadRegi(this, strPathCommmand, index);
+
+        cout << "Starting thread ID= " << index << ". Command file name= " << strFileCommmand.toLocal8Bit().constData() << endl;
+
+        m_pArrThreadRegi[index]->start(QThread::NormalPriority);
+        m_pArrThreadRegi[index]->exit();
+    }
+    return true;
+}
+
+void register_gui::SLT_RunBatchSequential()
+{
+    if (m_timerRunMultiThread->isActive())
+    {
+        cout << "Error! Finish MT timer first" << endl;
+        return;
+    }    
+    //if there is any pending, don't run
+
+    int iCntPending = GetCountPendingJobs();
+
+    if (iCntPending == 0)
+    {        
+        m_timerRunSequential->start(500);//Look up every 0.5 s and run next registration        
+        m_tTimeSeq = QTime::currentTime();        
+        ui.lineEdit_TotalProcTimeSeq->setText("");
+    }
+        
+    else
+        cout << "Cannot run! please wait until there is no pending job" << endl;
+    
+}
+
+void register_gui::SLT_RunBatchMultiThread()
+{    
+    if (m_vRegiQue.empty())
+        return;
+
+    if (m_timerRunSequential->isActive())
+    {
+        cout << "Error! Finish Sequential timer first" << endl;
+        return;
+    }
+
+    int iCntPending = GetCountPendingJobs();
+    int iCntStandBy = GetCountStandByJobs();
+
+    if (iCntPending == 0 && iCntStandBy > 0)
+    {
+        m_timerRunMultiThread->start(500);//Look up every 0.5 s and run next registration
+        m_tTimeMT = QTime::currentTime();
+        ui.lineEdit_TotalProcTimeMT->setText("");
+    }        
+    else
+        cout << "Cannot run due to pending jobs or all-done jobs" << endl;
+}
+int register_gui::GetCountPendingJobs()
+{
+    int iCntPending = 0;    
+    int iCntQued = m_vRegiQue.size();
+
+    for (int i = 0; i < iCntQued; i++)
+    {
+        if (m_vRegiQue.at(i).m_iStatus == ST_PENDING)
+        {
+            iCntPending++;
+        }
+    }
+    return iCntPending;
+}
+
+int register_gui::GetCountStandByJobs()
+{
+    int iCntStandby = 0;
+    int iCntQued = m_vRegiQue.size();
+
+    for (int i = 0; i < iCntQued; i++)
+    {
+        if (m_vRegiQue.at(i).m_iStatus == ST_NOT_STARTED)
+        {
+            iCntStandby++;
+        }
+    }
+    return iCntStandby;
+
+}
+
+//Continue until there is no "NOt_Started" items.
+void register_gui::SLT_TimerRunSEQ() //monitor the value
+{
+    if (m_vRegiQue.empty())
+    {
+        m_timerRunSequential->stop();
+        QString strNum = QString::number(m_tTimeSeq.elapsed() / 1000.0, 'f', 2);
+        ui.lineEdit_TotalProcTimeSeq->setText(strNum);
+        cout << "Timer stopped. Empty que" << endl;
+        return;
+    }   
+
+    int iCntPending = GetCountPendingJobs();
+
+    if (iCntPending > 0)
+        return;
+
+    int iCntQued = m_vRegiQue.size();
+
+    //if no pending
+    //find the target index
+    int targetIdx = -1;
+    for (int i = 0; i < iCntQued; i++)
+    {
+        if (m_vRegiQue.at(i).m_iStatus == ST_NOT_STARTED)
+        {
+            targetIdx = i;
+            break;
+        }
+    }
+
+    if (targetIdx < 0) //if there is no NOT_STARTED item
+    {     
+        QString strNum = QString::number(m_tTimeSeq.elapsed() / 1000.0, 'f', 2);
+        ui.lineEdit_TotalProcTimeSeq->setText(strNum);
+        cout << "Timer stopped. No more items to register." << endl;
+        m_timerRunSequential->stop();        
+    }
+    else
+    {
+        RunRegistrationSingle(targetIdx);
+    }
+    return;    
+}
+
+void register_gui::SLT_TimerRunMT()
+{
+    if (m_vRegiQue.empty())
+    {
+        m_timerRunMultiThread->stop();
+        QString strNum = QString::number(m_tTimeMT.elapsed() / 1000.0, 'f', 2);
+        ui.lineEdit_TotalProcTimeMT->setText(strNum);
+        cout << "Timer stopped. Empty que" << endl;
+        return;
+    }
+
+    int iMaxNumThread = ui.lineEdit_MaxNumberThread->text().toInt();
+    int iCntPending = GetCountPendingJobs();
+
+    int iAvailableSlots = iMaxNumThread - iCntPending;
+
+    if (iAvailableSlots < 1)
+        return;    
+
+    int iCntQued = m_vRegiQue.size();
+   
+    vector<int> vTargetIdx;
+    vector<int> vPendingIdx;
+
+    //int targetIdx = -1;
+    
+    for (int i = 0; i < iCntQued; i++)
+    {
+        if (m_vRegiQue.at(i).m_iStatus == ST_NOT_STARTED)
+            vTargetIdx.push_back(i);                    
+        else if (m_vRegiQue.at(i).m_iStatus == ST_PENDING)
+            vPendingIdx.push_back(i);        
+        
+        if (vTargetIdx.size() >= iAvailableSlots)
+            break;
+    }
+
+    if (vTargetIdx.empty() && vPendingIdx.empty()) //if there is no NOT_STARTED item
+    {        
+        QString strNum = QString::number(m_tTimeMT.elapsed() / 1000.0, 'f', 2);
+        ui.lineEdit_TotalProcTimeMT->setText(strNum);
+        cout << "Timer stopped. No more items to register." << endl;
+        m_timerRunMultiThread->stop();
+    }
+    else
+    {
+        vector<int>::iterator it;
+        int curIdx = 0;
+        for (it = vTargetIdx.begin(); it != vTargetIdx.end(); ++it)
+        {
+            curIdx = (*it);
+            RunRegistrationSingle(curIdx);
+        }        
+    }
+    return;
+}
+
+void register_gui::SLT_OpenSelectedOutputDir()
+{
+    int iSelected = m_iCurSelRow_Que;
+
+    if (iSelected < 0 || iSelected >= m_vRegiQue.size())
+    {
+        cout << "Error! Selection is not valid. Try other ones." << endl;
+        return;
+    }     
+    QString strPathCommand = m_vRegiQue.at(iSelected).m_quePathCommand;
+
+    QString strDirPathOutput = GetStrInfoFromCommandFile(PLM_OUTPUT_DIR_PATH, strPathCommand);
+
+    QDir dirOutput(strDirPathOutput);
+    if (!dirOutput.exists())
+    {
+        cout << "Error! The directory cannot found. You should run registration first" << endl;
+        return;
+    }
+
+    cout << "Found output dir= " << strDirPathOutput.toLocal8Bit().constData() << endl;
+
+
+    QString path = QDir::toNativeSeparators(strDirPathOutput);// ..(QApplication::applicationDirPath());
+    QDesktopServices::openUrl(QUrl("file:///" + path));
+
+// Alternative:
+//#if defined   Q_OS_WIN32
+//    // start explorer process here. E.g. "explorer.exe C:\windows"
+//    QString strCommand = QString("explorer %1").arg(strDirPathOutput); //works in linux as well??
+//    //strCommand = explorer H:/CBCT2/CT1/command1 --> 
+//    strCommand.replace('/', '\\');    
+//    ::system(strCommand.toLocal8Bit().constData());
+//#elif defined Q_OS_LINUX
+//    QString strCommand = QString("gnome-open %1").arg(strDirPathOutput);
+//    ::system(strCommand.toLocal8Bit().constData());
+//#elif defined Q_OS_MAC
+//    // start WHATEVER filebrowser here
+//    QString strCommand = QString("open %1").arg(strDirPathOutput);
+//    ::system(strCommand.toLocal8Bit().constData());    
+//#endif
+
+}
+
+QString register_gui::GetStrInfoFromCommandFile(enPlmCommandInfo plmInfo, QString& strPathCommandFile)
+{
+    QString resultStr;
+    QStringList tmpStrList = GetStringListFromFile(strPathCommandFile);
+    QString strTempLine;
+
+    QString strLineExcerpt;
+
+    int iCntLine = tmpStrList.count();
+
+    for (int i = 0; i < iCntLine; i++)
+    {
+        strTempLine = tmpStrList.at(i);
+
+        if (plmInfo == PLM_OUTPUT_DIR_PATH &&
+            (strTempLine.contains("img_out=") || strTempLine.contains("img_out =")))
+        {
+            strLineExcerpt = strTempLine;
+            break;
+        }
+        /*else if (plmInfo == enPlmCommandInfo::PLM_OUTPUT_DIR_PATH &&
+            (strTempLine.contains("img_out=") || strTempLine.contains("img_out =")))
+        {
+            strLineImgOut = strTempLine;
+            break;
+        }*/
+    }
+
+    QStringList infoStrList = strLineExcerpt.split("=");
+
+    QString infoStr;
+    if (infoStrList.count() > 1)
+        infoStr = infoStrList.at(1);
+
+    infoStr = infoStr.trimmed(); //remove only first and last spaces
+    if (plmInfo == PLM_OUTPUT_DIR_PATH)
+    {
+        QFileInfo fInfo(infoStr);
+        if (fInfo.exists())
+        {
+            resultStr = fInfo.absolutePath();
+        }
+    }
+    return resultStr;
+}
+
+
+QStringList register_gui::GetImagePathListFromCommandFile(QString& strPathCommandFile)
+{    
+    QStringList listImgPath;
+
+    QStringList tmpStrList = GetStringListFromFile(strPathCommandFile);    
+    
+    QString strLineExcerpt;
+
+    int iCntLine = tmpStrList.count();
+    QString strTempLine;
+
+    for (int i = 0; i < iCntLine; i++)
+    {
+        strTempLine = tmpStrList.at(i);
+
+        if (strTempLine.contains("fixed=") || strTempLine.contains("fixed =") ||
+            strTempLine.contains("moving=") || strTempLine.contains("moving =") ||
+            strTempLine.contains("img_out=") || strTempLine.contains("img_out =") )
+        {
+            QString strPath;
+            QStringList infoStrList = strTempLine.split("=");
+            if (infoStrList.count() > 1)
+                strPath = infoStrList.at(1);
+
+            strPath = strPath.trimmed();
+
+            if (strPath.length() > 1)
+                listImgPath.push_back(strPath);
+            
+        }
+    }   
+    return listImgPath;
+}
+
+//Put final command file on 
+void register_gui::CopyCommandFileToOutput(QString& strPathOriginCommandFile)
+{
+    QFileInfo fInfo(strPathOriginCommandFile);
+    if (!fInfo.exists())
+    {
+        cout << "Error! Orinal file doesn't exist" << endl;
+        return;
+    }     
+
+    QString strOutputDir = GetStrInfoFromCommandFile(PLM_OUTPUT_DIR_PATH, strPathOriginCommandFile);
+    QString strNewPath = strOutputDir + "/" + fInfo.fileName();    
+    QFile::copy(strPathOriginCommandFile, strNewPath);
+}
+
+void register_gui::ExportQueResult(QString& strPathOut)
+{
+    if (m_vRegiQue.empty())
+        return;    
+
+    int iCnt = m_vRegiQue.size();
+
+    ofstream fout;
+    fout.open(strPathOut.toLocal8Bit().constData());    
+    if (fout.fail())
+    {
+        cout << "File open failed." << endl;        
+        return;
+    }
+
+    fout << "Registration report-" << QUTIL::GetTimeStampDirName().toLocal8Bit().constData() << endl;
+
+    fout << "Fixed" << "\t"
+        << "Moving" << "\t"
+        << "CommandFile" << "\t"
+        << "Status" << "\t"
+        << "Time(s)" << "\t"
+        << "Score1" << "\t"
+        << "Score2" << endl;
+
+    for (int i = 0; i < iCnt; i++)
+    {
+        CRegiQueString curItem = m_vRegiQue.at(i);
+        fout << curItem.m_quePathFixed.toLocal8Bit().constData() << "\t"
+            << curItem.m_quePathMoving.toLocal8Bit().constData() << "\t"
+            << curItem.m_quePathCommand.toLocal8Bit().constData() << "\t"
+            << curItem.GetStrStatus().toLocal8Bit().constData() << "\t"
+            << curItem.GetStrTime().toLocal8Bit().constData() << "\t"
+            << curItem.GetStrScore().toLocal8Bit().constData() << "\t"
+            << curItem.GetStrScore().toLocal8Bit().constData() << endl;
+    }
+    fout.close();
+
+
+    //QString timeStamp = QUTIL::GetTimeStampDirName();
+    //QString strPathDirAnalysis = m_strPathDirWorkDir + "/" + strSubAnalysis
+}
+
+void register_gui::SLTM_ExportQueResult()
+{
+    QString strFilePath = QFileDialog::getSaveFileName(this, "Save registration report file", m_strPathDirDefault, "report (*.txt)", 0, 0);
+
+    if (strFilePath.length() < 1)
+        return;
+
+    QFileInfo fInfo(strFilePath);
+
+    if (fInfo.suffix() != "txt" && fInfo.suffix() != "TXT")
+    {
+        strFilePath = strFilePath + ".txt";
+    }
+
+    ExportQueResult(strFilePath);
+}
+
+
+void register_gui::SLT_CopyTableQueToClipboard()
+{
+    if (m_vRegiQue.empty())
+        return;
+
+
+    qApp->clipboard()->clear();
+
+    QStringList list;
+
+    int rowCnt = m_pTableModelQue->rowCount();
+    int columnCnt = m_pTableModelQue->columnCount();
+   
+    list << "\n";
+    list << "Fixed";
+    list << "Moving";
+    list << "CommandFile";
+    list << "Status";
+    list << "Processing_Time(s)";
+    list << "Score1";
+    list << "Score2";
+    list << "\n";
+
+    for (int j = 0; j < rowCnt; j++)
+    {
+        for (int i = 0; i < columnCnt; i++)
+        {
+            QStandardItem* item = m_pTableModelQue->item(j, i);
+            list << item->text();
+        }
+        list << "\n";
+    }
+
+    qApp->clipboard()->setText(list.join("\t"));
+}
+
+void register_gui::SLT_ViewSelectedImg()
+{
+    int iSelected = m_iCurSelRow_Que;
+
+    if (iSelected < 0 || iSelected >= m_vRegiQue.size())
+    {
+        cout << "Error! Selection is not valid. Try other ones." << endl;
+        return;
+    }
+    QString strPathCommand = m_vRegiQue.at(iSelected).m_quePathCommand;
+    QStringList strlistFilePath = GetImagePathListFromCommandFile(strPathCommand);
+
+    //Check available app first
+
+    QFileInfo fInfoApp(m_strPathReadImageApp);
+
+    if (!fInfoApp.exists())
+    {
+        QUTIL::ShowErrorMessage("Error! Viewer application is not found!");
+        return;
+    }
+
+    int iCntPath = strlistFilePath.count();
+
+    QString curPath;
+    QStringList validFiles;
+    for (int i = 0; i < iCntPath; i++)
+    {
+        curPath = strlistFilePath.at(i);
+        QFileInfo fInfoimg(curPath);
+        if (fInfoimg.exists())
+        {
+            validFiles.push_back(curPath);
+        }
+    }
+
+    //Shell command
+
+    QString strShellCommand = m_strPathReadImageApp;
+    strShellCommand = "\"" + strShellCommand + "\""; //IMPORTANT! due to the "Program Files" space issue
+
+    int iCntValidImg = validFiles.count();
+
+    for (int i = 0; i < iCntValidImg; i++)
+    {
+        strShellCommand = strShellCommand + " " + validFiles.at(i);
+    }
+
+//    strShellCommand = "\"" + strShellCommand + "\"";
+    if (!QProcess::startDetached(strShellCommand))
+        cout << "Failed to run viewer app. Command= " << strShellCommand.toLocal8Bit().constData() << endl;
+
+}
+
+void register_gui::WriteDefaultConfig()
+{
+    QSettings settings;
+
+    //QString test = QFileInfo(settings.fileName()).absolutePath(); //H:HKEY_CURRENTUSER_SOFTWARE/Plastimatch --> no such folder
+
+    settings.setValue ("DEFAULT_WORK_DIR", m_strPathDirDefault);
+    settings.setValue ("DEFAULT_VIEWER_PATH", m_strPathReadImageApp);
+    //settings.setValue("DEFAULT_TEMPLATE_DIR_PATH", m_strPathCommandTemplateDir);
+}
+
+bool register_gui::ReadDefaultConfig()
+{
+    QSettings settings;//when this is called, it accesses to some hidden directory
+    //1) default work dir
+    QVariant val = settings.value ("DEFAULT_WORK_DIR");
+    if (!val.isNull()) {
+        SetWorkDir(val.toString()); 
+    }
+    else
+    {
+        // Set workdir to folder of current directory
+        QString strPathCurrent = QDir::current().absolutePath();
+        SetWorkDir(strPathCurrent);
+    }
+
+    //2) default viewer path
+    val = settings.value ("DEFAULT_VIEWER_PATH");
+    if (!val.isNull()) {
+        SetReadImageApp(val.toString());
+    }
+
+    //3) default template directory
+    /*val = settings.value("DEFAULT_TEMPLATE_DIR_PATH");
+    if (!val.isNull()) {
+        SetCommandTemplateDir(val.toString());
+    }*/
+
+    return true;
+}
+
+void register_gui::CreateDefaultCommandFile(enRegisterOption option) //if there is same file, overwrite it
+{
+    QString strPathDefaultCommand;
+
+    if (option == PLAST_RIGID) {
+        strPathDefaultCommand = m_strPathCommandTemplateDir + "/" + "Default_Rigid.txt";
+    }
+    else if (option == PLAST_BSPLINE) {
+        strPathDefaultCommand = m_strPathCommandTemplateDir + "/" + "Default_B-spline.txt";
+    }
+    else if (option == PLAST_AFFINE) {
+        strPathDefaultCommand = m_strPathCommandTemplateDir + "/" + "Default_Affine.txt";
+    }
+    else if (option == PLAST_GRADIENT) {
+        strPathDefaultCommand = m_strPathCommandTemplateDir + "/" + "Default_Gradient.txt";
+    }
+    else {
+        return;
+    }
+
+    QUTIL::GenDefaultCommandFile(strPathDefaultCommand, option);
+
+    //// GCS logic for template
+    //if (option == PLAST_RIGID) {
+    //    SetTemplateNameFromSample ("Rigid");
+    //}
+    //else if (option == PLAST_BSPLINE) {
+    //    SetTemplateNameFromSample ("B-spline");
+    //}
+    //else {
+    //    return;
+    //}
+}
+
+//void register_gui::SetTemplateNameFromSample (QString& strName)
+//{
+//    ui.comboBox_Template->addItem(strName);
+//}
+
+void register_gui::UpdateCommandFileTemplateList(QString& strPathTemplateDir)
+{
+    //Search in default template directory    
+    if (strPathTemplateDir.length() < 1)
+        return;
+
+    //Look into the specified directory and load all the text files
+    QDir dirCmdTemplate(strPathTemplateDir);
+
+    if (!dirCmdTemplate.exists())
+        return;
+
+    ui.listWidgetCommandTemplate->clear();
+
+    QFileInfoList listFile = dirCmdTemplate.entryInfoList(QDir::Files, QDir::Name); //search for DICOM RS file
+    if (listFile.size() <= 0)
+    {
+        cout << "No template file was found." << endl;
+        return;
+    }
+    else
+    {
+        for (int i = 0; i < listFile.size(); i++)
+        {
+            if (listFile.at(i).suffix().contains("txt", Qt::CaseInsensitive))
+            {
+                QString strBaseName = listFile.at(i).baseName();
+                ui.listWidgetCommandTemplate->addItem(strBaseName);
+            }
+        }
+    }
+}
+
+void register_gui::SLT_CommandTemplateSelected() //when one of the list item selected
+{
+    int curIdx = ui.listWidgetCommandTemplate->currentRow();
+
+    if (curIdx < 0)
+        return;
+    
+    QString strBase = ui.listWidgetCommandTemplate->currentItem()->text();
+    QString curTemplateFilePath = m_strPathCommandTemplateDir + "/" + strBase + ".txt";
+
+    //QString strPathCommand = m_vRegiQue.at(row).m_quePathCommand;
+    QStringList listCurCommand;
+    ReadCommandFile(curTemplateFilePath, listCurCommand);
+    SetCommandViewerText_Main(listCurCommand);
+    
+    QString strDisp = "Template: " + strBase;
+    ui.label_CurCommandFile->setText(strDisp);
+    EnableUIForCommandfile(false);
+}
+
+void register_gui::EnableUIForCommandfile(bool bEnable)
+{    
+    ui.groupBox_CommandFile->setEnabled(bEnable);
+    //could be individualized for each components such as..
+    //ui.plainTextEdit->setEnabled(false);
+}
+
+void register_gui::SLT_CopyCommandTemplateToDataPool()
+{
+    int curIdx = ui.listWidgetCommandTemplate->currentRow();
+
+    if (curIdx < 0)
+        return;
+
+    QString curFileBase = ui.listWidgetCommandTemplate->currentItem()->text();
+    QString strPathSrc = m_strPathCommandTemplateDir + "/" + curFileBase + ".txt";
+    QFileInfo fInfo(strPathSrc);
+
+    if (!fInfo.exists())
+        return;
+
+    //Remove "Default"
+
+    QString strNewBase;
+    if (curFileBase.contains("Default_", Qt::CaseInsensitive))
+        strNewBase = curFileBase.replace("Default_", "Custom_", Qt::CaseInsensitive);
+    else
+        strNewBase = "Custom_" + curFileBase;
+
+    //working directory
+    QString strPathTarget = m_strPathDirDefault + "/" + strNewBase + ".txt";
+
+    QString strPathTargetMod; //final file path to avoid overwritten
+
+    if (!WriteCommandNoOverwriting(strPathSrc, strPathTarget, strPathTargetMod)) //write it with end fix if it is to be overwritten
+    {
+        cout << "Error in writing." << endl;
+        return;
+    }
+    //    QFile::copy(strPathCommand, strPathCommandNew);
+
+    m_strlistPath_Command.push_back(strPathTargetMod);
+    QFileInfo tmpInfo2 = QFileInfo(strPathTargetMod);
+    m_strlistBaseName_Command.push_back(tmpInfo2.fileName());
+    UpdateTable_Main(DATA2GUI);
+    SLT_UpdateFileList();  
+}
+
+
+bool register_gui::WriteCommandNoOverwriting(QString& strPathSrc, QString& strPathTarget, QString& strPathTargetMod)
+{
+    //strPathSrc exists --> chekced already
+   
+    //Check target path    
+    
+    QFileInfo fInfoTargOriginal(strPathTarget);
+    QString strBaseName = fInfoTargOriginal.baseName();
+
+    QString curPathOutput = strPathTarget;
+    int iEndFix = 0;
+    QString strEndFix = "";
+    while (true)
+    {
+        QFileInfo fInfoTarg(curPathOutput);
+        if (!fInfoTarg.exists()) //good
+        {
+            strPathTargetMod = curPathOutput;
+            break;
+        }
+        else
+        {
+            ++iEndFix;
+            strEndFix = "_" + QString::number(iEndFix);
+            curPathOutput = fInfoTargOriginal.absolutePath() + "/" + strBaseName + strEndFix + "." + fInfoTargOriginal.suffix();
+        }        
+    }
+
+    QFile::copy(strPathSrc, strPathTargetMod);
+
+    return true;
+}
+
+void register_gui::SLT_BrowseWorkingDir()
+{
+    QString strPathDir = m_strPathDirDefault;
+
+    QDir dir(strPathDir);
+    if (!dir.exists())
+    {
+        cout << "Error! No dir exists" << endl;
+        return;
+    }
+    QString path = QDir::toNativeSeparators(strPathDir);// ..(QApplication::applicationDirPath());
+    QDesktopServices::openUrl(QUrl("file:///" + path));
+}
+
+void register_gui::SLT_BrowseTemplateDir()
+{
+    QString strPathDir = m_strPathCommandTemplateDir;
+
+    QDir dir(strPathDir);
+    if (!dir.exists())
+    {
+        cout << "Error! No dir exists" << endl;
+        return;
+    }
+    QString path = QDir::toNativeSeparators(strPathDir);// ..(QApplication::applicationDirPath());
+    QDesktopServices::openUrl(QUrl("file:///" + path));
+}
+
+void register_gui::SLT_DeleteSingleTemplate()
+{
+    int curIdx = ui.listWidgetCommandTemplate->currentRow();
+
+    if (curIdx < 0)
+        return;
+
+    QString curFileBase = ui.listWidgetCommandTemplate->currentItem()->text();
+    QString strPathSrc = m_strPathCommandTemplateDir + "/" + curFileBase + ".txt";
+    QFileInfo fInfo(strPathSrc);
+
+    if (!fInfo.exists())
+        return;    
+
+    //Get confirmation
+
+    QMessageBox msgBox;
+    QString strMsg = "Command template: " + curFileBase + " will be permanently deleted from the folder. Are you sure?";
+    msgBox.setText(strMsg);
+    msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+    int res = msgBox.exec();
+
+    if (res == QMessageBox::Ok)
+    {
+        //delete the file
+        bool bSuccess = QFile::remove(strPathSrc);
+        if (bSuccess)
+            cout << "Template file: " << strPathSrc.toLocal8Bit().constData() << " was removed. Default templates will be regenerated when restarting the application." << endl;
+        else
+        {
+            cout << "Error! Template file: " << strPathSrc.toLocal8Bit().constData() << " couldn't be removed." << endl;
+            return;
+        }         
+    }
+    UpdateCommandFileTemplateList(m_strPathCommandTemplateDir);
+}
+
+void register_gui::SLTM_ImportDataPool()
+{    
+    QString filePath = QFileDialog::getOpenFileName(this, "Open Data pool log file", m_strPathDirDefault, "Data pool log file (*.dpl)", 0, 0);
+
+    /*if (filePath.length() < 1)
+        return;*/
+
+    QFileInfo fInfo(filePath);
+    if (!fInfo.exists())
+        return;  
+
+    ImportDataPool(filePath);
+}
+
+void register_gui::ImportDataPool(QString& strPathImportTxt)
+{
+    QStringList strList = GetStringListFromFile(strPathImportTxt);
+
+    if (strList.count() < 1)
+        return;
+
+    m_strlistPath_Fixed.clear();
+    m_strlistPath_Moving.clear();
+    m_strlistPath_Command.clear();
+
+    m_strlistBaseName_Fixed.clear();
+    m_strlistBaseName_Moving.clear();
+    m_strlistBaseName_Command.clear();    
+
+    QStringList::const_iterator it = strList.constBegin();
+    QString curStr;
+    QString curStrSub;
+    while (it != strList.constEnd())
+    {
+        curStr = (*it);
+        if (curStr.contains("FIXED_FILE_BEGIN", Qt::CaseSensitive))
+        {    
+            ++it;
+            while (it != strList.end())
+            {
+                curStrSub = (*it);
+
+                if (curStrSub.contains("FIXED_FILE_END", Qt::CaseSensitive))
+                    break;
+                else
+                {
+                    QFileInfo finfo(curStrSub);
+                    if (finfo.exists())
+                        m_strlistPath_Fixed.push_back(curStrSub);                        
+                }
+                ++it;
+            }
+        }
+        else if (curStr.contains("MOVING_FILE_BEGIN", Qt::CaseSensitive))
+        {
+            ++it;
+            while (it != strList.end())
+            {
+                curStrSub = (*it);
+
+                if (curStrSub.contains("MOVING_FILE_END", Qt::CaseSensitive))
+                    break;
+                else
+                {
+                    QFileInfo finfo(curStrSub);
+                    if (finfo.exists())
+                        m_strlistPath_Moving.push_back(curStrSub);
+                }
+                ++it;
+            }
+        }
+        else if (curStr.contains("COMMAND_FILE_BEGIN", Qt::CaseSensitive))
+        {    
+            ++it;
+            while (it != strList.end())
+            {                
+                curStrSub = (*it);
+
+                if (curStrSub.contains("COMMAND_FILE_END", Qt::CaseSensitive))
+                    break;
+                else
+                {
+                    QFileInfo finfo(curStrSub);
+                    if (finfo.exists())
+                        m_strlistPath_Command.push_back(curStrSub);
+                }
+                ++it;
+            }
+        }
+        ++it;
+    }//end of while main
+
+
+    UpdateBaseAndComboFromFullPath();
+    UpdateTable_Main(DATA2GUI); //When updating table, also do it for combo boxes
+}
+
+void register_gui::SLTM_ExportDataPool()
+{
+    QString strFilePath = QFileDialog::getSaveFileName(this, "Save Data pool log file", m_strPathDirDefault, "Datafile (*.dpl)", 0, 0);  
+
+    if (strFilePath.length() < 1)
+        return;        
+
+    QFileInfo fInfo(strFilePath);
+
+    if (fInfo.suffix() != "dpl" && fInfo.suffix() != "DPL")
+    {
+        strFilePath = strFilePath + ".dpl";
+    }
+
+    ExportDataPool(strFilePath);
+}
+
+void register_gui::ExportDataPool(QString& strPathExportTxt)
+{
+    ofstream fout;
+    fout.open(strPathExportTxt.toLocal8Bit().constData());
+    if (fout.fail())
+    {
+        cout << "Writing to file failed" << endl;
+        return;
+    }
+    /*QStringList m_strlistPath_Fixed;
+    QStringList m_strlistPath_Moving;
+    QStringList m_strlistPath_Command;*/
+
+    fout << "#Data pool log file (*.dpl) for plastimatch register_gui" << endl;
+    fout << "%FIXED_FILE_BEGIN%" << endl;
+
+    QStringList::const_iterator it;
+    for (it = m_strlistPath_Fixed.begin(); it != m_strlistPath_Fixed.end(); ++it)
+    {
+        fout << (*it).toLocal8Bit().constData() << endl;
+    }
+    fout << "%FIXED_FILE_END%" << endl;
+
+    fout << "%MOVING_FILE_BEGIN%" << endl;
+    for (it = m_strlistPath_Moving.begin(); it != m_strlistPath_Moving.end(); ++it)
+    {
+        fout << (*it).toLocal8Bit().constData() << endl;
+    }
+    fout << "%MOVING_FILE_END%" << endl;
+    fout << "%COMMAND_FILE_BEGIN%" << endl;
+    for (it = m_strlistPath_Command.begin(); it != m_strlistPath_Command.end(); ++it)
+    {
+        fout << (*it).toLocal8Bit().constData() << endl;
+    }
+    fout << "%COMMAND_FILE_END%" << endl;
+    fout.close();
+}
diff --git a/src/plastimatch/standalone/register_gui.h b/src/plastimatch/standalone/register_gui.h
new file mode 100644
index 0000000..2856540
--- /dev/null
+++ b/src/plastimatch/standalone/register_gui.h
@@ -0,0 +1,201 @@
+#ifndef REGISTER_GUI_H
+#define REGISTER_GUI_H
+
+#include <QtGui/QMainWindow>
+#include "ui_register_gui.h"
+#include <QStringList>
+#include <vector>
+#include "yk_config.h"
+#include "YKThreadRegi.h"
+#include <QMutex>
+
+#include <QTime>
+
+using namespace std;
+
+class QStandardItemModel;
+class YKThreadRegi;
+class QTimer;
+
+class register_gui : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    register_gui(QWidget *parent = 0, Qt::WFlags flags = 0);
+    ~register_gui();        
+
+    void SetCommandViewerText_Main(QStringList& strList);
+
+    void SetCommandViewerText_Que(QStringList& strList);
+
+    QStringList GetCommandViewerText();    
+    QStringList GetStringListFromFile(const QString& strFile);
+
+    QStringList ModifyCommandFile(QStringList& strlistOriginal, QString& strPathFixed, QString& strPathMoving, QString& strPathOut);
+
+    void SaveCommandText(QString& strPathCommand, QStringList& strListLines);
+
+    void UpdateStrListFromBase(QStringList& strListBase, QStringList& strListFull);
+
+    void DeleteRemainingThreads();
+
+    void EmptyTableModel(QStandardItemModel* pTableModel);
+
+    void UpdateBaseAndComboFromFullPath();//Base and ComboList
+
+    void AdaptCommandFileToNewPath(QString& strPathCommand,
+        QString& strPathFixed,
+        QString& strPathMoving);
+
+    void AddSingleToQue(QString& strPathFixed, QString& strPathMoving, QString& strPathCommand);
+
+    bool RunRegistrationSingle(int index);    
+    int GetCountPendingJobs();
+    int GetCountStandByJobs();
+
+    QString GetStrInfoFromCommandFile(enPlmCommandInfo plmInfo, QString& strPathCommandFile);
+    QStringList GetImagePathListFromCommandFile(QString& strPathCommandFile);
+
+
+    void CopyCommandFileToOutput(QString& strPathOriginCommandFile);
+
+    void ExportQueResult(QString& strPathOut);
+    void InitConfig();
+    void WriteDefaultTemplateFiles(QString& targetDirPath);
+
+    void ReadCommandFile(QString& strPathCommandFile, QStringList& strListOutput);    
+    bool WriteCommandNoOverwriting(QString& strPathSrc, QString& strPathTarget, QString& strPathTargetMod);
+
+    void EnableUIForCommandfile(bool bEnable);
+
+
+    void ImportDataPool(QString& strPathImportTxt);
+    void ExportDataPool(QString& strPathImportTxt);    
+
+    public slots:                
+        void SLT_SetDefaultDir();        
+        void SLT_LoadFixedFiles();
+        void SLT_LoadMovingFiles();
+        void SLT_LoadCommandFiles();       
+        
+        void SLT_ReadCommandFile_Main(QModelIndex index); //(QModelIndex& index) didn't work
+        void SLT_ReadCommandFile_Que(QModelIndex index); //(QModelIndex& index) didn't work        
+        void SLT_SelectionChangedMain(QItemSelection curItem, QItemSelection prevItem); //prevItem is not being used now
+        void SLT_ItemClickedMain(); //single clicked
+
+        void SLT_SelectionChangedQue(QItemSelection curItem, QItemSelection prevItem);
+        void SLT_ItemClickedQue(); //single clicked
+
+        void SLT_SaveCommandText();       
+        void SLT_SaveCommandFileAsTemplate();
+
+        void SLT_AddSingleToQue();
+        void SLT_AddMultipleToQueByLine();
+        void SLT_AddMultipleToQueByPermu();
+        //void SLT_CopySelectionToAll_Command();
+        void SLT_CopyCommandFile();
+        void SLT_ClearCommandFiles();
+
+        void SLT_UpdateFileList();//renaming, etc
+        void SLT_SortSelectedColumn(); //main only
+
+        void SLT_ClearQueAll();
+        void SLT_RemoveSelectedQue();
+
+        void SLT_RunSingleSelected();
+        void SLT_RunBatchSequential();
+        void SLT_RunBatchMultiThread();
+
+        void SLT_TimerRunSEQ();
+        void SLT_TimerRunMT();
+        
+        void SLTM_ExportQueResult();
+        void SLT_CopyTableQueToClipboard();
+        void SLT_SetDefaultViewer();
+
+        void SLT_OpenSelectedOutputDir();
+        void SLT_ViewSelectedImg();
+        //void SLT_CreateSampleRigid();
+        //void SLT_CreateSampleDeform();
+        void SLT_CommandTemplateSelected();
+        void SLT_CopyCommandTemplateToDataPool();    
+        void SLT_BrowseWorkingDir();
+        void SLT_BrowseTemplateDir();
+        void SLT_DeleteSingleTemplate();
+
+        
+        void SLTM_ImportDataPool();
+        void SLTM_ExportDataPool();
+
+        
+
+public:    
+    QStringList m_strlistPath_Fixed;
+    QStringList m_strlistPath_Moving;
+    QStringList m_strlistPath_Command;
+
+    QStringList m_strlistBaseName_Fixed;
+    QStringList m_strlistBaseName_Moving;
+    QStringList m_strlistBaseName_Command;
+
+    QStringList m_strlistPathOutputImg;
+    QStringList m_strlistPathOutputXf;
+
+    QString m_strPathDirDefault;
+    QString m_strPathReadImageApp;
+    QString m_strPathCommandTemplateDir;
+
+    void SetWorkDir(const QString& strPath);
+    void SetReadImageApp(const QString& strPath);
+    void SetCommandTemplateDir(const QString& strDirPath);
+
+
+    void InitTableMain(int rowCnt, int columnCnt);
+    void InitTableQue(int rowCnt, int columnCnt);
+    void UpdateTable_Main(enUpdateDirection updateDirection);
+    void UpdateTable_Que(); //only Data2Gui direction
+
+    void SetTableText(int row, int col, QString& inputStr);
+
+    void WriteDefaultConfig(); //m_strFileDefaultConfig, m_strPathDirDefault, m_strPathReadImageApp
+    bool ReadDefaultConfig();
+
+    void CreateDefaultCommandFile(enRegisterOption option);
+
+    void UpdateCommandFileTemplateList(QString& strPathTemplateDir);
+
+        //SetTemplateNameFromSample (QString& strName);
+
+    int m_iCurSelRow_Main;
+    int m_iCurSelCol_Main;
+
+    int m_iCurSelRow_Que;
+    int m_iCurSelCol_Que;
+
+
+    QStandardItemModel *m_pTableModelMain;
+    QStandardItemModel *m_pTableModelQue;
+
+    //QStandardItemModel *m_pTableModelQue;
+
+
+    YKThreadRegi** m_pArrThreadRegi; //Thread ID = index of Que table (max = 200)
+
+    int m_iNumOfThreadAll;
+    QMutex m_mutex;
+    
+    vector<CRegiQueString> m_vRegiQue;   
+
+    QTimer* m_timerRunSequential;    
+    QTimer* m_timerRunMultiThread;
+
+    QTime m_tTimeSeq;
+    QTime m_tTimeMT;
+    
+
+private:
+    Ui::register_guiClass ui;
+};
+
+#endif
diff --git a/src/plastimatch/standalone/register_gui.ui b/src/plastimatch/standalone/register_gui.ui
new file mode 100644
index 0000000..8f44339
--- /dev/null
+++ b/src/plastimatch/standalone/register_gui.ui
@@ -0,0 +1,1209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>register_guiClass</class>
+ <widget class="QMainWindow" name="register_guiClass">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>838</width>
+    <height>913</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>RegiAid - Plastimatch</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QVBoxLayout" name="verticalLayout">
+    <item>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="label_9">
+        <property name="text">
+         <string>Working directory</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="pushButton_SetDefaultDir">
+        <property name="text">
+         <string>set</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="lineEditDefaultDirPath"/>
+      </item>
+      <item>
+       <widget class="QPushButton" name="pushButtonBrowseWorkingDir">
+        <property name="text">
+         <string>Browse</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <layout class="QHBoxLayout" name="horizontalLayout_10">
+      <item>
+       <widget class="QLabel" name="label_12">
+        <property name="text">
+         <string>Image viewer path</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="pushButton_SetViewerPath">
+        <property name="text">
+         <string>set</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="lineEditDefaultViewerPath"/>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QTabWidget" name="tabWidget">
+        <property name="currentIndex">
+         <number>0</number>
+        </property>
+        <widget class="QWidget" name="tab">
+         <attribute name="title">
+          <string>Data pool</string>
+         </attribute>
+         <layout class="QHBoxLayout" name="horizontalLayout_2">
+          <item>
+           <layout class="QVBoxLayout" name="verticalLayout_3">
+            <item>
+             <widget class="QGroupBox" name="groupBox_6">
+              <property name="title">
+               <string>Que single</string>
+              </property>
+              <layout class="QVBoxLayout" name="verticalLayout_5">
+               <item>
+                <layout class="QGridLayout" name="gridLayout">
+                 <item row="0" column="0">
+                  <widget class="QLabel" name="label_6">
+                   <property name="text">
+                    <string>Fixed</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="2" column="0">
+                  <widget class="QComboBox" name="comboBox_Fixed">
+                   <property name="duplicatesEnabled">
+                    <bool>true</bool>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="2" column="1">
+                  <widget class="QComboBox" name="comboBox_Moving"/>
+                 </item>
+                 <item row="0" column="1">
+                  <widget class="QLabel" name="label_7">
+                   <property name="text">
+                    <string>Moving</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="2" column="2">
+                  <widget class="QComboBox" name="comboBox_Command"/>
+                 </item>
+                 <item row="0" column="2">
+                  <widget class="QLabel" name="label_8">
+                   <property name="text">
+                    <string>Command file</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="2" column="3">
+                  <widget class="QPushButton" name="pushButton_QueSingle">
+                   <property name="text">
+                    <string>Send to Que</string>
+                   </property>
+                  </widget>
+                 </item>
+                </layout>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item>
+             <widget class="QGroupBox" name="groupBox_7">
+              <property name="title">
+               <string>Que multiple</string>
+              </property>
+              <layout class="QHBoxLayout" name="horizontalLayout_6">
+               <item>
+                <layout class="QHBoxLayout" name="horizontalLayout_9">
+                 <item>
+                  <widget class="QPushButton" name="pushButton_QueByLine">
+                   <property name="text">
+                    <string>Que all lines</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QPushButton" name="pushButton_QueByPermutation">
+                   <property name="text">
+                    <string>Que permutation</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <spacer name="horizontalSpacer_3">
+                   <property name="orientation">
+                    <enum>Qt::Horizontal</enum>
+                   </property>
+                   <property name="sizeHint" stdset="0">
+                    <size>
+                     <width>40</width>
+                     <height>20</height>
+                    </size>
+                   </property>
+                  </spacer>
+                 </item>
+                </layout>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item>
+             <layout class="QHBoxLayout" name="horizontalLayout_7">
+              <item>
+               <widget class="QPushButton" name="pushButton_SortSelected">
+                <property name="text">
+                 <string>Sort selected column</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <spacer name="horizontalSpacer_2">
+                <property name="orientation">
+                 <enum>Qt::Horizontal</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>40</width>
+                  <height>20</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+              <item>
+               <widget class="QPushButton" name="pushButton_UpdateFileList">
+                <property name="text">
+                 <string>Apply deleting / renaming</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+            <item>
+             <widget class="QTableView" name="tableView_main">
+              <property name="enabled">
+               <bool>true</bool>
+              </property>
+              <property name="selectionMode">
+               <enum>QAbstractItemView::SingleSelection</enum>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <layout class="QVBoxLayout" name="verticalLayout_14">
+            <item>
+             <widget class="QGroupBox" name="groupBox_CommandTemplate">
+              <property name="title">
+               <string>Command File Template</string>
+              </property>
+              <layout class="QVBoxLayout" name="verticalLayout_4">
+               <item>
+                <layout class="QHBoxLayout" name="horizontalLayout_12">
+                 <item>
+                  <widget class="QListWidget" name="listWidgetCommandTemplate"/>
+                 </item>
+                 <item>
+                  <layout class="QVBoxLayout" name="verticalLayout_15">
+                   <item>
+                    <widget class="QPushButton" name="pushButtonTransferTemplate">
+                     <property name="text">
+                      <string>Transfer Selection</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item>
+                    <widget class="QPushButton" name="pushButtonTransferTemplate_2">
+                     <property name="text">
+                      <string>Delete Selection</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item>
+                    <widget class="QPushButton" name="pushButtonBrowseTemplateDir">
+                     <property name="text">
+                      <string>Browse Template Dir</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item>
+                    <spacer name="verticalSpacer">
+                     <property name="orientation">
+                      <enum>Qt::Vertical</enum>
+                     </property>
+                     <property name="sizeHint" stdset="0">
+                      <size>
+                       <width>20</width>
+                       <height>40</height>
+                      </size>
+                     </property>
+                    </spacer>
+                   </item>
+                  </layout>
+                 </item>
+                </layout>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item>
+             <widget class="QGroupBox" name="groupBox_CommandFile">
+              <property name="enabled">
+               <bool>true</bool>
+              </property>
+              <property name="title">
+               <string>Command file</string>
+              </property>
+              <layout class="QVBoxLayout" name="verticalLayout_12">
+               <item>
+                <layout class="QHBoxLayout" name="horizontalLayout_3">
+                 <item>
+                  <widget class="QPushButton" name="pushButton_CopyCommand">
+                   <property name="text">
+                    <string>Copy selection to</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QLineEdit" name="lineEdit_NumOfCopy">
+                   <property name="text">
+                    <string>1</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QLabel" name="label_10">
+                   <property name="text">
+                    <string>files</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QPushButton" name="pushButton_ClearCommandFiles">
+                   <property name="text">
+                    <string>Clear all</string>
+                   </property>
+                  </widget>
+                 </item>
+                </layout>
+               </item>
+               <item>
+                <layout class="QHBoxLayout" name="horizontalLayout_4">
+                 <item>
+                  <widget class="QLabel" name="label_2">
+                   <property name="text">
+                    <string>Command File Editor</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <spacer name="horizontalSpacer_5">
+                   <property name="orientation">
+                    <enum>Qt::Horizontal</enum>
+                   </property>
+                   <property name="sizeHint" stdset="0">
+                    <size>
+                     <width>40</width>
+                     <height>20</height>
+                    </size>
+                   </property>
+                  </spacer>
+                 </item>
+                 <item>
+                  <widget class="QLabel" name="label_CurCommandFile">
+                   <property name="text">
+                    <string>Current file</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <spacer name="horizontalSpacer">
+                   <property name="orientation">
+                    <enum>Qt::Horizontal</enum>
+                   </property>
+                   <property name="sizeHint" stdset="0">
+                    <size>
+                     <width>40</width>
+                     <height>20</height>
+                    </size>
+                   </property>
+                  </spacer>
+                 </item>
+                 <item>
+                  <widget class="QPushButton" name="pushButton_SaveCommandText">
+                   <property name="text">
+                    <string>Save</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QPushButton" name="pushButton_SaveCommandAsTemplate">
+                   <property name="text">
+                    <string>Save As Template</string>
+                   </property>
+                  </widget>
+                 </item>
+                </layout>
+               </item>
+               <item>
+                <widget class="QPlainTextEdit" name="plainTextEdit">
+                 <property name="maximumSize">
+                  <size>
+                   <width>16777215</width>
+                   <height>268</height>
+                  </size>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+        <widget class="QWidget" name="tab_2">
+         <attribute name="title">
+          <string>Que</string>
+         </attribute>
+         <layout class="QHBoxLayout" name="horizontalLayout_8">
+          <item>
+           <layout class="QVBoxLayout" name="verticalLayout_7">
+            <item>
+             <widget class="QTableView" name="tableView_que">
+              <property name="enabled">
+               <bool>true</bool>
+              </property>
+              <property name="selectionMode">
+               <enum>QAbstractItemView::SingleSelection</enum>
+              </property>
+              <property name="selectionBehavior">
+               <enum>QAbstractItemView::SelectRows</enum>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <layout class="QHBoxLayout" name="horizontalLayout_5">
+              <item>
+               <widget class="QGroupBox" name="groupBox">
+                <property name="title">
+                 <string>Edit Que</string>
+                </property>
+                <layout class="QVBoxLayout" name="verticalLayout_8">
+                 <item>
+                  <layout class="QVBoxLayout" name="verticalLayout_6">
+                   <item>
+                    <widget class="QPushButton" name="pushButton_RemoveSel">
+                     <property name="text">
+                      <string>Remove selection</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item>
+                    <widget class="QPushButton" name="pushButton_ClearQue">
+                     <property name="text">
+                      <string>Clear all</string>
+                     </property>
+                    </widget>
+                   </item>
+                  </layout>
+                 </item>
+                </layout>
+               </widget>
+              </item>
+              <item>
+               <widget class="QGroupBox" name="groupBox_2">
+                <property name="title">
+                 <string>Run Que</string>
+                </property>
+                <layout class="QVBoxLayout" name="verticalLayout_10">
+                 <item>
+                  <layout class="QGridLayout" name="gridLayout_2">
+                   <item row="1" column="0">
+                    <widget class="QPushButton" name="pushButton_RunAll_Seq">
+                     <property name="text">
+                      <string>Run sequentially</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="0" column="0">
+                    <widget class="QPushButton" name="pushButton_RunSelected">
+                     <property name="text">
+                      <string>Run selection</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="2" column="0">
+                    <widget class="QPushButton" name="pushButton_RunAll_MultiThread">
+                     <property name="text">
+                      <string>Run multi-thread</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="1" column="1">
+                    <widget class="QLabel" name="label_5">
+                     <property name="text">
+                      <string>Total time (s)</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="2" column="2">
+                    <widget class="QLineEdit" name="lineEdit_TotalProcTimeMT">
+                     <property name="text">
+                      <string>0</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="2" column="1">
+                    <widget class="QLabel" name="label_11">
+                     <property name="text">
+                      <string>Total time (s)</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="1" column="2">
+                    <widget class="QLineEdit" name="lineEdit_TotalProcTimeSeq">
+                     <property name="text">
+                      <string>0</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="3" column="1">
+                    <widget class="QLabel" name="label_3">
+                     <property name="text">
+                      <string>Number of threads</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item row="3" column="2">
+                    <widget class="QLineEdit" name="lineEdit_MaxNumberThread">
+                     <property name="text">
+                      <string>4</string>
+                     </property>
+                    </widget>
+                   </item>
+                  </layout>
+                 </item>
+                </layout>
+               </widget>
+              </item>
+              <item>
+               <widget class="QGroupBox" name="groupBox_4">
+                <property name="title">
+                 <string>Review</string>
+                </property>
+                <layout class="QVBoxLayout" name="verticalLayout_13">
+                 <item>
+                  <layout class="QVBoxLayout" name="verticalLayout_11">
+                   <item>
+                    <widget class="QPushButton" name="pushButtonViewSelectedImg">
+                     <property name="text">
+                      <string>View selected img</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item>
+                    <widget class="QPushButton" name="pushButton_OpenSelectedOutput">
+                     <property name="text">
+                      <string>Open selected dir</string>
+                     </property>
+                    </widget>
+                   </item>
+                   <item>
+                    <widget class="QPushButton" name="pushButton_CopyTableToClipboard">
+                     <property name="text">
+                      <string>Copy To Clipboard</string>
+                     </property>
+                    </widget>
+                   </item>
+                  </layout>
+                 </item>
+                </layout>
+               </widget>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <layout class="QVBoxLayout" name="verticalLayout_9">
+            <item>
+             <widget class="QLabel" name="label_4">
+              <property name="text">
+               <string>Command File Viewer</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPlainTextEdit" name="plainTextEdit_Que">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </widget>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menuBar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>838</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuView">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionLoad_fixed_images"/>
+    <addaction name="actionLoad_moving_images"/>
+    <addaction name="actionLoad_command_files"/>
+    <addaction name="actionImport_data_pool"/>
+    <addaction name="actionExport_data_pool"/>
+    <addaction name="actionSave_Que_Table_To_Text"/>
+   </widget>
+   <widget class="QMenu" name="menuView_2">
+    <property name="title">
+     <string>View</string>
+    </property>
+    <addaction name="actionVolume_Viewer"/>
+   </widget>
+   <addaction name="menuView"/>
+   <addaction name="menuView_2"/>
+  </widget>
+  <widget class="QToolBar" name="mainToolBar">
+   <attribute name="toolBarArea">
+    <enum>TopToolBarArea</enum>
+   </attribute>
+   <attribute name="toolBarBreak">
+    <bool>false</bool>
+   </attribute>
+  </widget>
+  <widget class="QStatusBar" name="statusBar"/>
+  <action name="actionVolume_Viewer">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Volume Viewer</string>
+   </property>
+  </action>
+  <action name="actionLoad_fixed_images">
+   <property name="text">
+    <string>Load fixed images</string>
+   </property>
+  </action>
+  <action name="actionLoad_moving_images">
+   <property name="text">
+    <string>Load moving images</string>
+   </property>
+  </action>
+  <action name="actionLoad_command_files">
+   <property name="text">
+    <string>Load command files</string>
+   </property>
+  </action>
+  <action name="actionSave_Que_Table_To_Text">
+   <property name="text">
+    <string>Save que table to text file</string>
+   </property>
+  </action>
+  <action name="actionImport_data_pool">
+   <property name="text">
+    <string>Import data pool</string>
+   </property>
+  </action>
+  <action name="actionExport_data_pool">
+   <property name="text">
+    <string>Export data pool</string>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>pushButton_SetDefaultDir</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_SetDefaultDir()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>131</x>
+     <y>64</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>103</x>
+     <y>76</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_SaveCommandText</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_SaveCommandText()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>703</x>
+     <y>583</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>682</x>
+     <y>700</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_QueSingle</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_AddSingleToQue()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>343</x>
+     <y>202</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>568</x>
+     <y>180</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_QueByLine</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_AddMultipleToQueByLine()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>49</x>
+     <y>263</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>8</x>
+     <y>299</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_QueByPermutation</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_AddMultipleToQueByPermu()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>202</x>
+     <y>266</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>6</x>
+     <y>261</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionLoad_fixed_images</sender>
+   <signal>triggered()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_LoadFixedFiles()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>434</x>
+     <y>352</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionLoad_moving_images</sender>
+   <signal>triggered()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_LoadMovingFiles()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>434</x>
+     <y>352</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionLoad_command_files</sender>
+   <signal>triggered()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_LoadCommandFiles()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>434</x>
+     <y>352</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_CopyCommand</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_CopyCommandFile()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>464</x>
+     <y>552</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>570</x>
+     <y>284</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_ClearCommandFiles</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_ClearCommandFiles()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>802</x>
+     <y>552</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>897</x>
+     <y>191</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_UpdateFileList</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_UpdateFileList()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>353</x>
+     <y>307</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>572</x>
+     <y>349</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_SortSelected</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_SortSelectedColumn()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>129</x>
+     <y>307</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>913</x>
+     <y>71</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_ClearQue</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_ClearQueAll()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>125</x>
+     <y>837</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>6</x>
+     <y>658</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_RemoveSel</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_RemoveSelectedQue()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>125</x>
+     <y>790</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>167</x>
+     <y>688</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_RunSelected</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_RunSingleSelected()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>241</x>
+     <y>772</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>323</x>
+     <y>680</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_RunAll_Seq</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_RunBatchSequential()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>241</x>
+     <y>801</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>484</x>
+     <y>675</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_RunAll_MultiThread</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_RunBatchMultiThread()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>241</x>
+     <y>830</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>105</x>
+     <y>678</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_OpenSelectedOutput</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_OpenSelectedOutputDir()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>568</x>
+     <y>813</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>666</x>
+     <y>680</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_CopyTableToClipboard</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_CopyTableQueToClipboard()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>568</x>
+     <y>848</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>635</x>
+     <y>677</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionSave_Que_Table_To_Text</sender>
+   <signal>triggered()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLTM_ExportQueResult()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>530</x>
+     <y>350</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_SetViewerPath</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_SetDefaultViewer()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>161</x>
+     <y>90</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>978</x>
+     <y>101</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonViewSelectedImg</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_ViewSelectedImg()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>568</x>
+     <y>778</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>565</x>
+     <y>891</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>listWidgetCommandTemplate</sender>
+   <signal>itemClicked(QListWidgetItem*)</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_CommandTemplateSelected()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>534</x>
+     <y>207</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>831</x>
+     <y>182</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonTransferTemplate</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_CopyCommandTemplateToDataPool()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>771</x>
+     <y>172</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>832</x>
+     <y>135</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButton_SaveCommandAsTemplate</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_SaveCommandFileAsTemplate()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>770</x>
+     <y>575</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>833</x>
+     <y>585</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonBrowseWorkingDir</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_BrowseWorkingDir()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>805</x>
+     <y>57</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>837</x>
+     <y>87</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonBrowseTemplateDir</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_BrowseTemplateDir()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>779</x>
+     <y>240</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>835</x>
+     <y>267</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>pushButtonTransferTemplate_2</sender>
+   <signal>released()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_DeleteSingleTemplate()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>771</x>
+     <y>204</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>832</x>
+     <y>219</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionImport_data_pool</sender>
+   <signal>triggered()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLTM_ImportDataPool()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>418</x>
+     <y>456</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>actionExport_data_pool</sender>
+   <signal>triggered()</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLTM_ExportDataPool()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>418</x>
+     <y>456</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>tableView_main</sender>
+   <signal>clicked(QModelIndex)</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_ItemClickedMain()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>54</x>
+     <y>387</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>7</x>
+     <y>389</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>tableView_que</sender>
+   <signal>clicked(QModelIndex)</signal>
+   <receiver>register_guiClass</receiver>
+   <slot>SLT_ItemClickedQue()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>483</x>
+     <y>164</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>248</x>
+     <y>40</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+ <slots>
+  <slot>SLT_SetDefaultDir()</slot>
+  <slot>SLT_LoadFixedFiles()</slot>
+  <slot>SLT_LoadMovingFiles()</slot>
+  <slot>SLT_LoadCommandFiles()</slot>
+  <slot>SLT_ReadCommandFile(QModelIndex)</slot>
+  <slot>SLT_SaveCommandText()</slot>
+  <slot>SLT_AddSingleToQue()</slot>
+  <slot>SLT_AddMultipleToQueByLine()</slot>
+  <slot>SLT_AddMultipleToQueByPermu()</slot>
+  <slot>SLT_CopyCommandFile()</slot>
+  <slot>SLT_ClearCommandFiles()</slot>
+  <slot>SLT_UpdateFileList()</slot>
+  <slot>SLT_SortSelectedColumn()</slot>
+  <slot>SLT_ClearQueAll()</slot>
+  <slot>SLT_RemoveSelectedQue()</slot>
+  <slot>SLT_RunSingleSelected()</slot>
+  <slot>SLT_RunBatchSequential()</slot>
+  <slot>SLT_RunBatchMultiThread()</slot>
+  <slot>SLT_OpenSelectedOutputDir()</slot>
+  <slot>SLT_CopyTableQueToClipboard()</slot>
+  <slot>SLTM_ExportQueResult()</slot>
+  <slot>SLT_SetDefaultViewer()</slot>
+  <slot>SLT_ViewSelectedImg()</slot>
+  <slot>SLT_CreateSampleRigid()</slot>
+  <slot>SLT_CreateSampleDeform()</slot>
+  <slot>SLT_CommandTemplateSelected()</slot>
+  <slot>SLT_CopyCommandTemplateToDataPool()</slot>
+  <slot>SLT_SaveCommandFileAsTemplate()</slot>
+  <slot>SLT_BrowseWorkingDir()</slot>
+  <slot>SLT_BrowseTemplateDir()</slot>
+  <slot>SLT_DeleteSingleTemplate()</slot>
+  <slot>SLTM_ImportDataPool()</slot>
+  <slot>SLTM_ExportDataPool()</slot>
+  <slot>SLT_ItemClickedMain()</slot>
+  <slot>SLT_ItemClickedQue()</slot>
+ </slots>
+</ui>
diff --git a/src/plastimatch/standalone/nki2mha_converter_main.cpp b/src/plastimatch/standalone/register_gui_main.cpp
similarity index 66%
copy from src/plastimatch/standalone/nki2mha_converter_main.cpp
copy to src/plastimatch/standalone/register_gui_main.cpp
index 2e20b2c..666b3d3 100644
--- a/src/plastimatch/standalone/nki2mha_converter_main.cpp
+++ b/src/plastimatch/standalone/register_gui_main.cpp
@@ -1,10 +1,10 @@
-#include "nki2mha_converter.h"
-#include <QtGui/QApplication>
-
-int main(int argc, char *argv[])
-{
-	QApplication a(argc, argv);
-	nki2mha_converter w;
-	w.show();
-	return a.exec();
-}
+#include "register_gui.h"
+#include <QtGui/QApplication>
+
+int main(int argc, char *argv[])
+{
+	QApplication a(argc, argv);
+        register_gui w;
+	w.show();
+	return a.exec();
+}
diff --git a/src/plastimatch/standalone/shuffle_mha_main.cxx b/src/plastimatch/standalone/shuffle_mha_main.cxx
index db9c174..0378de9 100755
--- a/src/plastimatch/standalone/shuffle_mha_main.cxx
+++ b/src/plastimatch/standalone/shuffle_mha_main.cxx
@@ -45,7 +45,7 @@ main (int argc, char* argv[])
     typedef itk::ImageSliceIteratorWithIndex<FloatImageType> IteratorType;
     ConstIteratorType it_in (v_in, v_in->GetLargestPossibleRegion());
     IteratorType it_out (v_out, v_out->GetLargestPossibleRegion());
-    ImageRegionType rgn_out = v_out->GetLargestPossibleRegion();
+    RegionType rgn_out = v_out->GetLargestPossibleRegion();
     it_in.SetFirstDirection(0);
     it_in.SetSecondDirection(1);
     it_out.SetFirstDirection(0);
diff --git a/src/plastimatch/standalone/sobp_main.cxx b/src/plastimatch/standalone/sobp_main.cxx
index 698739d..3782b44 100644
--- a/src/plastimatch/standalone/sobp_main.cxx
+++ b/src/plastimatch/standalone/sobp_main.cxx
@@ -1,92 +1,96 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-#include "plm_config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-
-#include "rt_sobp.h"
-
-int main (int argc, char* argv[])
-{
-    float dmin, dmax;
-    int Emin, Emax;
-    int particle;
-
-    Particle_type particle_type;
-
-    if (argc < 4) {
-        printf (
-            "Usage:\n"
-            "  sobp d dmin dmax    // Optimize in mm from dmin to dmax\n"
-            "  sobp e emin emax    // Optimize in MeV from emin to emax\n");
-        exit (0);
-    }
-
-    sscanf(argv[1],"%d", &particle);
-
-    if(particle ==1)
-    {
-        particle_type = PARTICLE_TYPE_P;
-    }
-    else if (particle ==2)
-    {
-        particle_type = PARTICLE_TYPE_HE;
-    }
-    else if (particle ==3)
-    {
-        particle_type = PARTICLE_TYPE_LI;
-    }
-    else if (particle ==4)
-    {
-        particle_type = PARTICLE_TYPE_BE;
-    }
-    else if (particle ==5)
-    {
-        particle_type = PARTICLE_TYPE_B;
-    }
-    else if (particle ==6)
-    {
-        particle_type = PARTICLE_TYPE_C;
-    }
-    else if (particle ==8)
-    {
-        particle_type = PARTICLE_TYPE_O;
-    }
-    else
-    {
-        particle_type = PARTICLE_TYPE_P;
-        printf("Invalid particle type");
-    }
-
-    if (particle_type != PARTICLE_TYPE_P) // no data for ions... to be implemented (ion bragg peaks!!)
-    {
-        particle_type = PARTICLE_TYPE_P;
-        printf("Ions data are not ready yet - beam switched to proton beams");
-    }
-
-    Rt_sobp sobp(particle_type);
-
-    // construction of the sobp using the proximal and distal limits
-    if (argv[2][0]=='d')
-    {
-        sscanf (argv[3], "%f", &dmin);
-        sscanf (argv[4], "%f", &dmax);
-        sobp.SetMinMaxDepths(dmin, dmax);
-    }
-    // construction of the sobp using the lower and higher energy
-    else if (argv[2][0]=='e')
-    {
-        sscanf (argv[3], "%d", &Emin);
-        sscanf (argv[4], "%d", &Emax);
-        sobp.SetMinMaxDepths(Emin, Emax);
-    }
-
-    sobp.printparameters();
-    sobp.Optimizer(sobp.optimizer_num_peaks());
-
-    sobp.print_sobp_curve();
-
-    return 0;
-}
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plm_config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "rt_mebs.h"
+
+int main (int argc, char* argv[])
+{
+    float dmin, dmax;
+    int Emin, Emax;
+    int particle;
+
+    Particle_type particle_type;
+
+    if (argc < 4) {
+        printf (
+            "Usage:\n"
+            "  sobp d dmin dmax    // Optimize in mm from dmin to dmax\n"
+            "  sobp e emin emax    // Optimize in MeV from emin to emax\n");
+        exit (0);
+    }
+
+    sscanf(argv[1],"%d", &particle);
+
+    if(particle ==1)
+    {
+        particle_type = PARTICLE_TYPE_P;
+    }
+    else if (particle ==2)
+    {
+        particle_type = PARTICLE_TYPE_HE;
+    }
+    else if (particle ==3)
+    {
+        particle_type = PARTICLE_TYPE_LI;
+    }
+    else if (particle ==4)
+    {
+        particle_type = PARTICLE_TYPE_BE;
+    }
+    else if (particle ==5)
+    {
+        particle_type = PARTICLE_TYPE_B;
+    }
+    else if (particle ==6)
+    {
+        particle_type = PARTICLE_TYPE_C;
+    }
+    else if (particle ==8)
+    {
+        particle_type = PARTICLE_TYPE_O;
+    }
+    else
+    {
+        particle_type = PARTICLE_TYPE_P;
+        printf("Invalid particle type");
+    }
+
+    if (particle_type != PARTICLE_TYPE_P) // no data for ions... to be implemented (ion bragg peaks!!)
+    {
+        particle_type = PARTICLE_TYPE_P;
+        printf("Ions data are not ready yet - beam switched to proton beams");
+    }
+
+    Rt_mebs mebs(particle_type);
+
+    // construction of the sobp using the proximal and distal limits
+    if (argv[2][0]=='d')
+    {
+        sscanf (argv[3], "%f", &dmin);
+        sscanf (argv[4], "%f", &dmax);
+        mebs.set_prescription_depths(dmin, dmax);
+    }
+    // construction of the sobp using the lower and higher energy
+    else if (argv[2][0]=='e')
+    {
+        sscanf (argv[3], "%d", &Emin);
+        sscanf (argv[4], "%d", &Emax);
+		mebs.set_energies(Emin, Emax);
+    }
+	std::vector<float> weight;
+	std::vector<float> energy;
+    mebs.optimizer(&weight, &energy);
+	for (int i = 0; i < energy.size(); i++)
+	{
+		mebs.add_peak(energy[i], mebs.get_spread(), weight[i]);
+	}
+	mebs.generate();
+	mebs.printparameters();
+
+    return 0;
+}
diff --git a/src/plastimatch/standalone/wed_main.cxx b/src/plastimatch/standalone/wed_main.cxx
index c855a0e..099d0b0 100644
--- a/src/plastimatch/standalone/wed_main.cxx
+++ b/src/plastimatch/standalone/wed_main.cxx
@@ -9,6 +9,7 @@
 #include "aperture.h"
 #include "plm_image.h"
 #include "plm_math.h"
+#include "print_and_exit.h"
 #include "proj_volume.h"
 #include "ray_trace_probe.h"
 #include "rpl_volume.h"
@@ -18,15 +19,106 @@
 #include "volume_limit.h"
 #include "wed_parms.h"
 
-#include "create_wed_volumes.cxx"
+Volume* 
+create_wed_volume (Wed_Parms* parms, Rpl_volume* rpl_vol)
+{
+
+   /* water equivalent depth volume has the same x,y dimensions as the rpl
+     * volume. Note: this means the wed x,y dimensions are equal to the
+     * aperture dimensions and the z-dimension is equal to the sampling
+     * resolution chosen for the rpl */
+    plm_long wed_dims[3];
+
+    Volume *vol = rpl_vol->get_vol ();
+    wed_dims[0] = vol->dim[0];
+    wed_dims[1] = vol->dim[1];
+    wed_dims[2] = vol->dim[2];
+
+    /////////////////////////////
+    //Should be insenstive to aperture rotation?
+    /*
+    Proj_volume *proj_vol = d_ptr->proj_vol;
+    double iso_src_vec[3];   //vector from isocenter to source
+    proj_vol->get_proj_matrix()->get_nrm(iso_src_vec);
+    */
+
+    float xoff = -(vol->dim[0] - parms->ic[0]);
+    float yoff = -(vol->dim[1] - parms->ic[1]);
+
+    float wed_off[3] = {xoff, yoff, 0.0f};
+    float wed_ps[3] = {1.0f, 1.0f, 1.0f};
+
+    return new Volume (wed_dims, wed_off, wed_ps, NULL, PT_FLOAT, 1);
+}
+
+static Volume*
+create_dew_volume (Wed_Parms* parms, const Volume::Pointer& patient_vol)
+{
+    float dew_off[3];
+    dew_off[0] = patient_vol->origin[0];
+    dew_off[1] = patient_vol->origin[1];
+    dew_off[2] = patient_vol->origin[2];
+
+    float dew_ps[3];
+    dew_ps[0] = patient_vol->spacing[0];
+    dew_ps[1] = patient_vol->spacing[1];
+    dew_ps[2] = patient_vol->spacing[2];
+
+    plm_long dew_dims[3];
+    dew_dims[0] = patient_vol->dim[0];
+    dew_dims[1] = patient_vol->dim[1];
+    dew_dims[2] = patient_vol->dim[2];
+
+    //If output volume dimensions were set in .cfg file, use these.
+    if (parms->dew_dim[0]!=-999.) {dew_dims[0]=parms->dew_dim[0];}
+    if (parms->dew_dim[1]!=-999.) {dew_dims[1]=parms->dew_dim[1];}
+    if (parms->dew_dim[2]!=-999.) {dew_dims[2]=parms->dew_dim[2];}
+
+    if (parms->dew_origin[0]!=-999.) {dew_off[0]=parms->dew_origin[0];}
+    if (parms->dew_origin[1]!=-999.) {dew_off[1]=parms->dew_origin[1];}
+    if (parms->dew_origin[2]!=-999.) {dew_off[2]=parms->dew_origin[2];}
+
+    if (parms->dew_spacing[0]!=-999.) {dew_ps[0]=parms->dew_spacing[0];}
+    if (parms->dew_spacing[1]!=-999.) {dew_ps[1]=parms->dew_spacing[1];}
+    if (parms->dew_spacing[2]!=-999.) {dew_ps[2]=parms->dew_spacing[2];}
+
+    return new Volume (dew_dims, dew_off, dew_ps, NULL, PT_FLOAT, 1);
+}
+
+Volume* create_proj_wed_volume (Rpl_volume* rpl_vol)
+{
+    float proj_wed_off[3] = {0.0f, 0.0f, 0.0f};
+    float proj_wed_ps[3] = {1.0f, 1.0f, 1.0f};
+    plm_long proj_wed_dims[3];
 
+    Volume *vol = rpl_vol->get_vol ();
+    proj_wed_dims[0] = vol->dim[0];
+    proj_wed_dims[1] = vol->dim[1];
+    proj_wed_dims[2] = 1;
 
-typedef struct callback_data Callback_data;
-struct callback_data {
-    Volume* wed_vol;   /* Water equiv depth volume */
-    int* ires;         /* Aperture Dimensions */
-    int ap_idx;        /* Current Aperture Coord */
-};
+    return new Volume (proj_wed_dims, proj_wed_off, proj_wed_ps, NULL, PT_FLOAT, 1);
+}
+
+Volume* 
+create_proj_sinogram_volume (Wed_Parms* parms, Volume *proj_wed_vol)
+{
+    float proj_wed_off[3];
+    proj_wed_off[0] = proj_wed_vol->origin[0];
+    proj_wed_off[1] = proj_wed_vol->origin[1];
+    proj_wed_off[2] = proj_wed_vol->origin[2];
+
+    float proj_wed_ps[3];
+    proj_wed_ps[0] = proj_wed_vol->spacing[0];
+    proj_wed_ps[1] = proj_wed_vol->spacing[1];
+    proj_wed_ps[2] = proj_wed_vol->spacing[2];
+
+    plm_long proj_wed_dims[3];
+    proj_wed_dims[0] = proj_wed_vol->dim[0];
+    proj_wed_dims[1] = proj_wed_vol->dim[1];
+    proj_wed_dims[2] = parms->sinogram_res;
+
+    return new Volume (proj_wed_dims, proj_wed_off, proj_wed_ps, NULL, PT_FLOAT, 1);
+}
 
 static int
 skin_ct (Volume* ct_volume, Volume* skin_volume, float background)
@@ -57,151 +149,124 @@ skin_ct (Volume* ct_volume, Volume* skin_volume, float background)
   return 0;
 }
 
+#if defined (commentout)
 void
-wed_ct_compute (
+wed_ct_compute_mode_2 (
     const std::string& out_fn,
     Wed_Parms* parms,
     Plm_image::Pointer& ct_vol,  // This is not always ct, 
                                  //  sometimes it is dose or 
                                  //  sometimes it is target mask.
     Rt_plan *scene,
+    Rt_beam *beam,
     float background
 )
 {
-    Rpl_volume* rpl_vol = scene->beam->rpl_vol;
-
-    if (parms->mode==0)  {
-        Volume* wed_vol;
-	wed_vol = create_wed_volume (parms, rpl_vol);
-        rpl_vol->compute_wed_volume (wed_vol, ct_vol->get_volume_float().get(), 
-            background);
-        Plm_image(wed_vol).save_image(out_fn);
-    }
+    Rpl_volume* rpl_vol = beam->rpl_vol;
 
-    if (parms->mode==1)  {
-        Volume* dew_vol;
-	//Fix below function, move to rpl_volume as create_wed_volume above.
-	//Dew parameters will need to be incorporated into ion_beam
-        dew_vol = create_dew_volume (parms, scene);
-        rpl_vol->compute_dew_volume (ct_vol->get_volume_float().get(), 
-            dew_vol, background);
-        Plm_image(dew_vol).save_image(out_fn);
-    }
+    /* Compute the aperture and range compensator */
+    rpl_vol->compute_beam_modifiers_passive_scattering (ct_vol->get_volume_float().get());
 
-    if (parms->mode==2) {
-        /* Compute the aperture and range compensator */
-        rpl_vol->compute_beam_modifiers (
-            ct_vol->get_volume_float().get(), 
-            background);
+    /* Save files as output */
+    Plm_image::Pointer& ap 
+        = rpl_vol->get_aperture()->get_aperture_image();
+    Plm_image::Pointer& rc 
+        = rpl_vol->get_aperture()->get_range_compensator_image();
 
-        /* Save files as output */
-        Plm_image::Pointer& ap 
-            = rpl_vol->get_aperture()->get_aperture_image();
-        Plm_image::Pointer& rc 
-            = rpl_vol->get_aperture()->get_range_compensator_image();
+#if defined (commentout)
+    ap->save_image (parms->output_ap_fn.c_str());
+    rc->save_image (out_fn);
+#endif
+}
 
-        ap->save_image (parms->output_ap_fn.c_str());
-        rc->save_image (out_fn);
-    }
+void
+wed_ct_compute_mode_3 (
+    const std::string& out_fn,
+    Wed_Parms* parms,
+    Plm_image::Pointer& ct_vol,  // This is not always ct, 
+                                 //  sometimes it is dose or 
+                                 //  sometimes it is target mask.
+    Rt_plan *scene,
+    Rt_beam *beam,
+    float background
+)
+{
+    Rpl_volume* rpl_vol = beam->rpl_vol;
 
-    if (parms->mode==3)  {
-        Volume* proj_wed_vol;
-        Volume* sinogram_vol;
+    Volume* proj_wed_vol;
+    Volume* sinogram_vol;
 	
-        proj_wed_vol = create_proj_wed_volume(rpl_vol);
-
-	if (parms->sinogram!=0)  {
-            sinogram_vol = create_proj_sinogram_volume(parms, proj_wed_vol);
-
-            float *sin_img = (float*) sinogram_vol->img;
-            float *proj_img = (float*) proj_wed_vol->img;
-            plm_long ijk[3] = {0,0,0};
-            plm_long n_voxels_sin = sinogram_vol->dim[0]*sinogram_vol->dim[1]*sinogram_vol->dim[2];
-            plm_long n_voxels_proj = proj_wed_vol->dim[0]*proj_wed_vol->dim[1]*proj_wed_vol->dim[2];
-            float *sin_array = new float[n_voxels_sin];
-            float *proj_array = new float[n_voxels_proj];
-
-            //Loop over angles determined by the resolution, and calcaulate a projective
-            //volume for each.  Then fill the sinogram volume with each slice.
-            int angles = parms->sinogram_res;
-
-            float src[3] = {parms->src[0], parms->src[1], parms->src[2]};
-            float iso[3] = {parms->isocenter[0], parms->isocenter[1], parms->isocenter[2]};
-
-            float src2[3] = {0, 0, parms->src[2]};
-            float radius[2] = {src[0]-iso[0], src[1]-iso[1]};
-            float radius_len = sqrt( (src[0]-iso[0])*(src[0]-iso[0]) + (src[1]-iso[1])*(src[1]-iso[1]));
-
-            float init_angle = atan2(radius[1],radius[0]);
-            float angle = 0;
-
-            for (int i=0; i!=angles; ++i)  {
-                angle = init_angle + ( i / (float) parms->sinogram_res)*2.*M_PI;
-                src2[0] = cos(angle)*radius_len + iso[0];
-                src2[1] = sin(angle)*radius_len + iso[1];
-
-                scene->beam->set_source_position (src2);
-                scene->init();
-                rpl_vol = scene->beam->rpl_vol;
-                rpl_vol->compute_proj_wed_volume (proj_wed_vol, background);
-
-                //Fill proj array with voxel values.
-                for (plm_long zz=0; zz!=n_voxels_proj; ++zz)  {
-                    proj_array[zz] = proj_img[zz];
-                }
+    proj_wed_vol = create_proj_wed_volume(rpl_vol);
+
+    if (parms->sinogram!=0)  {
+        sinogram_vol = create_proj_sinogram_volume(parms, proj_wed_vol);
+
+        float *sin_img = (float*) sinogram_vol->img;
+        float *proj_img = (float*) proj_wed_vol->img;
+        plm_long n_voxels_sin = sinogram_vol->dim[0]*sinogram_vol->dim[1]*sinogram_vol->dim[2];
+        plm_long n_voxels_proj = proj_wed_vol->dim[0]*proj_wed_vol->dim[1]*proj_wed_vol->dim[2];
+        float *sin_array = new float[n_voxels_sin];
+        float *proj_array = new float[n_voxels_proj];
+
+        //Loop over angles determined by the resolution, and calcaulate a projective
+        //volume for each.  Then fill the sinogram volume with each slice.
+        int angles = parms->sinogram_res;
+
+        float src[3] = {parms->src[0], parms->src[1], parms->src[2]};
+        float iso[3] = {parms->isocenter[0], parms->isocenter[1], parms->isocenter[2]};
+
+        float src2[3] = {0, 0, parms->src[2]};
+        float radius[2] = {src[0]-iso[0], src[1]-iso[1]};
+        float radius_len = sqrt( (src[0]-iso[0])*(src[0]-iso[0]) + (src[1]-iso[1])*(src[1]-iso[1]));
+
+        float init_angle = atan2(radius[1],radius[0]);
+        float angle = 0;
+
+        for (int i=0; i!=angles; ++i)  {
+            angle = init_angle + ( i / (float) parms->sinogram_res)*2.*M_PI;
+            src2[0] = cos(angle)*radius_len + iso[0];
+            src2[1] = sin(angle)*radius_len + iso[1];
+
+            beam->set_source_position (src2);
+            scene->prepare_beam_for_calc (beam);
+            rpl_vol = beam->rpl_vol;
+            rpl_vol->compute_proj_wed_volume (proj_wed_vol, background);
+
+            //Fill proj array with voxel values.
+            for (plm_long zz=0; zz!=n_voxels_proj; ++zz)  {
+                proj_array[zz] = proj_img[zz];
+            }
 	
-                for (int j=0; j!=proj_wed_vol->dim[0]; ++j)  {
-                    for (int k=0; k!=proj_wed_vol->dim[1]; ++k)  {
-                        sin_array[sinogram_vol->index(j,k,i)]
-                            = proj_array[proj_wed_vol->index(j,k,0)];
-                    }
+            for (int j=0; j!=proj_wed_vol->dim[0]; ++j)  {
+                for (int k=0; k!=proj_wed_vol->dim[1]; ++k)  {
+                    sin_array[sinogram_vol->index(j,k,i)]
+                        = proj_array[proj_wed_vol->index(j,k,0)];
                 }
             }
-            //Fill sinogram image with voxel values from assembled array.
-            for (plm_long zz=0; zz!=n_voxels_sin; ++zz)  {
-                sin_img[zz] = sin_array[zz];
-            }
-
-            Plm_image(sinogram_vol).save_image(out_fn);
+        }
+        //Fill sinogram image with voxel values from assembled array.
+        for (plm_long zz=0; zz!=n_voxels_sin; ++zz)  {
+            sin_img[zz] = sin_array[zz];
+        }
 
-            delete[] sin_array;
-            delete[] proj_array;
-	}
+        Plm_image(sinogram_vol).save_image(out_fn);
 
-	else {
-            rpl_vol->compute_proj_wed_volume (proj_wed_vol, background);
-            Plm_image(proj_wed_vol).save_image(out_fn);
-	}       
+        delete[] sin_array;
+        delete[] proj_array;
     }
-}
 
-void
-wed_ct_compute (
-    const std::string& out_fn,
-    Wed_Parms* parms,
-    Rt_plan *scene,
-    float background
-)
-{
-    Plm_image::Pointer ct_vol = Plm_image::New();
-    wed_ct_compute (out_fn, parms, ct_vol, scene, background);
+    else {
+        rpl_vol->compute_proj_wed_volume (proj_wed_vol, background);
+        Plm_image(proj_wed_vol).save_image(out_fn);
+    }       
 }
+#endif
 
-int
-wed_ct_initialize(Wed_Parms *parms)
+void
+do_wed (Wed_Parms *parms)
 {
-
-    Plm_image::Pointer dose_vol;
-    Rt_plan scene;
-    scene.beam = new Rt_beam;
     float background[4];
 
-    /*
-    for (int i=0; i=100000000;++i)  {
-      scene.init ();
-    }
-    */
-
     //Background value for wed ct output
     background[0] = -1000.;
     //Background value for wed dose output
@@ -211,207 +276,126 @@ wed_ct_initialize(Wed_Parms *parms)
     //Background value for projection of wed
     background[3] = 0.;
 
-    /* load the patient and insert into the scene */
+    /* load the input ct */
     Plm_image::Pointer ct_vol = Plm_image::New (
         parms->input_ct_fn, PLM_IMG_TYPE_ITK_FLOAT);
     if (!ct_vol) {
-        fprintf (stderr, "\n** ERROR: Unable to load patient volume.\n");
-        return -1;
+        print_and_exit ("** ERROR: Unable to load patient volume.\n");
     }
-  
-    if (parms->skin_fn != "") {
-        fprintf (stderr, "\n** Skin file defined.  Modifying input ct...\n");
+
+    /* Load the input dose */
+    Plm_image::Pointer dose_vol;
+    if (parms->input_dose_fn != "") {
+        printf("Loading input dose: %s\n",parms->input_dose_fn.c_str());
+        dose_vol = plm_image_load (parms->input_dose_fn.c_str(), 
+            PLM_IMG_TYPE_ITK_FLOAT);
+    }
+
+    /* Load the input proj_wed */
+    Rpl_volume::Pointer proj_wed = Rpl_volume::New ();
+    if (parms->input_proj_wed_fn != "") {
+        proj_wed->load_rpl (parms->input_proj_wed_fn);
+    }
+
+    if (parms->input_skin_fn != "") {
+        printf ("Skin file defined.  Modifying input ct...\n");
  
         Volume* ct_volume = ct_vol->get_volume_float().get();
-        Plm_image::Pointer skin_vol = plm_image_load (parms->skin_fn, 
+        Plm_image::Pointer skin_vol = plm_image_load (parms->input_skin_fn, 
             PLM_IMG_TYPE_ITK_FLOAT);
         if (!skin_vol) {
-            fprintf (stderr, "\n** ERROR: Unable to load skin input.\n");
-            return -1;
+            print_and_exit ("\n** ERROR: Unable to load skin input.\n");
         }
         Volume* skin_volume = skin_vol->get_volume_float().get();
     
-        if (skin_ct(ct_volume, skin_volume, background[0]))  {
-            //apply skin input to ct
-            fprintf (stderr, "\n** ERROR: Unable to apply skin input to ct input.\n");
+        if (skin_ct(ct_volume, skin_volume, background[0])) {
+            print_and_exit ("\n** ERROR: Unable to apply skin input to ct input.\n");
         }
     }
   
-    scene.set_patient (ct_vol);
-  
-    printf("%s\n",parms->input_dose_fn.c_str());
- 
-    //  if (parms->input_dose_fn != "" && parms->output_dose_fn != "") {
-    //Load the input dose, or input wed_dose
-    if ((parms->mode==0)||(parms->mode==1))  {
-        dose_vol = plm_image_load (parms->input_dose_fn.c_str(), 
-            PLM_IMG_TYPE_ITK_FLOAT);
-    }
-    /* set scene parameters */
-    scene.beam->set_source_position (parms->src);
-    scene.beam->set_isocenter_position (parms->isocenter);
-
-    scene.beam->get_aperture()->set_distance (parms->ap_offset);
-    scene.beam->get_aperture()->set_spacing (parms->ap_spacing);
-  
-    //Scene dimensions are set by .cfg file
-
-    //Note: Set dimensions first, THEN center, as set_dim() also changes center
-    int ap_res[2];
-    float ap_center[2];
-
-    //Aperture dimensions
+    Aperture::Pointer aperture = Aperture::New();
+    aperture->set_distance (parms->ap_offset);
+    aperture->set_spacing (parms->ap_spacing);
     if (parms->have_ires) {
-        scene.beam->get_aperture()->set_dim (parms->ires);
-	//If dew, pad each by one for interpolations
-	if (parms->mode==1)  {
-            ap_res[0] = (int) (parms->ires[0]+2);
-            ap_res[1] = (int) (parms->ires[1]+2);
-            scene.beam->get_aperture()->set_dim (ap_res);
-            parms->ires[0]=ap_res[0];
-            parms->ires[1]=ap_res[1];
-	}
-    }
-    //If dew option, and not specified in .cfg files, then we guess
-    //at some scene dimensions set by input wed image.
-
-    else if (parms->mode==1)  {
-        Volume *wed_vol = dose_vol->get_volume_float().get();
-        //Grab aperture dimensions from input wed.
-        //We also pad each dimension by 1, for the later trilinear 
-        //interpolations.
-        ap_res[0] = (int) (wed_vol->dim[0]+2);
-        ap_res[1] = (int) (wed_vol->dim[1]+2);
-  
-        scene.beam->get_aperture()->set_dim (ap_res);
-        parms->ires[0]=ap_res[0];
-        parms->ires[1]=ap_res[1];
+        aperture->set_dim (parms->ires);
     }
-
-    //Aperture Center
-    //Note - Center MUST be reset here if set in the config file, as set_dim()
-    //will reset the center.
     if (parms->have_ic) {
+        aperture->set_center (parms->ic);
+    }
 
-        if (parms->mode==1)  {
-
-	  //If center is not defined in config file (in general,
-	  //it shouldn't be), then default values should be reset
-	  if ((parms->ic[0]==-99.5)&&(parms->ic[1]==-99.5))  {
-	    Volume *wed_vol = dose_vol->get_volume_float().get();
-	    parms->ic[0] = wed_vol->origin[0] + wed_vol->dim[0];
-	    parms->ic[1] = wed_vol->origin[1] + wed_vol->dim[1];
-	  }
-
-	  //else set it at the center
-	  else {
-            ap_center[0] = parms->ic[0]+1.*parms->ap_spacing[0];
-            ap_center[1] = parms->ic[1]+1.*parms->ap_spacing[1];
-            scene.beam->get_aperture()->set_center (ap_center);
-	  }
-        }
+    double src[3], isocenter[3];
+    for (int i = 0; i < 3; i++) {
+        src[i] = parms->src[i];
+        isocenter[i] = parms->isocenter[i];
+    }
+    Rpl_volume rpl;
+    rpl.set_ct_volume (ct_vol);
+    rpl.set_aperture (aperture);
+    rpl.set_geometry (
+        src,
+        isocenter,
+        aperture->vup,
+        aperture->get_distance(),
+        aperture->get_dim(),
+        aperture->get_center(),
+        aperture->get_spacing(),
+        parms->ray_step);
+
+    if (proj_wed) {
+        proj_wed->set_ct_volume (ct_vol);
+        proj_wed->set_aperture (aperture);
+        proj_wed->set_geometry (
+            src,
+            isocenter,
+            aperture->vup,
+            aperture->get_distance(),
+            aperture->get_dim(),
+            aperture->get_center(),
+            aperture->get_spacing(),
+            parms->ray_step);
+    }
 
-        else {
-            ap_center[0] = parms->ic[0];
-            ap_center[1] = parms->ic[1];
-            scene.beam->get_aperture()->set_center (ap_center);
-        }
+    /* Compute the rpl volume */
+    rpl.compute_rpl_PrSTRP_no_rgc ();
 
-    }
-    //And again, guess if not specified.
-    //Note - add the dew option below if not specified.
-    else if (parms->mode==1)  {
-        //Set center as half the resolutions.
-        ap_center[0] = (float) ap_res[0]/2.;
-        ap_center[1] = (float) ap_res[1]/2.;
-        scene.beam->get_aperture()->set_center (ap_center);
-        parms->ic[0]=ap_center[0];
-        parms->ic[1]=ap_center[1];
-    } 
-
-    scene.beam->set_step_length(parms->ray_step);
-
-
-
-    /* Try to setup the scene with the provided parameters.
-       This function computes the rpl volume. */
-    if (!scene.init ()) {
-        fprintf (stderr, "ERROR: Unable to initilize scene.\n");
-        return -1;
+    if (parms->output_proj_wed_fn != "") {
+        rpl.save (parms->output_proj_wed_fn);
     }
 
-    /* Save rpl volume if requested */
-    if (parms->rpl_vol_fn != "") {
-        scene.beam->rpl_vol->save (parms->rpl_vol_fn);
+    if (parms->output_dew_ct_fn != "") {
+        Volume* dew_vol = create_dew_volume (parms, ct_vol->get_volume_float());
+        rpl.compute_dew_volume (ct_vol->get_volume_float().get(), 
+            dew_vol, -1000);
+        Plm_image(dew_vol).save_image(parms->output_dew_ct_fn);
     }
 
-    printf ("Working...\n");
-    fflush(stdout);
-  
-    /* Compute the ct-wed volume */
-    if (parms->mode==0)  {
+    if (parms->output_wed_ct_fn != "") {
         printf ("Computing patient wed volume...\n");
-        wed_ct_compute (parms->output_ct_fn, parms, ct_vol, 
-            &scene, background[0]);
+	Volume *wed_vol = create_wed_volume (parms, &rpl);
+        rpl.compute_wed_volume (wed_vol, ct_vol->get_volume_float().get(), 
+            background[0]);
+        Plm_image(wed_vol).save_image(parms->output_wed_ct_fn);
         printf ("done.\n");
     }
-  
-    /* Compute the dose-wed volume */
-    if (parms->input_dose_fn != "" && parms->output_dose_fn != "") {
-        if ((parms->mode==0)||(parms->mode==1))  {
-            printf ("Calculating dose...\n");
-            wed_ct_compute (parms->output_dose_fn,
-                parms, dose_vol, &scene, background[1]);
-            printf ("Complete...\n");
-        }
-    }
 
-    /* Compute the aperture and range compensator volumes */
-    if (parms->mode==2)  {
-        printf ("Calculating depths...\n");
-        wed_ct_compute (parms->output_depth_fn,
-            parms, dose_vol, &scene, background[2]);
-        printf ("Complete...\n");
+    /* Compute the proj_ct volume */
+    if (parms->output_proj_ct_fn != "") {
+        rpl.compute_rpl_HU ();
+        rpl.save (parms->output_proj_ct_fn);
     }
-
-    /* Compute the projective wed volume */
-    if (parms->mode==3)  {
-        printf ("Calculating wed projection...\n");
-        wed_ct_compute (parms->output_proj_wed_fn,
-            parms, &scene, background[3]);
-        printf ("Complete...\n");
-    }
-
-    return 0;
 }
 
 int
 main (int argc, char* argv[])
 {
     Wed_Parms *parms = new Wed_Parms();
-    int wed_iter = 1;
   
     //sets parms if input with .cfg file, skips with group option
     if (!parms->parse_args (argc, argv)) {
         exit (0); 
     }
-  
-    if (parms->group)  {
-        wed_iter = 0;
-    
-        while(wed_iter!=parms->group)  {
-            if (parms->group) {
-                parms->parse_group(argc, argv, wed_iter);
-                wed_ct_initialize(parms);
-                wed_iter++;
-            }
-      
-        }
-    }
-    else {
-        //Compute wed without loop
-        wed_ct_initialize(parms);
-    }
-  
+
+    do_wed (parms);
+
     return 0;
 }
diff --git a/src/plastimatch/standalone/xf_to_empirefmt.cxx b/src/plastimatch/standalone/xf_to_empirefmt.cxx
index fc9b625..1139bca 100644
--- a/src/plastimatch/standalone/xf_to_empirefmt.cxx
+++ b/src/plastimatch/standalone/xf_to_empirefmt.cxx
@@ -1,131 +1,131 @@
-/* -----------------------------------------------------------------------
-   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
-   ----------------------------------------------------------------------- */
-#include "plm_config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include "itkImage.h"
-#include "itkImageRegionConstIterator.h"
-
-#define BUFLEN 1024
-
-char header_pat[] = 
-    "ObjectType = Image\n"
-    "NDims = 3\n"
-    "BinaryData = True\n"
-    "BinaryDataByteOrderMSB = False\n"
-	"TransformMatrix = 1 0 0 0 1 0 0 0 1\n"
-    "Offset = 0 0 0\n"
-	"CenterOfRotation = 0 0 0\n"
-    "ElementSpacing = %f %f %f\n"
-    "DimSize = %d %d %d\n"
-	"AnatomicalOrientation = RPI\n"
-	"ElementNumberOfChannels = 1\n"
-    "ElementType = MET_FLOAT\n"
-    "ElementDataFile = %s\n"
-    ;
-
-
-int main (int argc, char* argv[])
-{
-    // File pointers
-    FILE *ofpx, *ofpy, *ofpz;
-    Plm_image_header pih;
-    FloatImageType::Pointer img_fixed;
-    DeformationFieldType::Pointer vf;
-    // input xform from bspline
-    Xform xf_in, xf_out;
-
-    if (argc!=4)  {
-        std::cerr << "Wrong Parameters " << std::endl;
-        std::cerr << "Usage: " << argv[0];
-        std::cerr << " <input xform> ";
-        std::cerr << " <fixed image> ";
-        std::cerr << " <outputVectorFieldDir>" << std::endl;;
-        return 1;
-    }
-
-    xform_load (&xf_in, argv[1]);
-    img_fixed = itk_image_load_float (argv[2], 0);
-    xform_to_itk_vf (&xf_out, &xf_in, img_fixed);
-    vf = xf_out.get_itk_vf();
-
-    // need to write to 3 separate files, named as defX.mhd defY.mhd and defZ.mhd
-    char ofn1[255], ofn2[255], ofn3[255];
-
-    sprintf(ofn1, "%s%s", argv[3], "defX.mhd");
-    ofpx = fopen(ofn1, "w");
-    if (ofpx == NULL) 
-    {
-        fprintf(stdout, "open file %s failed\n", ofn1);
-        exit(-1);
-    }
-    sprintf(ofn2, "%s%s", argv[3], "defY.mhd");
-    ofpy = fopen(ofn2, "w");
-    if (ofpx == NULL) 
-    {
-        fprintf(stdout, "open file %s failed\n", ofn2);
-        exit(-1);
-    }
-
-    sprintf(ofn3, "%s%s", argv[3], "defZ.mhd");
-    ofpz = fopen(ofn3, "w");
-    if (ofpz == NULL) 
-    {
-        fprintf(stdout, "open file %s failed\n", ofn3);
-        exit(-1);
-    }
-
-    // print file header
-    pih.set_from_itk_image (img_fixed);
-
-    // write deformation field to separate binary files
-    fprintf(ofpx, header_pat, pih.m_spacing[0], pih.m_spacing[1], pih.m_spacing[2], 
-        pih.m_region.GetSize()[0], pih.m_region.GetSize()[1], pih.m_region.GetSize()[2], 
-        "defX.raw");
-    fclose(ofpx);
-    fprintf(ofpy, header_pat, pih.m_spacing[0], pih.m_spacing[1], pih.m_spacing[2], 
-        pih.m_region.GetSize()[0], pih.m_region.GetSize()[1], pih.m_region.GetSize()[2], 
-        "defY.raw");
-    fclose(ofpy);
-    fprintf(ofpz, header_pat, pih.m_spacing[0], pih.m_spacing[1], pih.m_spacing[2], 
-        pih.m_region.GetSize()[0], pih.m_region.GetSize()[1], pih.m_region.GetSize()[2], 
-        "defZ.raw");
-    fclose(ofpz);
-
-    sprintf(ofn1, "%s%s", argv[3], "defX.raw");
-    ofpx = fopen(ofn1, "wb");
-    sprintf(ofn2, "%s%s", argv[3], "defY.raw");
-    ofpy = fopen(ofn2, "wb");
-    sprintf(ofn3, "%s%s", argv[3], "defZ.raw");
-    ofpz = fopen(ofn3, "wb");
-
-    // iterate through the deformation field pixel by pixel and write each x, y, z
-    // component to the corresponding binary files
-    typedef itk::ImageRegionConstIterator< DeformationFieldType > RegionIteratorType;
-
-    fprintf (stdout, "requested region size %ld %ld %ld\n",
-        vf->GetRequestedRegion().GetSize()[0],
-        vf->GetRequestedRegion().GetSize()[1],
-        vf->GetRequestedRegion().GetSize()[2]);
-
-    RegionIteratorType  vf_it(vf, vf->GetRequestedRegion() );
-    
-    vf_it.GoToBegin();
-    while(! vf_it.IsAtEnd() )
-    {
-        DeformationFieldType::PixelType v(vf_it.Get());        
-        fwrite(&v[0], 4, 1, ofpx);
-        fwrite(&v[1], 4, 1, ofpy);
-        fwrite(&v[2], 4, 1, ofpz);
-        ++ vf_it;
-    }
-
-    fclose(ofpx);
-    fclose(ofpy);
-    fclose(ofpz);
-
-    return 0;
-}
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plm_config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "itkImage.h"
+#include "itkImageRegionConstIterator.h"
+
+#define BUFLEN 1024
+
+char header_pat[] = 
+    "ObjectType = Image\n"
+    "NDims = 3\n"
+    "BinaryData = True\n"
+    "BinaryDataByteOrderMSB = False\n"
+	"TransformMatrix = 1 0 0 0 1 0 0 0 1\n"
+    "Offset = 0 0 0\n"
+	"CenterOfRotation = 0 0 0\n"
+    "ElementSpacing = %f %f %f\n"
+    "DimSize = %d %d %d\n"
+	"AnatomicalOrientation = RPI\n"
+	"ElementNumberOfChannels = 1\n"
+    "ElementType = MET_FLOAT\n"
+    "ElementDataFile = %s\n"
+    ;
+
+
+int main (int argc, char* argv[])
+{
+    // File pointers
+    FILE *ofpx, *ofpy, *ofpz;
+    Plm_image_header pih;
+    FloatImageType::Pointer img_fixed;
+    DeformationFieldType::Pointer vf;
+    // input xform from bspline
+    Xform xf_in, xf_out;
+
+    if (argc!=4)  {
+        std::cerr << "Wrong Parameters " << std::endl;
+        std::cerr << "Usage: " << argv[0];
+        std::cerr << " <input xform> ";
+        std::cerr << " <fixed image> ";
+        std::cerr << " <outputVectorFieldDir>" << std::endl;;
+        return 1;
+    }
+
+    xform_load (&xf_in, argv[1]);
+    img_fixed = itk_image_load_float (argv[2], 0);
+    xform_to_itk_vf (&xf_out, &xf_in, img_fixed);
+    vf = xf_out.get_itk_vf();
+
+    // need to write to 3 separate files, named as defX.mhd defY.mhd and defZ.mhd
+    char ofn1[255], ofn2[255], ofn3[255];
+
+    sprintf(ofn1, "%s%s", argv[3], "defX.mhd");
+    ofpx = fopen(ofn1, "w");
+    if (ofpx == NULL) 
+    {
+        fprintf(stdout, "open file %s failed\n", ofn1);
+        exit(-1);
+    }
+    sprintf(ofn2, "%s%s", argv[3], "defY.mhd");
+    ofpy = fopen(ofn2, "w");
+    if (ofpx == NULL) 
+    {
+        fprintf(stdout, "open file %s failed\n", ofn2);
+        exit(-1);
+    }
+
+    sprintf(ofn3, "%s%s", argv[3], "defZ.mhd");
+    ofpz = fopen(ofn3, "w");
+    if (ofpz == NULL) 
+    {
+        fprintf(stdout, "open file %s failed\n", ofn3);
+        exit(-1);
+    }
+
+    // print file header
+    pih.set_from_itk_image (img_fixed);
+
+    // write deformation field to separate binary files
+    fprintf(ofpx, header_pat, pih.m_spacing[0], pih.m_spacing[1], pih.m_spacing[2], 
+        pih.m_region.GetSize()[0], pih.m_region.GetSize()[1], pih.m_region.GetSize()[2], 
+        "defX.raw");
+    fclose(ofpx);
+    fprintf(ofpy, header_pat, pih.m_spacing[0], pih.m_spacing[1], pih.m_spacing[2], 
+        pih.m_region.GetSize()[0], pih.m_region.GetSize()[1], pih.m_region.GetSize()[2], 
+        "defY.raw");
+    fclose(ofpy);
+    fprintf(ofpz, header_pat, pih.m_spacing[0], pih.m_spacing[1], pih.m_spacing[2], 
+        pih.m_region.GetSize()[0], pih.m_region.GetSize()[1], pih.m_region.GetSize()[2], 
+        "defZ.raw");
+    fclose(ofpz);
+
+    sprintf(ofn1, "%s%s", argv[3], "defX.raw");
+    ofpx = fopen(ofn1, "wb");
+    sprintf(ofn2, "%s%s", argv[3], "defY.raw");
+    ofpy = fopen(ofn2, "wb");
+    sprintf(ofn3, "%s%s", argv[3], "defZ.raw");
+    ofpz = fopen(ofn3, "wb");
+
+    // iterate through the deformation field pixel by pixel and write each x, y, z
+    // component to the corresponding binary files
+    typedef itk::ImageRegionConstIterator< DeformationFieldType > RegionIteratorType;
+
+    fprintf (stdout, "requested region size %ld %ld %ld\n",
+        vf->GetRequestedRegion().GetSize()[0],
+        vf->GetRequestedRegion().GetSize()[1],
+        vf->GetRequestedRegion().GetSize()[2]);
+
+    RegionIteratorType  vf_it(vf, vf->GetRequestedRegion() );
+    
+    vf_it.GoToBegin();
+    while(! vf_it.IsAtEnd() )
+    {
+        DeformationFieldType::PixelType v(vf_it.Get());        
+        fwrite(&v[0], 4, 1, ofpx);
+        fwrite(&v[1], 4, 1, ofpy);
+        fwrite(&v[2], 4, 1, ofpz);
+        ++ vf_it;
+    }
+
+    fclose(ofpx);
+    fclose(ofpy);
+    fclose(ofpz);
+
+    return 0;
+}
diff --git a/src/plastimatch/standalone/xvi_archive.cxx b/src/plastimatch/standalone/xvi_archive.cxx
index 4ba318b..895c1bd 100644
--- a/src/plastimatch/standalone/xvi_archive.cxx
+++ b/src/plastimatch/standalone/xvi_archive.cxx
@@ -7,15 +7,16 @@
    ** This algorithm does not use dbase file, which reduces need
    to access xvi computer **
 
-   (1) Get patient ID (PATID) from command line argument
-   (2) Search through file system, identify CBCT images according to 
-       patient_<PATID>/IMAGES/img_<CBCT_UID>/Reconstruction/<RECON_UID>.SCAN
+   (1) Get input directory from command line
+   (2) Identify CBCT images according to 
+       <INDIR>/IMAGES/img_<CBCT_UID>/Reconstruction/<RECON_UID>.SCAN
    (3) Load Reconstruction/<RECON_UID>.INI
    (3a) Parse this file to get Name, TreatmentID, 
    (4) Load Reconstruction/<RECON_UID>.INI.XVI
    (4a) Parse this file to get Date, Xform
    (5) Identify reference image in Mosaiq (optional?)
    
+   ** Needs fix for multiple reference studies **
 
    ----------------------------------------------------------------------- */
 #include <stdlib.h>
@@ -33,8 +34,22 @@
 #include "string_util.h"
 #include "xvi_archive.h"
 
-#define DEFAULT_DATABASE_DIRECTORY ""
-#define DEFAULT_PATIENT_DIRECTORY ""
+Rt_study::Pointer
+load_reference_ct (
+    const std::string& patient_ct_set_dir,
+    const std::string& cbct_ref_uid)
+{
+    std::string reference_ct_dir = string_format ("%s/%s",
+        patient_ct_set_dir.c_str(), cbct_ref_uid.c_str());
+    if (is_directory (reference_ct_dir)) {
+        Rt_study::Pointer reference_study = Rt_study::New();
+        reference_study->load (reference_ct_dir);
+        return reference_study;
+    } else {
+        printf ("Error.  No matching reference CT found.\n");
+        return Rt_study::Pointer();
+    }
+}
 
 void
 do_xvi_archive (Xvi_archive_parms *parms)
@@ -44,37 +59,6 @@ do_xvi_archive (Xvi_archive_parms *parms)
     std::string patient_images_dir = compose_filename (
         parms->patient_dir, "IMAGES");
 
-    /* Load one of the reference CTs */
-    Dir_list ct_set_dir (patient_ct_set_dir);
-    if (ct_set_dir.num_entries == 0) {
-        printf ("Error.  No CT_SET found.\n");
-        return;
-    }
-    Rt_study reference_study;
-    std::string reference_uid;
-    for (int i = 0; i < ct_set_dir.num_entries; i++) {
-        if (ct_set_dir.entries[i][0] == '.') {
-            continue;
-        }
-        std::string fn = string_format ("%s/%s",
-            patient_ct_set_dir.c_str(), ct_set_dir.entries[i]);
-        if (is_directory (fn)) {
-            printf ("Loaded reference study (%s)\n", fn.c_str());
-            reference_study.load (fn);
-            reference_uid = ct_set_dir.entries[i];
-            break;
-        }
-    }
-    if (!reference_study.have_image()) {
-        printf ("Error.  No reference CT loaded.\n");
-        return;
-    }
-    Rt_study_metadata::Pointer& reference_meta = 
-        reference_study.get_rt_study_metadata ();
-    printf ("Reference Meta: %s %s\n",
-        reference_meta->get_patient_name().c_str(),
-        reference_meta->get_patient_id().c_str());
-
     Dir_list images_dir (patient_images_dir);
     if (images_dir.num_entries == 0) {
         printf ("Error.  No images found.\n");
@@ -112,6 +96,9 @@ do_xvi_archive (Xvi_archive_parms *parms)
             continue;
         }
 
+        printf ("cbct_dir = %s/%s\n", 
+            patient_images_dir.c_str(), images_dir.entries[i]);
+        
         /* Load the INI file */
         INIReader recon_ini (recon_ini_fn);
         std::string cbct_ref_uid = 
@@ -120,18 +107,70 @@ do_xvi_archive (Xvi_archive_parms *parms)
             string_format ("%s^%s",
                 recon_ini.Get ("IDENTIFICATION", "LastName", "").c_str(),
                 recon_ini.Get ("IDENTIFICATION", "FirstName", "").c_str());
-
+        std::string status_line_string = 
+            recon_ini.Get ("XVI", "StatusLineText", "");
+        std::string linac_string = "";
+        size_t n = status_line_string.find ("Plan Description:");
+        if (n != std::string::npos) {
+            linac_string = status_line_string.substr (
+                n + strlen ("Plan Description:"));
+            n = linac_string.find_first_not_of (" \t\r\n");
+            linac_string = linac_string.substr (n);
+            n = linac_string.find_first_of (" \t\r\n");
+            if (n != std::string::npos) {
+                linac_string = linac_string.substr (0, n);
+            }
+        }
         printf ("name = %s\n", patient_name.c_str());
         printf ("reference_uid = %s\n", cbct_ref_uid.c_str());
+        printf ("linac_string = %s\n", linac_string.c_str());
 
+#if defined (commentout)
         /* Verify if the file belongs to this reference CT */
         if (cbct_ref_uid != reference_uid) {
             printf ("Reference UID mismatch.  Skipping.\n");
             continue;
         }
+#endif
+
+        /* Load the matching reference CT */
+        Rt_study::Pointer reference_study = load_reference_ct (
+            patient_ct_set_dir, cbct_ref_uid);
+        if (!reference_study) {
+            printf ("No matching CT for this CBCT.  Skipping.\n");
+            continue;
+        }
+
+        /* Extract metadata from reference CT */
+        Rt_study_metadata::Pointer& reference_meta = 
+            reference_study->get_rt_study_metadata ();
+        printf ("Reference Meta: %s %s\n",
+            reference_meta->get_patient_name().c_str(),
+            reference_meta->get_patient_id().c_str());
 
         /* Load the INI.XVI file */
         INIReader recon_xvi (recon_xvi_fn);
+        std::string date_time_string = 
+            recon_xvi.Get ("ALIGNMENT", "DateTime", "");
+        std::string date_string, time_string;
+        size_t semicol_pos = date_time_string.find (";");
+        if (semicol_pos != std::string::npos) {
+            printf ("semicol_pos = %d\n", (int) semicol_pos);
+            date_string 
+                = string_trim (date_time_string.substr (0, semicol_pos));
+            printf ("date = |%s|\n", date_string.c_str());
+            time_string 
+                = string_trim (date_time_string.substr (semicol_pos+1));
+            while (1) {
+                size_t colon_pos = time_string.find (":");
+                if (colon_pos == std::string::npos) {
+                    break;
+                }
+                time_string = time_string.substr (0, colon_pos)
+                    + time_string.substr (colon_pos+1);
+            }
+            printf ("time = |%s|\n", time_string.c_str());
+        }
         std::string registration_string = 
             recon_xvi.Get ("ALIGNMENT", "OnlineToRefTransformCorrection", "");
         printf ("xform = %s\n", registration_string.c_str());
@@ -139,30 +178,83 @@ do_xvi_archive (Xvi_archive_parms *parms)
         /* Load the .SCAN */
         Rt_study cbct_study;
         cbct_study.load_image (scan_fn);
-
         if (!cbct_study.have_image()) {
             printf ("ERROR: decompression failure with patient %s\n",
                 reference_meta->get_patient_id().c_str());
             exit (1);
         }
 
-        /* Write the DICOM image */
-        std::string output_dir = string_format (
-            "cbct_output/%s", images_dir.entries[i]);
-
+        /* Set DICOM image header fields */
         Rt_study_metadata::Pointer& cbct_meta 
             = cbct_study.get_rt_study_metadata ();
+        cbct_meta->set_patient_name (patient_name);
         if (parms->patient_id_override != "") {
             cbct_meta->set_patient_id (parms->patient_id_override);
         } else {
             cbct_meta->set_patient_id (
                 reference_meta->get_patient_id().c_str());
         }
-        cbct_meta->set_patient_name (patient_name);
+        if (date_string != "" && time_string != "") {
+            cbct_meta->set_study_date (date_string);
+            cbct_meta->set_study_time (time_string);
+            cbct_meta->set_image_metadata(0x0008, 0x0012, date_string);
+            cbct_meta->set_image_metadata(0x0008, 0x0013, time_string);
+        }
+        std::string study_description = "CBCT: " + linac_string;
+        cbct_meta->set_study_metadata (0x0008, 0x1030, study_description);
+        cbct_meta->set_image_metadata (0x0028, 0x1050, "500");  // Window
+        cbct_meta->set_image_metadata (0x0028, 0x1051, "2000"); // Level
+        std::string patient_position
+            = reference_meta->get_image_metadata(0x0018, 0x5100);
+        cbct_meta->set_image_metadata (0x0018, 0x5100, patient_position);
+        printf ("Patient position is %s\n", patient_position.c_str());
+
+        /* Fix patient orientation based on reference CT */
+        float dc[9] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
+        if (patient_position == "HFS") {
+            /* Do nothing */
+            //continue;
+        }
+        else if (patient_position == "HFP") {
+            // dc = { -1, 0, 0, 0, -1, 0, 0, 0, 1 };
+            dc[0] = dc[4] = -1;
+            cbct_study.get_image()->get_volume()->set_direction_cosines (dc);
+            //continue;
+        }
+        else if (patient_position == "FFS") {
+            // dc = { -1, 0, 0, 0, 1, 0, 0, 0, -1 };
+            dc[0] = dc[8] = -1;
+            cbct_study.get_image()->get_volume()->set_direction_cosines (dc);
+            //continue;
+        }
+        else if (patient_position == "FFP") {
+            // dc = { 1, 0, 0, 0, -1, 0, 0, 0, -1 };
+            dc[4] = dc[8] = -1;
+            cbct_study.get_image()->get_volume()->set_direction_cosines (dc);
+            //continue;
+        }
+        else {
+            /* Punt */
+            patient_position = "HFS";
+        }
+        float origin[3];
+        cbct_study.get_image()->get_volume()->get_origin(origin);
+        origin[0] = dc[0] * origin[0];
+        origin[1] = dc[4] * origin[1];
+        origin[2] = dc[8] * origin[2];
+        cbct_study.get_image()->get_volume()->set_origin (origin);
+
+//        cbct_study.save_image ("cbct.mha");
+
+        /* Write the DICOM image */
+        std::string output_dir = string_format (
+            "cbct_output/%s/%s", 
+            reference_meta->get_patient_id().c_str(),
+            images_dir.entries[i]);
         cbct_study.save_dicom (output_dir);
 
         /* Create the DICOM SRO */
-        Xform::Pointer xf = Xform::New();
+        AffineTransformType::Pointer aff = AffineTransformType::New();
         AffineTransformType::ParametersType xfp(12);
         float xvip[16];
         int rc = sscanf (registration_string.c_str(), 
@@ -176,28 +268,161 @@ do_xvi_archive (Xvi_archive_parms *parms)
             printf ("Error parsing transform string.\n");
             exit (1);
         }
-        xfp[0] = 1;
-        xfp[1] = 0;
-        xfp[2] = 0;
-        xfp[3] = 0;
-        xfp[4] = 1;
-        xfp[5] = 0;
-        xfp[6] = 0;
-        xfp[7] = 0;
-        xfp[8] = 1;
-        xfp[9] = - xvip[12] * 10;
-        xfp[10] = - xvip[13] * 10;
-        xfp[11] = - xvip[14] * 10;
+
+        printf ("XVI\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", 
+            xvip[0], xvip[1], xvip[2], xvip[3], 
+            xvip[4], xvip[5], xvip[6], xvip[7], 
+            xvip[8], xvip[9], xvip[10], xvip[11], 
+            xvip[12], xvip[13], xvip[14], xvip[15]);
+        
+        if (patient_position == "HFS") {
+            xfp[0] =   xvip[8];
+            xfp[1] =   xvip[9];
+            xfp[2] =   xvip[10];
+            xfp[3] =   xvip[4];
+            xfp[4] =   xvip[5];
+            xfp[5] =   xvip[6];
+            xfp[6] = - xvip[0];
+            xfp[7] = - xvip[1];
+            xfp[8] = - xvip[2];
+
+            // B
+            xfp[9]  =   (xfp[0]*xvip[12] + xfp[3]*xvip[13] + xfp[6]*xvip[14]);
+            xfp[10] =   (xfp[1]*xvip[12] + xfp[4]*xvip[13] + xfp[7]*xvip[14]);
+            xfp[11] = - (xfp[2]*xvip[12] + xfp[5]*xvip[13] + xfp[8]*xvip[14]);
+
+            // "A", Verified
+            xfp[9]  = - (xfp[0]*xvip[12] + xfp[1]*xvip[13] + xfp[2]*xvip[14]);
+            xfp[10] = - (xfp[3]*xvip[12] + xfp[4]*xvip[13] + xfp[5]*xvip[14]);
+            xfp[11] = - (xfp[6]*xvip[12] + xfp[7]*xvip[13] + xfp[8]*xvip[14]);
+        
+        }
+        else if (patient_position == "HFP") {
+            xfp[0] =   xvip[8];
+            xfp[1] =   xvip[9];
+            xfp[2] = - xvip[10];
+            xfp[3] =   xvip[4];
+            xfp[4] =   xvip[5];
+            xfp[5] = - xvip[6];
+            xfp[6] =   xvip[0];
+            xfp[7] =   xvip[1];
+            xfp[8] = - xvip[2];
+
+            // "A", Unlikely
+            xfp[9]  =   (xfp[0]*xvip[12] + xfp[1]*xvip[13] + xfp[2]*xvip[14]);
+            xfp[10] = - (xfp[3]*xvip[12] + xfp[4]*xvip[13] + xfp[5]*xvip[14]);
+            xfp[11] = - (xfp[6]*xvip[12] + xfp[7]*xvip[13] + xfp[8]*xvip[14]);
+        
+            // "B", Possible
+            xfp[9]  =   (xfp[0]*xvip[12] + xfp[3]*xvip[13] + xfp[6]*xvip[14]);
+            xfp[10] =   (xfp[1]*xvip[12] + xfp[4]*xvip[13] + xfp[7]*xvip[14]);
+            xfp[11] = - (xfp[2]*xvip[12] + xfp[5]*xvip[13] + xfp[8]*xvip[14]);
+
+        }
+        else if (patient_position == "FFS") {
+            xfp[0] = - xvip[8];
+            xfp[1] = - xvip[9];
+            xfp[2] = - xvip[10];
+            xfp[3] =   xvip[4];
+            xfp[4] =   xvip[5];
+            xfp[5] =   xvip[6];
+            xfp[6] =   xvip[0];
+            xfp[7] =   xvip[1];
+            xfp[8] =   xvip[2];
+
+            // "B", Unlikely
+            xfp[9]  = - (xfp[0]*xvip[12] + xfp[3]*xvip[13] + xfp[6]*xvip[14]);
+            xfp[10] =   (xfp[1]*xvip[12] + xfp[4]*xvip[13] + xfp[7]*xvip[14]);
+            xfp[11] = - (xfp[2]*xvip[12] + xfp[5]*xvip[13] + xfp[8]*xvip[14]);
+
+            // "A", Possible
+            xfp[9]  =   (xfp[0]*xvip[12] + xfp[1]*xvip[13] + xfp[2]*xvip[14]);
+            xfp[10] = - (xfp[3]*xvip[12] + xfp[4]*xvip[13] + xfp[5]*xvip[14]);
+            xfp[11] = - (xfp[6]*xvip[12] + xfp[7]*xvip[13] + xfp[8]*xvip[14]);
+        
+        }
+        else if (patient_position == "FFP") {
+            xfp[0] = - xvip[8];
+            xfp[1] = - xvip[9];
+            xfp[2] =   xvip[10];
+            xfp[3] =   xvip[4];
+            xfp[4] =   xvip[5];
+            xfp[5] = - xvip[6];
+            xfp[6] = - xvip[0];
+            xfp[7] = - xvip[1];
+            xfp[8] =   xvip[2];
+
+            // A
+            xfp[9]  =   (xfp[0]*xvip[12] + xfp[1]*xvip[13] + xfp[2]*xvip[14]);
+            xfp[10] =   (xfp[3]*xvip[12] + xfp[4]*xvip[13] + xfp[5]*xvip[14]);
+            xfp[11] = - (xfp[6]*xvip[12] + xfp[7]*xvip[13] + xfp[8]*xvip[14]);
+        
+            // "B", Mostly Verified
+            xfp[9]  =   (xfp[0]*xvip[12] + xfp[3]*xvip[13] + xfp[6]*xvip[14]);
+            xfp[10] =   (xfp[1]*xvip[12] + xfp[4]*xvip[13] + xfp[7]*xvip[14]);
+            xfp[11] = - (xfp[2]*xvip[12] + xfp[5]*xvip[13] + xfp[8]*xvip[14]);
+        }
+
+        // Convert cm to mm
+        xfp[9]  *= 10;
+        xfp[10] *= 10;
+        xfp[11] *= 10;
+        
+#if defined (commentout)
+        aff->SetParametersByValue (xfp);
+        vnl_matrix_fixed< double, 3, 3 > xfp_rot_inv = 
+            aff->GetMatrix().GetInverse();
+        printf ("XFORM-R INV\n%f %f %f\n%f %f %f\n%f %f %f\n",
+            xfp_rot_inv[0][0],
+            xfp_rot_inv[0][1],
+            xfp_rot_inv[0][2],
+            xfp_rot_inv[1][0],
+            xfp_rot_inv[1][1],
+            xfp_rot_inv[1][2],
+            xfp_rot_inv[2][0],
+            xfp_rot_inv[2][1],
+            xfp_rot_inv[2][2]);
+#endif
+        
+        // dicom translation = - 10 * dicom_rotation * xvi translation
+        // Old, "perfect" HFS setting
+#if defined (commentout)
+        xfp[9]  = -10 * (xfp[0]*xvip[12] + xfp[1]*xvip[13] + xfp[2]*xvip[14]);
+        xfp[10] = -10 * (xfp[3]*xvip[12] + xfp[4]*xvip[13] + xfp[5]*xvip[14]);
+        xfp[11] = -10 * (xfp[6]*xvip[12] + xfp[7]*xvip[13] + xfp[8]*xvip[14]);
+
+        xfp[9]  = -10 * (xfp[0]*xvip[12] + xfp[3]*xvip[13] + xfp[6]*xvip[14]);
+        xfp[10] = -10 * (xfp[1]*xvip[12] + xfp[4]*xvip[13] + xfp[7]*xvip[14]);
+        xfp[11] = -10 * (xfp[2]*xvip[12] + xfp[5]*xvip[13] + xfp[8]*xvip[14]);
+#endif
+        
+        Xform::Pointer xf = Xform::New();
         xf->set_aff (xfp);
 
+        printf ("XFORM\n%f %f %f\n%f %f %f\n%f %f %f\n%f %f %f\n",
+            xfp[0],
+            xfp[1],
+            xfp[2],
+            xfp[3],
+            xfp[4],
+            xfp[5],
+            xfp[6],
+            xfp[7],
+            xfp[8],
+            xfp[9],
+            xfp[10],
+            xfp[11]
+        );
+
+//        reference_study->save_image ("ct.nrrd");
+//        xf->save ("xf.tfm");
+        
 #if defined (commentout)
-        std::string xf_file = string_format (
-            "%s/xf.tfm", output_dir.c_str());
-        xf->save (xf_file);
 #endif
 
         Dcmtk_sro::save (
-            xf, reference_study.get_rt_study_metadata (),
+            xf,
+            reference_study->get_rt_study_metadata (),
             cbct_study.get_rt_study_metadata (),
             output_dir, true);
 
@@ -226,8 +451,7 @@ parse_fn (
 
     /* Input files and directories */
     parser->add_long_option ("", "patient-directory", 
-        "base directory containing patient images", 1, 
-        DEFAULT_PATIENT_DIRECTORY);
+        "base directory containing patient images", 1, "");
 
     /* Other options */
     parser->add_long_option ("", "patient-id-override", 
@@ -252,7 +476,7 @@ parse_fn (
 
 
 int
-main(int argc, char *argv[])
+main (int argc, char *argv[])
 {
     Xvi_archive_parms parms;
 
diff --git a/src/plastimatch/standalone/yk_config.h b/src/plastimatch/standalone/yk_config.h
new file mode 100644
index 0000000..35f53dd
--- /dev/null
+++ b/src/plastimatch/standalone/yk_config.h
@@ -0,0 +1,149 @@
+#ifndef YK_CONFIG_H
+#define YK_CONFIG_H
+
+
+//#include "itkImage.h"
+//#include "itkImageFileReader.h"
+//#include "itkImageFileWriter.h"
+
+#include <QString>
+#include <QFileInfo>
+
+#define DEFAULT_LABEL_SIZE1 512
+#define DEFAULT_LABEL_SIZE2 256
+#define DEFAULT_LABEL_SIZE3 256
+
+#define MAX_LINE_LENGTH 1024
+
+
+#define GY2YKIMG_MAG 700.0 //if 1000: 70Gy --> 70000 --> overflow,  if 500: 70Gy --> 35000 cGy, if 700: 100Gy --> 70000 cGy ->OK
+#define GAMMA2YKIMG_MAG 1000.0 //2 --> 512: 0~1: 256, 1-2: 256
+#define GY2CGY 100.0;
+
+#define NON_NEG_SHIFT 0.0 //2 --> 512: 0~1: 256, 1-2: 256
+
+#define DEFAULT_LABEL_WIDTH 256
+#define DEFAULT_LABEL_HEIGHT 256
+
+struct VEC3D{
+    double x;
+    double y;
+    double z;
+};
+
+// 1mm spacing, unit = cGy
+class ProtonSetFileMGH{
+public:
+    VEC3D fDim;
+    VEC3D fOrigin;
+    VEC3D fSpacing;
+    QString strCTDir;
+    QString strPathCompDose;
+    QString strPathRefDose;
+};
+
+
+
+enum enPLANE{
+    PLANE_AXIAL = 0,
+    PLANE_FRONTAL,
+    PLANE_SAGITTAL,
+};
+
+
+enum enPROFILE_DIRECTON{
+    PRIFLE_HOR = 0,
+    PRIFLE_VER,
+};
+
+
+enum enViewArrange{
+    AXIAL_FRONTAL_SAGITTAL = 0,
+    FRONTAL_SAGITTAL_AXIAL,
+    SAGITTAL_AXIAL_FRONTAL,
+};
+
+enum enRegisterOption{
+    PLAST_RIGID = 0,
+    PLAST_GRADIENT,
+    PLAST_AFFINE,
+    PLAST_BSPLINE,
+};
+
+
+enum enUpdateDirection{
+    GUI2DATA = 0,
+    DATA2GUI,    
+};
+
+
+#define DEFAULT_NUM_COLUMN_MAIN 3
+#define DEFAULT_MAXNUM_MAIN 50
+#define DEFAULT_NUM_COLUMN_QUE 7
+#define DEFAULT_MAXNUM_QUE 500
+
+
+enum enStatus{
+    ST_NOT_STARTED = 0,
+    ST_PENDING,
+    ST_ERROR,
+    ST_DONE,
+};
+
+
+enum enPlmCommandInfo{
+    PLM_OUTPUT_DIR_PATH = 0,    
+    PLM_TEMP1,
+    PLM_TEMP2,
+};
+
+
+class CRegiQueString {
+public:
+    QString m_quePathFixed; //file name with extention
+    QString m_quePathMoving;
+    QString m_quePathCommand;
+    int m_iStatus; //0: not started, 1: pending, 2: done
+    double m_fProcessingTime; //sec
+    double m_fScore;
+
+public:
+    CRegiQueString(){ m_iStatus = ST_NOT_STARTED; m_fScore = 0.0; m_fProcessingTime = 0.0; }
+    ~CRegiQueString(){ ; }
+    QString GetStrFixed(){
+        QFileInfo fInfo(m_quePathFixed);
+        return fInfo.fileName();
+    }
+    QString GetStrMoving(){
+        QFileInfo fInfo(m_quePathMoving);
+        return fInfo.fileName();
+    }
+    QString GetStrCommand(){
+        QFileInfo fInfo(m_quePathCommand);
+        return fInfo.fileName();
+    }
+
+    QString GetStrStatus(){
+        if (m_iStatus == ST_NOT_STARTED)
+            return QString("Wait");
+        else if (m_iStatus == ST_PENDING)
+            return QString("Pending");
+        else if (m_iStatus == ST_DONE)
+            return QString("Done");
+        else return QString("");
+    }
+
+    QString GetStrScore(){
+        return QString::number(m_fScore, 'f', 2);
+    }
+
+    QString GetStrTime(){
+        return QString::number(m_fProcessingTime, 'f', 2);
+    }
+};
+
+
+#endif // YK_CONFIG_H
+
+
+
diff --git a/src/plastimatch/sys/CMakeLists.txt b/src/plastimatch/sys/CMakeLists.txt
index e89f2e6..e2dbbbe 100644
--- a/src/plastimatch/sys/CMakeLists.txt
+++ b/src/plastimatch/sys/CMakeLists.txt
@@ -18,6 +18,7 @@ set (PLMSYS_LIBRARY_SRC
   compiler_warnings.h
   delayload.c delayload.h
   dir_list.cxx dir_list.h
+  double_align8.h
   dlib_threads.cxx dlib_threads.h 
   file_util.cxx file_util.h
   logfile.cxx logfile.h
@@ -29,10 +30,13 @@ set (PLMSYS_LIBRARY_SRC
   plm_int.h
   plm_macros.h
   plm_math.h
+  plm_return_code.h
   plm_sleep.cxx plm_sleep.h 
-  plm_timer.cxx plm_timer.h
+  plm_timer.cxx plm_timer.h plm_timer_p.h
+  plm_va_copy.h
   plm_version.h 
   print_and_exit.cxx print_and_exit.h
+  smart_pointer.h
   string_util.cxx string_util.h
   )
 
@@ -76,4 +80,5 @@ plm_add_library (
   "${PLMSYS_LIBRARY_SRC}" 
   "${PLMSYS_LIBRARY_DEPENDENCIES}"
   ""
+  "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
   "${PLMSYS_LIBRARY_HEADERS}")
diff --git a/src/plastimatch/sys/file_util.cxx b/src/plastimatch/sys/file_util.cxx
index 37d347a..5952553 100644
--- a/src/plastimatch/sys/file_util.cxx
+++ b/src/plastimatch/sys/file_util.cxx
@@ -128,13 +128,13 @@ make_directory (const std::string& dirname)
 }
 
 void
-make_parent_directories (const char *dirname)
+make_parent_directories (const char *filename)
 {
     char *p, *tmp;
 
-    if (!dirname) return;
+    if (!filename) return;
 
-    tmp = strdup (dirname);
+    tmp = strdup (filename);
     p = tmp;
     while (*p) {
 	if (ISSLASH (*p) && p != tmp) {
@@ -148,9 +148,9 @@ make_parent_directories (const char *dirname)
 }
 
 void
-make_parent_directories (const std::string& dirname)
+make_parent_directories (const std::string& filename)
 {
-    make_parent_directories (dirname.c_str());
+    make_parent_directories (filename.c_str());
 }
 
 void
@@ -216,39 +216,3 @@ plm_chdir (char* s)
     return _chdir (s);
 #endif
 }
-
-/* cross platform directory list */
-int
-plm_get_dir_list (const char*** f_list)
-{
-#if (WIN32)
-    // Win32 Version goes here
-    return -1;
-#elif (UNIX)
-    DIR *dp;
-    struct dirent *ep;
-    char b[NAME_MAX];
-    int n;
-
-    if (!plm_getcwd (b, NAME_MAX)) {
-        return -1;
-    }
-
-    dp = opendir (b);
-    if (dp != NULL) {
-        n=0;
-        while ((ep=readdir(dp))) { n++; }
-        *f_list = (const char**)malloc (n*sizeof(char*));
-        rewinddir (dp);
-        n=0;
-        while ((ep=readdir(dp))) {
-            (*f_list)[n] = (char*)malloc (strlen(ep->d_name));
-            strcpy ((char*)(*f_list)[n++], ep->d_name);
-        }
-        (void) closedir (dp);
-        return n;
-    } else {
-        return -1;
-    }
-#endif
-}
diff --git a/src/plastimatch/sys/file_util.h b/src/plastimatch/sys/file_util.h
index ffe21e9..49f31f8 100644
--- a/src/plastimatch/sys/file_util.h
+++ b/src/plastimatch/sys/file_util.h
@@ -7,7 +7,7 @@
 #include "plmsys_config.h"
 #include <string>
 #include <stdio.h>
-#include "sys/plm_int.h"
+#include "plm_int.h"
 
 PLMSYS_API int file_exists (const char *filename);
 PLMSYS_API int file_exists (const std::string& filename);
@@ -19,13 +19,12 @@ PLMSYS_API void copy_file (const std::string& dst_fn,
     const std::string& src_fn);
 PLMSYS_API void make_directory (const char *dirname);
 PLMSYS_API void make_directory (const std::string& dirname);
-PLMSYS_API void make_parent_directories (const char *dirname);
-PLMSYS_API void make_parent_directories (const std::string& dirname);
+PLMSYS_API void make_parent_directories (const char *filename);
+PLMSYS_API void make_parent_directories (const std::string& filename);
 PLMSYS_API void make_directory_recursive (const std::string& dirname);
 PLMSYS_API FILE* make_tempfile (void);
 PLMSYS_API char* plm_getcwd (char* s, int len);
 PLMSYS_API int plm_chdir (char* s);
-PLMSYS_API int plm_get_dir_list (const char*** f_list);
 PLMSYS_API FILE* plm_fopen (const char *path, const char *mode);
 PLMSYS_API FILE* plm_fopen (const std::string& path, const char *mode);
 
diff --git a/src/plastimatch/sys/path_util.cxx b/src/plastimatch/sys/path_util.cxx
index 8f999a5..9889e54 100644
--- a/src/plastimatch/sys/path_util.cxx
+++ b/src/plastimatch/sys/path_util.cxx
@@ -43,6 +43,16 @@ strip_extension (const std::string& filename)
     return filename.substr(0, lastdot); 
 }
 
+std::string strip_extension_if (
+    const std::string& filename, const std::string& ext)
+{
+    if (extension_is (filename, ext.c_str())) {
+        return strip_extension (filename);
+    } else {
+        return filename;
+    }
+}
+
 void
 trim_trailing_slashes (char *pathname)
 {
diff --git a/src/plastimatch/sys/path_util.h b/src/plastimatch/sys/path_util.h
index 526496f..d08f8db 100644
--- a/src/plastimatch/sys/path_util.h
+++ b/src/plastimatch/sys/path_util.h
@@ -20,6 +20,8 @@ PLMSYS_API int extension_is (const char* fname, const char* ext);
 PLMSYS_API int extension_is (const std::string& fname, const char* ext);
 PLMSYS_API void strip_extension (char* filename);
 PLMSYS_API std::string strip_extension (const std::string& filename);
+PLMSYS_API std::string strip_extension_if (
+    const std::string& filename, const std::string& ext);
 PLMSYS_API void trim_trailing_slashes (char *pathname);
 PLMSYS_API std::string trim_trailing_slashes (const std::string& pathname);
 PLMSYS_API char* file_util_parent (const char *filename);
diff --git a/src/plastimatch/sys/plm_config.h.in b/src/plastimatch/sys/plm_config.h.in
index a58ca44..57ee42b 100644
--- a/src/plastimatch/sys/plm_config.h.in
+++ b/src/plastimatch/sys/plm_config.h.in
@@ -4,11 +4,6 @@
 #ifndef __plm_config_h__
 #define __plm_config_h__
 
-/* ITK 3.20 is missing this */
-#if defined __cplusplus
-#include <stddef.h>
-#endif
-
 /* Architecture attributes */
 #cmakedefine MACHINE_IS_32_BIT 1
 #cmakedefine MACHINE_IS_64_BIT 1
@@ -57,10 +52,13 @@
 
 /* User options */
 #cmakedefine PLM_BUILD_SHARED_LIBS 1
-#cmakedefine PLM_CONFIG_USE_PATCHED_ITK 1
-#cmakedefine PLM_CONFIG_USE_SS_IMAGE_VEC 1
-#cmakedefine PLM_CONFIG_ALT_DCOS 1
 #cmakedefine PLM_CONFIG_KEYHOLIZE 1
+#cmakedefine PLM_CONFIG_USE_PATCHED_ITK 1
+#cmakedefine PLM_CONFIG_VOL_LIST 1
+
+#cmakedefine PLM_CONFIG_LEGACY_BSPLINE_EXTEND 1
+#cmakedefine PLM_CONFIG_LEGACY_BSPLINE_XFORM_IO 1
+#cmakedefine PLM_CONFIG_LEGACY_MI_METRIC 1
 
 /* Needed for dynamic loading */
 #cmakedefine PLM_USE_GPU_PLUGINS 1
@@ -76,15 +74,6 @@
 /* ITK attributes */
 #cmakedefine ITK_FOUND 1
 #cmakedefine ITK_USE_SYSTEM_GDCM 1
-#if (ITK_FOUND && !PLM_CUDA_COMPILE)
-#include "itkConfigure.h"
-//#if !defined (ITK_LEGACY_REMOVE)
-//#define ITK_LEGACY_SILENT 1 /* Silence MSVC+ITK compiler warnings */
-//#endif
-#if (ITK_VERSION_MAJOR >= 3) && (ITK_VERSION_MINOR >= 10) && defined (ITK_USE_ORIENTED_IMAGE_DIRECTION)
-#define PLM_ITK_ORIENTED_IMAGES 1
-#endif
-#endif
 
 /* Make Microsoft compiler less whiny */
 #if _MSC_VER >= 1400
@@ -114,11 +103,9 @@
    for initialization of multi-dimensional array */
 #pragma GCC diagnostic ignored "-Wmissing-braces"
 #endif
-/* -Wunused-local-typedefs is emitted by older versions of ITK */
 #if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 8) && !defined(NVCC)
-# if (ITK_FOUND && (ITK_VERSION_MAJOR == 3))
+/* -Wunused-local-typedefs is emitted by older versions of ITK */
 # pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-# endif
 #endif
 
 /* Make clang compiler less whiny */
@@ -132,44 +119,37 @@
 #pragma clang diagnostic ignored "-Wtautological-compare"
 #endif
 
-#if _MSC_VER < 1700
+#if defined(_MSC_VER) && _MSC_VER < 1700
 #define inline __inline
 #endif
 
-#if defined(__BORLANDC__) || defined(_MSC_VER)
+#if defined(_MSC_VER) && _MSC_VER < 1900
 #define snprintf _snprintf
 #endif
 
-
-/* JAS 2012.04.24
- * Plastimatch 2.0 method for demangling symbols */
 #ifdef __cplusplus
 #define EXTERNC extern "C"
 #else
 #define EXTERNC
 #endif
 
-
-/* JAS 2011.02.07
- * GPU Plugins are not considered "shared libs"
- * otherwise Win32 build will break when not using "shared libs" */
 #if ((defined(_WIN32) || defined(WIN32)) && (defined (PLM_USE_GPU_PLUGINS)))
 #ifdef plmcuda_EXPORTS
- #define plmcuda_EXPORT(f, ...)                                     \
+#define plmcuda_EXPORT(f, ...)                                     \
     __declspec(dllexport)                                          \
     f (__VA_ARGS__); typedef f##_t(__VA_ARGS__); 
  #else
- #define plmcuda_EXPORT(f, ...)                                     \
+#define plmcuda_EXPORT(f, ...)                                     \
     __declspec(dllimport)                                          \
     f (__VA_ARGS__); typedef f##_t(__VA_ARGS__); 
 #endif
 
 #ifdef plmopencl_EXPORTS
- #define plmopencl_EXPORT(f, ...)                                   \
+#define plmopencl_EXPORT(f, ...)                                   \
     __declspec(dllexport)                                          \
     f (__VA_ARGS__); typedef f##_t(__VA_ARGS__); 
 #else
- #define plmopencl_EXPORT(f, ...)                                   \
+#define plmopencl_EXPORT(f, ...)                                   \
     __declspec(dllimport)                                          \
     f (__VA_ARGS__); typedef f##_t(__VA_ARGS__); 
 #endif
diff --git a/src/plastimatch/sys/plm_math.h b/src/plastimatch/sys/plm_math.h
index 2e03ad2..0245fdd 100644
--- a/src/plastimatch/sys/plm_math.h
+++ b/src/plastimatch/sys/plm_math.h
@@ -235,5 +235,11 @@ static inline int is_number (const double x)
     return 1;
 }
 
+template<class T> T 
+clamp (T value, T min_value, T max_value) {
+    if (value < min_value) return min_value;
+    if (value > max_value) return max_value;
+    return value;
+}
 
 #endif
diff --git a/src/plastimatch/sys/print_and_exit.cxx b/src/plastimatch/sys/print_and_exit.cxx
index 60097f1..8be5e38 100644
--- a/src/plastimatch/sys/print_and_exit.cxx
+++ b/src/plastimatch/sys/print_and_exit.cxx
@@ -22,7 +22,7 @@ print_and_exit (const char* prompt_fmt, ...)
         va_list argptr;
         va_start (argptr, prompt_fmt);
         std::string error_message = string_format_va (prompt_fmt, argptr);
-        lprintf ("%s\n", error_message.c_str());
+        lprintf ("%s", error_message.c_str());
         Plm_exception pe = Plm_exception (error_message);
         va_end (argptr);
         throw pe;
diff --git a/src/plastimatch/sys/string_util.cxx b/src/plastimatch/sys/string_util.cxx
index e9ae3e1..03d025c 100644
--- a/src/plastimatch/sys/string_util.cxx
+++ b/src/plastimatch/sys/string_util.cxx
@@ -17,6 +17,12 @@
 #include "string_util.h"
 
 bool
+string_starts_with (const std::string& s1, const char* s2)
+{
+    return string_starts_with (s1.c_str(), s2);
+}
+
+bool
 string_starts_with (const char* s1, const char* s2)
 {
     return strncmp (s1, s2, strlen(s2)) == 0;
@@ -355,13 +361,35 @@ bool string_value_true (const std::string& s)
     return t == "1" || t == "true" || t == "on" || t == "yes";
 }
 
+bool string_value_false (const char* s)
+{
+    return string_value_false (std::string(s));
+}
+
+bool string_value_false (const std::string& s)
+{
+    return !string_value_true (s);
+}
+
 // compiler does not recognize the std::to_string (c++11), so made our own
 template <typename T>
 std::string PLM_to_string(T value)
 {
     std::ostringstream os ;
-    os << value ;
-    return os.str() ;
+    os << value;
+    return os.str();
+}
+
+// fancy version for arrays
+template <typename T>
+std::string PLM_to_string(T* value, int n)
+{
+    std::ostringstream os ;
+    for (int i = 0; i < n; i++) {
+        if (i > 0) os << " ";
+        os << value[i];
+    }
+    return os.str();
 }
 
 /* http://stackoverflow.com/questions/236129/split-a-string-in-c
@@ -389,3 +417,25 @@ std::vector<std::string> string_split (
     string_split (s, delim, elems);
     return elems;
 }
+
+bool split_tag_val (
+    const std::string& s, 
+    std::string& tag,
+    std::string& val,
+    char delim)
+{
+    size_t loc = s.find (delim);
+    if (loc == std::string::npos) {
+        tag = "";
+        val = "";
+        return false;
+    }
+    tag = string_trim (s.substr (0, loc));
+    val = string_trim (s.substr (loc + 1));
+    return true;
+}
+
+/* Explicit instantiations */
+template PLMSYS_API std::string PLM_to_string(double value);
+template PLMSYS_API std::string PLM_to_string(int *value, int n);
+template PLMSYS_API std::string PLM_to_string(double *value, int n);
diff --git a/src/plastimatch/sys/string_util.h b/src/plastimatch/sys/string_util.h
index f3d82f0..18ed22d 100644
--- a/src/plastimatch/sys/string_util.h
+++ b/src/plastimatch/sys/string_util.h
@@ -10,6 +10,7 @@
 #include <vector>
 #include "plm_return_code.h"
 
+PLMSYS_API bool string_starts_with (const std::string& s1, const char* s2);
 PLMSYS_API bool string_starts_with (const char* s1, const char* s2);
 PLMSYS_API int plm_strcmp (const char* s1, const char* s2);
 PLMSYS_API std::string make_lowercase (const std::string& s);
@@ -41,10 +42,15 @@ PLMSYS_API size_t ci_find (const std::string& str1, const std::string& str2);
 
 PLMSYS_API bool string_value_true (const char* s);
 PLMSYS_API bool string_value_true (const std::string& s);
+PLMSYS_API bool string_value_false (const char* s);
+PLMSYS_API bool string_value_false (const std::string& s);
 
 template <typename T> PLMSYS_API std::string PLM_to_string(T value);
+template <typename T> PLMSYS_API std::string PLM_to_string(T *value, int n);
 
 PLMSYS_API std::vector<std::string>& string_split (const std::string &s, char delim, std::vector<std::string> &elems);
 PLMSYS_API std::vector<std::string> string_split (const std::string &s, char delim);
+PLMSYS_API bool split_tag_val (const std::string& s, 
+    std::string& tag, std::string& val, char delim = '=');
 
 #endif
diff --git a/src/plastimatch/test/itk_test.cxx b/src/plastimatch/test/itk_test.cxx
index 2140c60..82800c1 100755
--- a/src/plastimatch/test/itk_test.cxx
+++ b/src/plastimatch/test/itk_test.cxx
@@ -4,6 +4,7 @@
 #include "itkImageFileWriter.h"
 
 #include "file_util.h"
+#include "itk_metadata.h"
 #include "itk_image_save.h"
 #include "logfile.h"
 #include "path_util.h"
@@ -94,7 +95,13 @@ int main
 //    itk_image_save (image, "foo.mha");
 //    itk_image_save (tmp, "foo.mha");
 //    itk_image_save_float (tmp, "foo.mha");
-    my_itk_image_save (tmp, "foo.mha");
+//    my_itk_image_save (tmp, "foo.mha");
+
+    itk::MetaDataDictionary& dict = tmp->GetMetaDataDictionary();
+    itk_metadata_set (&dict, "Hello", "World");
+    itk_image_save (tmp, "img_with_meta.nrrd");
+    itk_image_save (tmp, "img_with_meta.mha");
+    itk_image_save (tmp, "img_with_meta.hd5");
 
     Plm_image::Pointer pli = Plm_image::New (image);
     Rt_study_metadata rsm;
diff --git a/src/plastimatch/test/itk_test_directions.cxx b/src/plastimatch/test/itk_test_directions.cxx
index 42d30de..2a14769 100755
--- a/src/plastimatch/test/itk_test_directions.cxx
+++ b/src/plastimatch/test/itk_test_directions.cxx
@@ -90,6 +90,7 @@ int main
         }
     }
 
+    pli->save_image ("itk_test_directions.mha");
     pli->save_image ("itk_test_directions.nrrd");
 
     Rt_study rt;
diff --git a/src/plastimatch/util/CMakeLists.txt b/src/plastimatch/util/CMakeLists.txt
index a197fa2..818b33d 100644
--- a/src/plastimatch/util/CMakeLists.txt
+++ b/src/plastimatch/util/CMakeLists.txt
@@ -21,11 +21,11 @@ set (PLMUTIL_LIBRARY_SRC
   distance_map.cxx
   diff.cxx
   dvh.cxx dvh.h 
-  float_pair_list.cxx float_pair_list.h
   gamma_dose_comparison.cxx gamma_dose_comparison.h
   geometry_chooser.cxx geometry_chooser.h 
   hausdorff_distance.cxx hausdorff_distance.h 
   image_boundary.cxx image_boundary.h
+  image_center.cxx image_center.h
   itk_adjust.cxx
   itk_distance_map.cxx
   itk_crop.cxx
@@ -82,6 +82,8 @@ if (OPENMP_FOUND)
   set (PLMUTIL_LIBRARY_LDFLAGS "${OPENMP_LDFLAGS}")
   set_source_files_properties (dice_statistics.cxx
     PROPERTIES COMPILE_FLAGS ${OPENMP_FLAGS})
+  set_source_files_properties (image_center.cxx
+    PROPERTIES COMPILE_FLAGS ${OPENMP_FLAGS})
   set_source_files_properties (vf_invert.cxx
     PROPERTIES COMPILE_FLAGS ${OPENMP_FLAGS})
 endif ()
@@ -94,4 +96,5 @@ plm_add_library (
   "${PLMUTIL_LIBRARY_SRC}" 
   "${PLMUTIL_LIBRARY_DEPENDENCIES}"
   "${PLMUTIL_LIBRARY_LDFLAGS}"
+  "${PLASTIMATCH_INCLUDE_DIRECTORIES}"
   "${PLMUTIL_LIBRARY_HEADERS}")
diff --git a/src/plastimatch/util/dice_statistics.cxx b/src/plastimatch/util/dice_statistics.cxx
index 7035a94..f2d234d 100644
--- a/src/plastimatch/util/dice_statistics.cxx
+++ b/src/plastimatch/util/dice_statistics.cxx
@@ -16,6 +16,7 @@
 #include "dice_statistics.h"
 #include "itk_image.h"
 #include "itk_image_load.h"
+#include "itk_image_save.h"
 #include "itk_resample.h"
 #include "logfile.h"
 #include "plm_image.h"
@@ -79,10 +80,14 @@ Dice_statistics::set_compare_image (
 void 
 Dice_statistics::run ()
 {
-    /* Resample warped onto geometry of reference */
+    /* Resample and/or expand images based on geometry of reference */
     if (!itk_image_header_compare (d_ptr->ref_image, d_ptr->cmp_image)) {
-        d_ptr->cmp_image = resample_image (d_ptr->cmp_image, 
-            Plm_image_header (d_ptr->ref_image), 0, 0);
+        Plm_image_header pih;
+        pih.set_geometry_to_contain (
+            Plm_image_header (d_ptr->cmp_image),
+            Plm_image_header (d_ptr->ref_image));
+        d_ptr->cmp_image = resample_image (d_ptr->cmp_image, pih, 0, 0);
+        d_ptr->ref_image = resample_image (d_ptr->ref_image, pih, 0, 0);
     }
 
     /* Initialize counters */
diff --git a/src/plastimatch/util/dvh.cxx b/src/plastimatch/util/dvh.cxx
index 678d606..9f18430 100644
--- a/src/plastimatch/util/dvh.cxx
+++ b/src/plastimatch/util/dvh.cxx
@@ -80,11 +80,7 @@ Dvh::run ()
     plm_long ss_dim[3];
 
     FloatImageType::Pointer dose_img = d_ptr->dose->itk_float ();
-#if (PLM_CONFIG_USE_SS_IMAGE_VEC)
     UCharVecImageType::Pointer ss_img = d_ptr->rtss->get_ss_img_uchar_vec ();
-#else
-    UInt32ImageType::Pointer ss_img = d_ptr->rtss->get_ss_img_uint32 ();
-#endif
 
     /* GCS HACK: This should go into rtss.cxx */
     Rtss *ss_list;
@@ -92,11 +88,7 @@ Dvh::run ()
         ss_list = d_ptr->rtss->get_structure_set_raw();
     } else {
         ss_list = new Rtss;
-#if (PLM_CONFIG_USE_SS_IMAGE_VEC)
         int num_structures = ss_img->GetVectorLength() * 8;
-#else
-        int num_structures = 32;
-#endif
         for (int i = 0; i < num_structures; i++) {
             ss_list->add_structure ("Unknown Structure", 
                 "255 255 0", i+1, i);
@@ -164,15 +156,9 @@ Dvh::run ()
     typedef itk::ImageRegionConstIterator < FloatImageType > 
         FloatIteratorType;
     FloatIteratorType it_d (dose_img, dose_img->GetRequestedRegion ());
-#if (PLM_CONFIG_USE_SS_IMAGE_VEC)
     typedef itk::ImageRegionConstIterator < UCharVecImageType > 
         UCharVecIteratorType;
     UCharVecIteratorType it_s (ss_img, ss_img->GetRequestedRegion ());
-#else
-    typedef itk::ImageRegionConstIterator < UInt32ImageType > 
-        UInt32IteratorType;
-    UInt32IteratorType it_s (ss_img, ss_img->GetRequestedRegion ());
-#endif
 
     /* Loop through dose & ss images */
     for (it_d.GoToBegin(), it_s.GoToBegin(); 
@@ -180,11 +166,7 @@ Dvh::run ()
          ++it_d, ++it_s)
     {
         float d = it_d.Get();
-#if (PLM_CONFIG_USE_SS_IMAGE_VEC)
 	itk::VariableLengthVector<unsigned char> s = it_s.Get();
-#else
-        uint32_t s = it_s.Get();
-#endif
 
         /* Convert from cGy to Gy */
         if (d_ptr->dose_units == DVH_UNITS_CGY) {
@@ -203,7 +185,6 @@ Dvh::run ()
             Rtss_roi *curr_structure = ss_list->slist[sno];
                     
             /* Is this pixel in the current structure? */
-#if (PLM_CONFIG_USE_SS_IMAGE_VEC)
             int curr_bit = curr_structure->bit;
             unsigned int uchar_no = curr_bit / 8;
             unsigned int bit_no = curr_bit % 8;
@@ -214,9 +195,6 @@ Dvh::run ()
                     curr_bit, ss_img->GetVectorLength() * 8);
             }
             bool in_struct = s[uchar_no] & bit_mask;
-#else            
-            uint32_t in_struct = s & (1 << curr_structure->bit);
-#endif
 
             /* If so, update histogram & structure size */
             if (in_struct) {
diff --git a/src/plastimatch/util/gamma_dose_comparison.cxx b/src/plastimatch/util/gamma_dose_comparison.cxx
index 8946162..c2941bc 100644
--- a/src/plastimatch/util/gamma_dose_comparison.cxx
+++ b/src/plastimatch/util/gamma_dose_comparison.cxx
@@ -33,9 +33,6 @@ class Gamma_dose_comparison_private {
 public:
     Gamma_dose_comparison_private ()
     {
-        img_in1 = 0;
-        img_in2 = 0;
-        img_mask = 0;
         labelmap_out = 0;
 
         have_gamma_image = false;
@@ -67,6 +64,8 @@ public:
         b_resample_nn = false;				
         b_interp_search = false;
 
+        b_ref_only_threshold = false;
+
         for (int i = 0; i < MAX_NUM_HISTOGRAM_BIN; i++)	{
             arr_gamma_histo[i] = 0;
         }
@@ -74,9 +73,9 @@ public:
         progress_callback = 0;
     }
 public:
-    Plm_image *img_in1; /*!< input dose image 1 for gamma analysis*/
-    Plm_image *img_in2; /*!< input dose image 2 for gamma analysis*/
-    Plm_image *img_mask; /*!< input mask image for gamma analysis*/
+    Plm_image::Pointer img_in1; /*!< input dose image 1 for gamma analysis*/
+    Plm_image::Pointer img_in2; /*!< input dose image 2 for gamma analysis*/
+    Plm_image::Pointer img_mask; /*!< input mask image for gamma analysis*/
     Plm_image *labelmap_out; /*!< output uchar type labelmap, voxel value = 1/0 for pass/fail */
 
     /* Gamma image is float type image, voxel value = calculated gamma value */
@@ -115,6 +114,7 @@ public:
     bool b_local_gamma;
     bool b_compute_full_region;
     float f_inherent_resample_mm;
+    bool b_ref_only_threshold;//this option is needed for a situation where a ref region is below threshold and comp region is above threshold.
 
     plm_long voxels_in_mask;
     plm_long voxels_in_image;
@@ -147,64 +147,61 @@ Gamma_dose_comparison::~Gamma_dose_comparison () {
 void 
 Gamma_dose_comparison::set_reference_image (const char* image_fn)
 {
-    d_ptr->img_in1 = new Plm_image (image_fn);
+    d_ptr->img_in1 = Plm_image::New (image_fn);
 }
 
 void 
-Gamma_dose_comparison::set_reference_image (Plm_image* image)
+Gamma_dose_comparison::set_reference_image (const FloatImageType::Pointer image)
 {
-    d_ptr->img_in1 = image;
+    d_ptr->img_in1 = Plm_image::New (image);
 }
 
 void 
-Gamma_dose_comparison::set_reference_image (
-    const FloatImageType::Pointer image)
+Gamma_dose_comparison::set_reference_image (const Plm_image::Pointer& image)
 {
-    d_ptr->img_in1 = new Plm_image (image);
+    d_ptr->img_in1 = image;
 }
 
 void 
 Gamma_dose_comparison::set_compare_image (const char* image_fn)
 {
-    d_ptr->img_in2 = new Plm_image (image_fn);
+    d_ptr->img_in2 = Plm_image::New (image_fn);
 }
 
 void 
-Gamma_dose_comparison::set_compare_image (Plm_image* image)
+Gamma_dose_comparison::set_compare_image (const Plm_image::Pointer& image)
 {
     d_ptr->img_in2 = image;
 }
 
 void 
-Gamma_dose_comparison::set_compare_image (
-    const FloatImageType::Pointer image)
+Gamma_dose_comparison::set_compare_image (const FloatImageType::Pointer image)
 {
-    d_ptr->img_in2 = new Plm_image (image);
+    d_ptr->img_in2 = Plm_image::New (image);
 }
 
 void 
 Gamma_dose_comparison::set_mask_image (const std::string& image_fn)
 {
-    d_ptr->img_mask = new Plm_image (image_fn);
+    d_ptr->img_mask = Plm_image::New (image_fn);
 }
 
 void 
 Gamma_dose_comparison::set_mask_image (const char* image_fn)
 {
-    d_ptr->img_mask = new Plm_image (image_fn);
+    d_ptr->img_mask = Plm_image::New (image_fn);
 }
 
 void 
-Gamma_dose_comparison::set_mask_image (Plm_image* image)
+Gamma_dose_comparison::set_mask_image (const Plm_image::Pointer& image)
 {
     d_ptr->img_mask = image;
 }
 
 void 
-Gamma_dose_comparison::set_mask_image (
-    const UCharImageType::Pointer image)
+Gamma_dose_comparison::set_mask_image (const UCharImageType::Pointer image)
 {
-    d_ptr->img_mask = new Plm_image (image);
+    d_ptr->img_mask = Plm_image::New (image);
 }
 
 float
@@ -361,9 +358,28 @@ Gamma_dose_comparison::get_pass_fraction ()
     return d_ptr->analysis_num_pass / (float) d_ptr->analysis_num_vox;
 }
 
+int
+Gamma_dose_comparison::get_analysis_num_vox()
+{    
+    return (int)d_ptr->analysis_num_vox;
+}
+
+int
+Gamma_dose_comparison::get_passed_num_vox()
+{
+    return (int)d_ptr->analysis_num_pass;
+}
+
+float Gamma_dose_comparison::get_reference_dose()
+{
+    return (float)d_ptr->reference_dose;
+}
+
+
 void 
 Gamma_dose_comparison::resample_image_to_reference (
-    Plm_image *image_reference, Plm_image *image_moving)
+    const Plm_image::Pointer& image_reference,
+    Plm_image::Pointer& image_moving)
 {
     Plm_image_header pih;
     pih.set_from_plm_image (image_reference);
@@ -380,7 +396,7 @@ Gamma_dose_comparison::resample_image_to_reference (
 
 void 
 Gamma_dose_comparison::resample_image_with_fixed_spacing (
-    Plm_image *input_img, float spacing[3])
+    Plm_image::Pointer& input_img, float spacing[3])
 {	
     Plm_image_header pih;
     pih.set_from_plm_image (input_img);	
@@ -466,7 +482,7 @@ Gamma_dose_comparison_private::do_gamma_analysis ()
     }
     rg.SetSize (sz);
     rg.SetIndex (st);
-    dc = pih.m_direction;
+    dc = pih.GetDirection();
 
     FloatImageType::Pointer gamma_img = FloatImageType::New();
     UCharImageType::Pointer gamma_labelmap = UCharImageType::New();
@@ -508,6 +524,7 @@ Gamma_dose_comparison_private::do_gamma_analysis ()
     //int reg_pixsize; 
     float level1, level2, level3, dr2, dd2, gg;
     float f0,f1,f2,f3;
+    float fixedlevel2;//corresponding position pixel value of level1 to apply threshold
 	
 
     ////interpolated voxel position (float) for interp-search
@@ -585,7 +602,7 @@ Gamma_dose_comparison_private::do_gamma_analysis ()
     float gamma_comp_r3[4]; //r3: sub2-region moving point		
     float gamma_comp_rn[4]; //rn: normal_vector point (will give the smallest gamma value in gamma space)
 
-    float ref_dose_general;
+    float ref_dose_general;//in local gamma option, this should be a local dose, not a reference dose.
 
     float sum_up = 0.0;
     float sum_down = 0.0;
@@ -619,22 +636,33 @@ Gamma_dose_comparison_private::do_gamma_analysis ()
             ref_dose_general = level1;
         else //global, default
             ref_dose_general = this->reference_dose;	//by default, this is coming from ref dose (max dose if not defined)
+                       
+        k1 = itk_1_iterator.GetIndex();
+        itk_1->TransformIndexToPhysicalPoint(k1, phys);
+        itk_2->TransformPhysicalPointToIndex(phys, k2);
 
+        
+        if (b_ref_only_threshold) //don't care the corresponding pixel value
+            fixedlevel2 = -1.0;            
+        else
+            fixedlevel2 = itk_2->GetPixel(k2);//just for comparison to corresponding value
+            
 
         //if this option is on, computation will be much faster because dose comparison will not be perforemd.
         if (!this->b_compute_full_region){
             if (this->have_analysis_thresh){
-                if (level1 < analysis_threshold_in_Gy){
+                //if (level1 < analysis_threshold_in_Gy){
+                if (level1 < analysis_threshold_in_Gy && fixedlevel2 < analysis_threshold_in_Gy){
                     gamma_img_iterator.Set (NoProcessGammaValue);//YK: is 0.0 Safe? how about -1.0?
                     ++gamma_img_iterator;
                     continue;//skip the rest of the loop. This point will not be counted in analysis
                 }			
             }
-        }
-		
-        k1=itk_1_iterator.GetIndex();
+        }		
+
+        /*k1=itk_1_iterator.GetIndex();
         itk_1->TransformIndexToPhysicalPoint( k1, phys );
-        itk_2->TransformPhysicalPointToIndex( phys, k2 );		
+        itk_2->TransformPhysicalPointToIndex( phys, k2 );		*/
 
         //k2 is the voxel index of the k1's physical (mm) position in img2
     
@@ -671,7 +699,8 @@ Gamma_dose_comparison_private::do_gamma_analysis ()
                 (k2[1]-k1[1])*(k2[1]-k1[1])*f1 +
                 (k2[2]-k1[2])*(k2[2]-k1[2])*f2 ;
             //dd2: (dose diff./D)^2						
-            dd2 = ((level1 - level2) / reference_dose) * ((level1 - level2) / reference_dose) * f3; // (if local gamma is on, {[(d1-d2)/d1]*100 (%) / tol_dose(%)}^2)            								
+            //dd2 = ((level1 - level2) / reference_dose) * ((level1 - level2) / reference_dose) * f3; // (if local gamma is on, {[(d1-d2)/d1]*100 (%) / tol_dose(%)}^2)            								
+            dd2 = ((level1 - level2) / ref_dose_general) * ((level1 - level2) / ref_dose_general) * f3; // (if local gamma is on, {[(d1-d2)/d1]*100 (%) / tol_dose(%)}^2)            								
 
             gg = dr2 + dd2;
             // in this subregion, only minimum value is take.
@@ -763,7 +792,10 @@ Gamma_dose_comparison_private::do_gamma_analysis ()
 	
         /* Get statistics */
         if (this->have_analysis_thresh) {
-            if (level1 > analysis_threshold_in_Gy) {
+            //if (level1 > analysis_threshold_in_Gy) {            
+            /* include this voxel */
+            /*fixedlevel2 = -1 if include hig dose Comp is off*/
+            if (!(level1 < analysis_threshold_in_Gy && fixedlevel2 < analysis_threshold_in_Gy)){
                 this->analysis_num_vox ++;
                 if (gamma <= 1) {
                     this->analysis_num_pass ++;
@@ -880,6 +912,10 @@ void Gamma_dose_comparison_private::compose_report()
     std::string item;
 
     memset(itemStr, 0, iStrSize);
+    sprintf(itemStr, "%s\t%3.2f\n", "pass_rate(%)", analysis_num_pass / (float)analysis_num_vox*100.0);
+    str_gamma_report = str_gamma_report + std::string(itemStr);
+
+    memset(itemStr, 0, iStrSize);
     sprintf(itemStr, "%s\t%d\n", "interp_search", this->b_interp_search);
     str_gamma_report = str_gamma_report + std::string(itemStr);
 
@@ -893,7 +929,7 @@ void Gamma_dose_comparison_private::compose_report()
 	
     memset(itemStr, 0, iStrSize);
     sprintf( itemStr, "%s\t%d\n","compute_full_region", this->b_compute_full_region);
-    str_gamma_report= str_gamma_report+std::string(itemStr);
+    str_gamma_report= str_gamma_report+std::string(itemStr);    
 
     memset(itemStr, 0, iStrSize);
     sprintf(itemStr, "%s\t%d\n", "resample-nn", this->b_resample_nn);
@@ -908,6 +944,10 @@ void Gamma_dose_comparison_private::compose_report()
     str_gamma_report= str_gamma_report+std::string(itemStr);
 
     memset(itemStr, 0, iStrSize);
+    sprintf(itemStr, "%s\t%d\n", "ref-only-threshold", this->b_ref_only_threshold);
+    str_gamma_report = str_gamma_report + std::string(itemStr);
+
+    memset(itemStr, 0, iStrSize);
     sprintf( itemStr, "%s\t%3.2f\n","reference_dose_Gy", this->reference_dose);
     str_gamma_report= str_gamma_report+std::string(itemStr);
 
@@ -933,20 +973,20 @@ void Gamma_dose_comparison_private::compose_report()
     sprintf( itemStr, "%s\t%d\n","number_of_total_voxels", (int) this->voxels_in_image);
     str_gamma_report= str_gamma_report+std::string(itemStr);
 
-    memset(itemStr, 0, iStrSize);	
-    sprintf( itemStr, "%s\t%d\n","number_of_analysis_voxels", (int) this->analysis_num_vox);
-    str_gamma_report= str_gamma_report+std::string(itemStr);
+    memset(itemStr, 0, iStrSize);
+    sprintf(itemStr, "%s\t%d\n", "number_of_analysis_voxels", (int) this->analysis_num_vox);
+    str_gamma_report = str_gamma_report + std::string(itemStr);
 
-    memset(itemStr, 0, iStrSize);	
-    sprintf( itemStr, "%s\t%d\n","number_of_pass_voxels", (int) this->analysis_num_pass);
-    str_gamma_report= str_gamma_report+std::string(itemStr);
+    memset(itemStr, 0, iStrSize);
+    sprintf(itemStr, "%s\t%d\n", "number_of_pass_voxels", (int) this->analysis_num_pass);
+    str_gamma_report = str_gamma_report + std::string(itemStr);
 
-    memset(itemStr, 0, iStrSize);	
-    sprintf( itemStr, "%s\t%3.2f\n","pass_rate(%)", analysis_num_pass/(float)analysis_num_vox*100.0);
-    str_gamma_report= str_gamma_report+std::string(itemStr);
+    memset(itemStr, 0, iStrSize);
+    sprintf(itemStr, "%s\t%3.2f\n", "pass_rate(%)", analysis_num_pass / (float)analysis_num_vox*100.0);
+    str_gamma_report = str_gamma_report + std::string(itemStr);
 
     memset(itemStr, 0, iStrSize);
-    sprintf(itemStr, "###################___BEGIN GAMMA HISTOGRAM___###################\n");
+    sprintf(itemStr, "[BEGIN GAMMA HISTOGRAM]\n");
     str_gamma_report = str_gamma_report + std::string(itemStr);
 
     //print out histogram
@@ -963,7 +1003,7 @@ void Gamma_dose_comparison_private::compose_report()
     str_gamma_report = str_gamma_report + std::string(itemStr);
 
     memset(itemStr, 0, iStrSize);
-    sprintf(itemStr, "###################___END GAMMA HISTOGRAM___###################\n");
+    sprintf(itemStr, "[END GAMMA HISTOGRAM]\n");
     str_gamma_report = str_gamma_report + std::string(itemStr);
 }
 
@@ -1034,3 +1074,24 @@ void Gamma_dose_comparison::set_interp_search(bool b_interp_search)
 {
     d_ptr->b_interp_search = b_interp_search;
 }
+
+Plm_image* Gamma_dose_comparison::get_ref_image()
+{
+    return d_ptr->img_in1.get();
+}
+
+Plm_image* Gamma_dose_comparison::get_comp_image()
+{
+    return d_ptr->img_in2.get();
+}
+
+bool Gamma_dose_comparison::is_ref_only_threshold()
+{
+    return d_ptr->b_ref_only_threshold;
+}
+
+void Gamma_dose_comparison::set_ref_only_threshold(bool b_ref_only_threshold)
+{
+    d_ptr->b_ref_only_threshold = b_ref_only_threshold;
+}
+
diff --git a/src/plastimatch/util/gamma_dose_comparison.h b/src/plastimatch/util/gamma_dose_comparison.h
index 21b76bc..d15d7d5 100644
--- a/src/plastimatch/util/gamma_dose_comparison.h
+++ b/src/plastimatch/util/gamma_dose_comparison.h
@@ -44,14 +44,14 @@ public:
       from the specified filename. */
     void set_reference_image (const char* image_fn);
     /*! \brief Set the reference image as a Plm image. */
-    void set_reference_image (Plm_image* image);
+    void set_reference_image (const Plm_image::Pointer& image);
     /*! \brief Set the reference image as an ITK image. */
     void set_reference_image (const FloatImageType::Pointer image);
     /*! \brief Set the compare image.  The image will be loaded
       from the specified filename. */
     void set_compare_image (const char* image_fn);
     /*! \brief Set the compare image as a Plm image. */
-    void set_compare_image (Plm_image* image);
+    void set_compare_image (const Plm_image::Pointer& image);
     /*! \brief Set the compare image as an ITK image. */
     void set_compare_image (const FloatImageType::Pointer image);
 
@@ -60,7 +60,7 @@ public:
     void set_mask_image (const char* image_fn);
     void set_mask_image (const std::string& image_fn);
     /*! \brief Set the mask image as a Plm image. */
-    void set_mask_image (Plm_image* image);
+    void set_mask_image (const Plm_image::Pointer& image);
     /*! \brief Set the mask image as an ITK image. */
     void set_mask_image (const UCharImageType::Pointer image);
 
@@ -128,16 +128,23 @@ public:
     Plm_image* get_fail_image ();
     /*! \brief Return a binary image of failing voxels as an ITK image. */
     UCharImageType::Pointer get_fail_image_itk ();
+
+    /*! \brief Return ref. image used for gamma evaluation */
+    Plm_image* get_ref_image();
+    /*! \brief Return comp. image used for gamma evaluation */
+    Plm_image* get_comp_image();
+
+
+
     /*! \brief Return fraction of passing points, subject to reference dose 
       being greater than analysis threshold */
     float get_pass_fraction ();
-    ///@}
-    /*! \brief Resample image_moving to image_reference */
-    void resample_image_to_reference (Plm_image *image_reference, Plm_image *image_moving);
 
-    /*! \brief Resample ref image with fixed spacing */
-    void resample_image_with_fixed_spacing (Plm_image *input_img, float spacing[3]);
+    int get_analysis_num_vox();
+    int get_passed_num_vox();
+    float get_reference_dose();
 
+    ///@}
     std::string get_report_string();	
     void set_report_string(std::string& report_str);
     bool is_local_gamma();
@@ -150,6 +157,21 @@ public:
     void set_resample_nn(bool b_resample_nn);
     bool is_interp_search();
     void set_interp_search(bool b_interp_search);
+
+    bool is_ref_only_threshold();
+    /*! \brief By default, when analysis is limited to areas with 
+     dose higher than a percent threshold, this threshold is based 
+     on the dose in either reference or compare image.  
+     If set_ref_only_threshold() is called with a value of true, 
+     the analysis will only consider dose higher than the threshold 
+     in the compare image */
+    void set_ref_only_threshold(bool b_ref_only_threshold);
+
+protected:
+    /*! \brief Resample ref image with fixed spacing */
+    void resample_image_with_fixed_spacing (Plm_image::Pointer& input_img, float spacing[3]);
+    /*! \brief Resample image_moving to image_reference */
+    void resample_image_to_reference (const Plm_image::Pointer& image_reference, Plm_image::Pointer& image_moving);
 };
 
 #endif
diff --git a/src/plastimatch/util/geometry_chooser.cxx b/src/plastimatch/util/geometry_chooser.cxx
index 45c1406..53b6875 100644
--- a/src/plastimatch/util/geometry_chooser.cxx
+++ b/src/plastimatch/util/geometry_chooser.cxx
@@ -223,8 +223,11 @@ template PLMUTIL_API void Geometry_chooser::set_fixed_image<UCharImageType::Poin
 template PLMUTIL_API void Geometry_chooser::set_fixed_image<FloatImageType::Pointer> (const FloatImageType::Pointer&);
 template PLMUTIL_API void Geometry_chooser::set_fixed_image<DeformationFieldType::Pointer> (const DeformationFieldType::Pointer&);
 template PLMUTIL_API void Geometry_chooser::set_reference_image<UCharImageType::Pointer> (const UCharImageType::Pointer&);
+template PLMUTIL_API void Geometry_chooser::set_reference_image<CharImageType::Pointer> (const CharImageType::Pointer&);
+template PLMUTIL_API void Geometry_chooser::set_reference_image<UShortImageType::Pointer> (const UShortImageType::Pointer&);
 template PLMUTIL_API void Geometry_chooser::set_reference_image<ShortImageType::Pointer> (const ShortImageType::Pointer&);
 template PLMUTIL_API void Geometry_chooser::set_reference_image<UInt32ImageType::Pointer> (const UInt32ImageType::Pointer&);
 template PLMUTIL_API void Geometry_chooser::set_reference_image<Int32ImageType::Pointer> (const Int32ImageType::Pointer&);
 template PLMUTIL_API void Geometry_chooser::set_reference_image<FloatImageType::Pointer> (const FloatImageType::Pointer&);
+template PLMUTIL_API void Geometry_chooser::set_reference_image<DoubleImageType::Pointer> (const DoubleImageType::Pointer&);
 template PLMUTIL_API void Geometry_chooser::set_reference_image<DeformationFieldType::Pointer> (const DeformationFieldType::Pointer&);
diff --git a/src/plastimatch/util/hausdorff_distance.cxx b/src/plastimatch/util/hausdorff_distance.cxx
index a842b1a..d399d13 100644
--- a/src/plastimatch/util/hausdorff_distance.cxx
+++ b/src/plastimatch/util/hausdorff_distance.cxx
@@ -237,10 +237,14 @@ Hausdorff_distance::run_internal (
 void 
 Hausdorff_distance::run ()
 {
-    /* Resample cmp image onto geometry of reference */
+    /* Resample and/or expand images based on geometry of reference */
     if (!itk_image_header_compare (d_ptr->ref_image, d_ptr->cmp_image)) {
-        d_ptr->cmp_image = resample_image (d_ptr->cmp_image, 
-            Plm_image_header (d_ptr->ref_image), 0, 0);
+        Plm_image_header pih;
+        pih.set_geometry_to_contain (
+            Plm_image_header (d_ptr->cmp_image),
+            Plm_image_header (d_ptr->ref_image));
+        d_ptr->cmp_image = resample_image (d_ptr->cmp_image, pih, 0, 0);
+        d_ptr->ref_image = resample_image (d_ptr->ref_image, pih, 0, 0);
     }
 
     d_ptr->clear_statistics ();
diff --git a/src/plastimatch/util/image_boundary.cxx b/src/plastimatch/util/image_boundary.cxx
index d5d6268..038b623 100755
--- a/src/plastimatch/util/image_boundary.cxx
+++ b/src/plastimatch/util/image_boundary.cxx
@@ -122,7 +122,7 @@ Image_boundary_private::run ()
     unsigned char *img_in = (unsigned char*) vol_in->img;
 
     /* Allocate output image */
-    Plm_image *pli_out = pli_in.clone ();
+    Plm_image::Pointer pli_out = pli_in.clone ();
     Volume::Pointer vol_out = pli_out->get_volume_uchar ();
     unsigned char *img_out = (unsigned char*) vol_out->img;
 
@@ -141,9 +141,6 @@ Image_boundary_private::run ()
 
     /* Save the output image */
     this->output_image = pli_out->itk_uchar ();
-
-    /* Clean up */
-    delete pli_out;
 }
 
 Image_boundary::Image_boundary ()
diff --git a/src/plastimatch/util/image_center.cxx b/src/plastimatch/util/image_center.cxx
new file mode 100755
index 0000000..bb47925
--- /dev/null
+++ b/src/plastimatch/util/image_center.cxx
@@ -0,0 +1,99 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#include "plm_config.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "itkImage.h"
+#include "itkImageFileReader.h"
+#include "itkImageMomentsCalculator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkImageSliceConstIteratorWithIndex.h"
+
+#include "compiler_warnings.h"
+#include "image_center.h"
+#include "itk_image.h"
+#include "itk_image_load.h"
+#include "itk_image_save.h"
+#include "itk_resample.h"
+#include "logfile.h"
+#include "plm_image.h"
+#include "plm_image_header.h"
+#include "volume.h"
+
+class Image_center_private {
+public:
+    DoubleVector3DType center_of_mass;
+    Plm_image::Pointer image;
+};
+
+Image_center::Image_center ()
+{
+    d_ptr = new Image_center_private;
+}
+
+Image_center::~Image_center ()
+{
+    delete d_ptr;
+}
+
+void 
+Image_center::set_image (
+    const UCharImageType::Pointer& image)
+{
+    d_ptr->image = Plm_image::New(image);
+}
+
+void 
+Image_center::set_image (
+    const Plm_image::Pointer& pli)
+{
+    d_ptr->image = pli;
+}
+
+void 
+Image_center::run ()
+{
+    /* Convert image to Volume type */
+    Volume::Pointer vol = d_ptr->image->get_volume_uchar ();
+    double x = 0, y = 0, z = 0;
+    size_t num_vox = 0;
+    unsigned char *img = vol->get_raw<unsigned char>();
+
+#pragma omp parallel for reduction(+:num_vox,x,y,z)
+    LOOP_Z_OMP (k, vol) {
+        plm_long ijk[3];      /* Index within image (vox) */
+        float xyz[3];         /* Position within image (mm) */
+        ijk[2] = k;
+        xyz[2] = vol->origin[2] + ijk[2] * vol->step[2*3+2];
+        LOOP_Y (ijk, xyz, vol) {
+            LOOP_X (ijk, xyz, vol) {
+                plm_long v = volume_index (vol->dim, ijk);
+                unsigned char vox_img = img[v];
+
+                if (vox_img) {
+                    num_vox++;
+                    x += xyz[0];
+                    y += xyz[1];
+                    z += xyz[2];
+                }
+            }
+        }
+    }
+
+    /* Compute volume and center of mass */
+    /* Voxel size is same for both images */
+    if (num_vox > 0) {
+        d_ptr->center_of_mass[0] = x / num_vox;
+        d_ptr->center_of_mass[1] = y / num_vox;
+        d_ptr->center_of_mass[2] = z / num_vox;
+    }
+}
+
+DoubleVector3DType 
+Image_center::get_image_center_of_mass ()
+{
+  return d_ptr->center_of_mass;
+}
diff --git a/src/plastimatch/util/image_center.h b/src/plastimatch/util/image_center.h
new file mode 100755
index 0000000..2bdabbc
--- /dev/null
+++ b/src/plastimatch/util/image_center.h
@@ -0,0 +1,45 @@
+/* -----------------------------------------------------------------------
+   See COPYRIGHT.TXT and LICENSE.TXT for copyright and license information
+   ----------------------------------------------------------------------- */
+#ifndef _image_center_h_
+#define _image_center_h_
+
+#include "plmutil_config.h"
+#include "itk_image_type.h"
+#include "plm_image.h"
+
+class Image_center_private;
+
+/*! \brief 
+ * The Image_center class computes the center of mass of a binary image.
+ */
+class PLMUTIL_API Image_center {
+public:
+    Image_center ();
+    ~Image_center ();
+public:
+    Image_center_private *d_ptr;
+public:
+
+    /*! \name Inputs */
+    ///@{
+    /*! \brief Set the reference image as a Plm image. */
+    void set_image (const UCharImageType::Pointer& image);
+    /*! \brief Set the reference image as an ITK image. */
+    void set_image (const Plm_image::Pointer& image);
+    ///@}
+
+    /*! \name Execution */
+    ///@{
+    /*! \brief Compute dice statistics */
+    void run ();
+    ///@}
+
+    /*! \name Outputs */
+    ///@{
+    /*! \brief Return the center of mass */
+    DoubleVector3DType get_image_center_of_mass ();
+    ///@}
+};
+
+#endif
diff --git a/src/plastimatch/util/itk_adjust.cxx b/src/plastimatch/util/itk_adjust.cxx
index 11567dc..df92b01 100755
--- a/src/plastimatch/util/itk_adjust.cxx
+++ b/src/plastimatch/util/itk_adjust.cxx
@@ -10,6 +10,7 @@
 #include "itk_image_clone.h"
 #include "plm_math.h"
 #include "print_and_exit.h"
+#include "pwlut.h"
 
 FloatImageType::Pointer
 itk_adjust (FloatImageType::Pointer image_in, const Float_pair_list& al)
@@ -20,6 +21,15 @@ itk_adjust (FloatImageType::Pointer image_in, const Float_pair_list& al)
     FloatImageType::RegionType rg = image_out->GetLargestPossibleRegion ();
     FloatIteratorType it (image_out, rg);
 
+    Pwlut pwlut;
+    pwlut.set_lut (al);
+
+    for (it.GoToBegin(); !it.IsAtEnd(); ++it) {
+        it.Set (pwlut.lookup (it.Get()));
+    }
+    return image_out;
+
+#if defined (commentout)
     /* Special processing for end caps */
     float left_slope = 1.0;
     float right_slope = 1.0;
@@ -90,6 +100,7 @@ itk_adjust (FloatImageType::Pointer image_in, const Float_pair_list& al)
         it.Set (vout);
     }
     return image_out;
+#endif
 }
 
 FloatImageType::Pointer
diff --git a/src/plastimatch/util/itk_mask.cxx b/src/plastimatch/util/itk_mask.cxx
index b2831cb..8c57a4d 100644
--- a/src/plastimatch/util/itk_mask.cxx
+++ b/src/plastimatch/util/itk_mask.cxx
@@ -27,11 +27,13 @@ mask_image (
 	= mask->GetLargestPossibleRegion();
     const typename ImageType::PointType& og = input->GetOrigin();
     const typename ImageType::SpacingType& sp = input->GetSpacing();
+    const typename ImageType::DirectionType& dc = input->GetDirection();
     
     typename ImageType::Pointer im_out = ImageType::New();
     im_out->SetRegions (rgn_input);
     im_out->SetOrigin (og);
     im_out->SetSpacing (sp);
+    im_out->SetDirection (dc);
     im_out->Allocate ();
 
     ImageIteratorType it_in (input, rgn_input);
diff --git a/src/plastimatch/util/rt_study_warp.cxx b/src/plastimatch/util/rt_study_warp.cxx
index 784e634..dc109c8 100644
--- a/src/plastimatch/util/rt_study_warp.cxx
+++ b/src/plastimatch/util/rt_study_warp.cxx
@@ -228,13 +228,16 @@ rt_study_warp (Rt_study *rt_study, Plm_file_format file_type, Warp_parms *parms)
 
     /* Set user-supplied metadata also prior to loading files,
        because the user supplied patient position is needed to load XiO data */
-    rt_study->set_study_metadata (parms->m_metadata);
+    rt_study->set_study_metadata (parms->m_study_metadata);
 
     /* Load input file(s) */
     load_input_files (rt_study, file_type, parms);
 
     /* Set user-supplied metadata (overrides metadata in input files) */
-    rt_study->set_study_metadata (parms->m_metadata);
+    rt_study->set_study_metadata (parms->m_study_metadata);
+    rt_study->set_image_metadata (parms->m_image_metadata);
+    rt_study->set_dose_metadata (parms->m_dose_metadata);
+    rt_study->set_rtss_metadata (parms->m_rtss_metadata);
 
     /* Load transform */
     if (parms->xf_in_fn != "") {
@@ -315,9 +318,27 @@ rt_study_warp (Rt_study *rt_study, Plm_file_format file_type, Warp_parms *parms)
     lprintf ("PIH is:\n");
     pih.print ();
 
+    /* Check if output image geometry matches input image geometry.
+       If it doesn't we need to resample to get the output geometry. 
+       We need to supply an identity xform if none was supplied, 
+       so that the warp function can do the resample. */
+    bool pih_changed = false;
+    if (rt_study->get_image()) {
+        Plm_image_header pih_input_image (rt_study->get_image());
+        if (!Plm_image_header::compare (&pih_input_image, &pih)) {
+            pih_changed = true;
+            if (parms->xf_in_fn == "") {
+                TranslationTransformType::Pointer trn
+                    = TranslationTransformType::New();
+                xform->set_trn(trn);
+            }
+        }
+    }
+
     /* Warp the image and create vf */
     if (rt_study->have_image()
-        && parms->xf_in_fn != ""
+        && (parms->xf_in_fn != ""
+            || pih_changed)
         && (parms->output_img_fn != ""
             || parms->output_vf_fn != ""
             || parms->output_dicom != ""))
@@ -377,7 +398,7 @@ rt_study_warp (Rt_study *rt_study, Plm_file_format file_type, Warp_parms *parms)
             parms->output_xio_dirname.c_str(), "dose");
         xio_dose_save (
             rt_study->get_dose(),
-            rt_study->get_metadata(), 
+            rt_study->get_study_metadata(), 
             rt_study->get_xio_ct_transform(),
             fn.c_str(), 
             rt_study->get_xio_dose_filename().c_str());
diff --git a/src/plastimatch/util/synthetic_mha.cxx b/src/plastimatch/util/synthetic_mha.cxx
index 4744ca0..1c900a3 100644
--- a/src/plastimatch/util/synthetic_mha.cxx
+++ b/src/plastimatch/util/synthetic_mha.cxx
@@ -484,7 +484,7 @@ synth_noise (
     
     /* Set intensity */
     *label = 0;
-    *intens = parms->noise_mean + (float) r;
+    *intens = parms->noise_mean + (float) r * parms->noise_std;
 }
 
 
@@ -579,10 +579,10 @@ synthetic_mha (
             Plm_image pi (parms->fixed_fn);
             Plm_image_header pih;
             pih.set_from_plm_image (&pi);
-            og = pih.m_origin;
-            sp = pih.m_spacing;
-            rg = pih.m_region;
-            itk_dc = pih.m_direction;
+            og = pih.GetOrigin();
+            sp = pih.GetSpacing();
+            rg = pih.GetRegion();
+            itk_dc = pih.GetDirection();
             for (int d1 = 0; d1 < 3; d1++) {
                 parms->origin[d1] = og[d1];
             }
diff --git a/src/plastimatch/util/warp_parms.h b/src/plastimatch/util/warp_parms.h
index 36aef1a..4456069 100644
--- a/src/plastimatch/util/warp_parms.h
+++ b/src/plastimatch/util/warp_parms.h
@@ -26,9 +26,6 @@ public:
     std::string input_dose_ast_fn;
     std::string input_dose_mc_fn;
     std::string fixed_img_fn;
-
-    /* Dij input files */
-    std::string ctatts_in_fn;
     std::string dif_in_fn;
 
     /* Output files */
@@ -52,6 +49,7 @@ public:
     std::string prefix_format;
     bool dicom_with_uids;
     Xio_version output_xio_version;
+    bool output_dij_dose_volumes;
 
     /* Algorithm options */
     float default_val;
@@ -74,7 +72,10 @@ public:
     Direction_cosines m_dc;
 
     /* Metadata options */
-    std::vector<std::string> m_metadata;
+    std::vector<std::string> m_study_metadata;
+    std::vector<std::string> m_image_metadata;
+    std::vector<std::string> m_dose_metadata;
+    std::vector<std::string> m_rtss_metadata;
 
 public:
     Warp_parms () {
@@ -94,6 +95,7 @@ public:
 	prefix_format = "mha";
         dicom_with_uids = true;
 	output_xio_version = XIO_VERSION_4_2_1;
+        output_dij_dose_volumes = false;
 
 	prune_empty = 0;
 	use_itk = 0;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/plastimatch.git



More information about the debian-med-commit mailing list