diff --git a/CMakeLists.txt b/CMakeLists.txt
index 28a46c26ae414bed55bcb7b67231df3f46b2bf10..7046a712b20c3d814b0223aeb0ec0e848b560e6f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,25 +1,474 @@
-cmake_minimum_required(VERSION 3.0.2)
+##################################################    Project     ##################################################
+cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
+cmake_policy          (SET CMP0023 OLD)
+project               (pli_vis VERSION 1.0 LANGUAGES C CXX)
+list                  (APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
+set_property          (GLOBAL PROPERTY USE_FOLDERS ON)
+set                   (CMAKE_CXX_VISIBILITY_PRESET hidden)
+set                   (CMAKE_VISIBILITY_INLINES_HIDDEN 1)
+set                   (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++11 -fopenmp -lgomp")
+set                   (CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} "--expt-extended-lambda"
+                                                          "-lcublas"
+                                                          "-lcublas_device"
+                                                          "-lcudadevrt"
+                                                         #"-gencode=arch=compute_50,code=sm_50")
+                                                          "-gencode=arch=compute_60,code=sm_60")
 
-# Setup project metadata.
-set(ProjectName "pli_vis")
-project(${ProjectName} VERSION 1.0.0 LANGUAGES C CXX)
-set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++11 -fopenmp -lgomp")
-set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} "--expt-extended-lambda" "-gencode=arch=compute_35,code=sm_35" "-gencode=arch=compute_50,code=sm_50" "-gencode=arch=compute_61,code=sm_61")
+##################################################    Options     ##################################################
+option(BUILD_TESTS "Build tests." OFF)
 
-# Setup dependencies.
-include("cmake/_dependencies.cmake")
+##################################################    Sources     ##################################################
+set(PROJECT_SOURCES
+  CMakeLists.txt
+  cmake/assign_source_group.cmake
+  cmake/import_library.cmake
+  
+  include/pli_vis/aspects/loggable.hpp
+  include/pli_vis/aspects/renderable.hpp
+  include/pli_vis/cuda/pt/cartesian_locator.h
+  include/pli_vis/cuda/pt/runge_kutta_4_integrator.h
+  include/pli_vis/cuda/pt/tracer.h
+  include/pli_vis/cuda/pt/trilinear_interpolator.h
+  include/pli_vis/cuda/sh/choose.h
+  include/pli_vis/cuda/sh/clebsch_gordan.h
+  include/pli_vis/cuda/sh/factorial.h
+  include/pli_vis/cuda/sh/launch.h
+  include/pli_vis/cuda/sh/legendre.h
+  include/pli_vis/cuda/sh/spherical_harmonics.h
+  include/pli_vis/cuda/sh/wigner.h
+  include/pli_vis/cuda/utility/convert.h
+  include/pli_vis/cuda/utility/vector_ops.h
+  include/pli_vis/cuda/zernike/disk.h
+  include/pli_vis/cuda/zernike/launch.h
+  include/pli_vis/cuda/zernike/zernike.h
+  include/pli_vis/cuda/global_tractography.h
+  include/pli_vis/cuda/odf_field.h
+  include/pli_vis/cuda/polar_plot.h
+  include/pli_vis/cuda/spherical_histogram.h
+  include/pli_vis/io/io.hpp
+  include/pli_vis/io/io_slice_impl.hpp
+  include/pli_vis/io/io_volume_impl.hpp
+  include/pli_vis/opengl/auxiliary/glm_uniforms.hpp
+  include/pli_vis/opengl/all.hpp
+  include/pli_vis/opengl/buffer.hpp
+  include/pli_vis/opengl/framebuffer.hpp
+  include/pli_vis/opengl/opengl.hpp
+  include/pli_vis/opengl/program.hpp
+  include/pli_vis/opengl/shader.hpp
+  include/pli_vis/opengl/texture.hpp
+  include/pli_vis/opengl/vertex_array.hpp
+  include/pli_vis/third_party/glew/GL/glew.h
+  include/pli_vis/third_party/qwt/qwt.h
+  include/pli_vis/third_party/qwt/qwt_abstract_legend.h
+  include/pli_vis/third_party/qwt/qwt_abstract_scale.h
+  include/pli_vis/third_party/qwt/qwt_abstract_scale_draw.h
+  include/pli_vis/third_party/qwt/qwt_abstract_slider.h
+  include/pli_vis/third_party/qwt/qwt_analog_clock.h
+  include/pli_vis/third_party/qwt/qwt_arrow_button.h
+  include/pli_vis/third_party/qwt/qwt_clipper.h
+  include/pli_vis/third_party/qwt/qwt_color_map.h
+  include/pli_vis/third_party/qwt/qwt_column_symbol.h
+  include/pli_vis/third_party/qwt/qwt_compass.h
+  include/pli_vis/third_party/qwt/qwt_compass_rose.h
+  include/pli_vis/third_party/qwt/qwt_compat.h
+  include/pli_vis/third_party/qwt/qwt_counter.h
+  include/pli_vis/third_party/qwt/qwt_curve_fitter.h
+  include/pli_vis/third_party/qwt/qwt_date.h
+  include/pli_vis/third_party/qwt/qwt_date_scale_draw.h
+  include/pli_vis/third_party/qwt/qwt_date_scale_engine.h
+  include/pli_vis/third_party/qwt/qwt_dial.h
+  include/pli_vis/third_party/qwt/qwt_dial_needle.h
+  include/pli_vis/third_party/qwt/qwt_dyngrid_layout.h
+  include/pli_vis/third_party/qwt/qwt_event_pattern.h
+  include/pli_vis/third_party/qwt/qwt_global.h
+  include/pli_vis/third_party/qwt/qwt_graphic.h
+  include/pli_vis/third_party/qwt/qwt_interval.h
+  include/pli_vis/third_party/qwt/qwt_interval_symbol.h
+  include/pli_vis/third_party/qwt/qwt_knob.h
+  include/pli_vis/third_party/qwt/qwt_legend.h
+  include/pli_vis/third_party/qwt/qwt_legend_data.h
+  include/pli_vis/third_party/qwt/qwt_legend_label.h
+  include/pli_vis/third_party/qwt/qwt_magnifier.h
+  include/pli_vis/third_party/qwt/qwt_math.h
+  include/pli_vis/third_party/qwt/qwt_matrix_raster_data.h
+  include/pli_vis/third_party/qwt/qwt_null_paintdevice.h
+  include/pli_vis/third_party/qwt/qwt_painter.h
+  include/pli_vis/third_party/qwt/qwt_painter_command.h
+  include/pli_vis/third_party/qwt/qwt_panner.h
+  include/pli_vis/third_party/qwt/qwt_picker.h
+  include/pli_vis/third_party/qwt/qwt_picker_machine.h
+  include/pli_vis/third_party/qwt/qwt_pixel_matrix.h
+  include/pli_vis/third_party/qwt/qwt_plot.h
+  include/pli_vis/third_party/qwt/qwt_plot_abstract_barchart.h
+  include/pli_vis/third_party/qwt/qwt_plot_barchart.h
+  include/pli_vis/third_party/qwt/qwt_plot_canvas.h
+  include/pli_vis/third_party/qwt/qwt_plot_curve.h
+  include/pli_vis/third_party/qwt/qwt_plot_dict.h
+  include/pli_vis/third_party/qwt/qwt_plot_directpainter.h
+  include/pli_vis/third_party/qwt/qwt_plot_glcanvas.h
+  include/pli_vis/third_party/qwt/qwt_plot_grid.h
+  include/pli_vis/third_party/qwt/qwt_plot_histogram.h
+  include/pli_vis/third_party/qwt/qwt_plot_intervalcurve.h
+  include/pli_vis/third_party/qwt/qwt_plot_item.h
+  include/pli_vis/third_party/qwt/qwt_plot_layout.h
+  include/pli_vis/third_party/qwt/qwt_plot_legenditem.h
+  include/pli_vis/third_party/qwt/qwt_plot_magnifier.h
+  include/pli_vis/third_party/qwt/qwt_plot_marker.h
+  include/pli_vis/third_party/qwt/qwt_plot_multi_barchart.h
+  include/pli_vis/third_party/qwt/qwt_plot_panner.h
+  include/pli_vis/third_party/qwt/qwt_plot_picker.h
+  include/pli_vis/third_party/qwt/qwt_plot_rasteritem.h
+  include/pli_vis/third_party/qwt/qwt_plot_renderer.h
+  include/pli_vis/third_party/qwt/qwt_plot_rescaler.h
+  include/pli_vis/third_party/qwt/qwt_plot_scaleitem.h
+  include/pli_vis/third_party/qwt/qwt_plot_seriesitem.h
+  include/pli_vis/third_party/qwt/qwt_plot_shapeitem.h
+  include/pli_vis/third_party/qwt/qwt_plot_spectrocurve.h
+  include/pli_vis/third_party/qwt/qwt_plot_spectrogram.h
+  include/pli_vis/third_party/qwt/qwt_plot_svgitem.h
+  include/pli_vis/third_party/qwt/qwt_plot_textlabel.h
+  include/pli_vis/third_party/qwt/qwt_plot_tradingcurve.h
+  include/pli_vis/third_party/qwt/qwt_plot_zoneitem.h
+  include/pli_vis/third_party/qwt/qwt_plot_zoomer.h
+  include/pli_vis/third_party/qwt/qwt_point_3d.h
+  include/pli_vis/third_party/qwt/qwt_point_data.h
+  include/pli_vis/third_party/qwt/qwt_point_mapper.h
+  include/pli_vis/third_party/qwt/qwt_point_polar.h
+  include/pli_vis/third_party/qwt/qwt_raster_data.h
+  include/pli_vis/third_party/qwt/qwt_round_scale_draw.h
+  include/pli_vis/third_party/qwt/qwt_samples.h
+  include/pli_vis/third_party/qwt/qwt_sampling_thread.h
+  include/pli_vis/third_party/qwt/qwt_scale_div.h
+  include/pli_vis/third_party/qwt/qwt_scale_draw.h
+  include/pli_vis/third_party/qwt/qwt_scale_engine.h
+  include/pli_vis/third_party/qwt/qwt_scale_map.h
+  include/pli_vis/third_party/qwt/qwt_scale_widget.h
+  include/pli_vis/third_party/qwt/qwt_series_data.h
+  include/pli_vis/third_party/qwt/qwt_series_store.h
+  include/pli_vis/third_party/qwt/qwt_slider.h
+  include/pli_vis/third_party/qwt/qwt_spline.h
+  include/pli_vis/third_party/qwt/qwt_symbol.h
+  include/pli_vis/third_party/qwt/qwt_system_clock.h
+  include/pli_vis/third_party/qwt/qwt_text.h
+  include/pli_vis/third_party/qwt/qwt_text_engine.h
+  include/pli_vis/third_party/qwt/qwt_text_label.h
+  include/pli_vis/third_party/qwt/qwt_thermo.h
+  include/pli_vis/third_party/qwt/qwt_transform.h
+  include/pli_vis/third_party/qwt/qwt_wheel.h
+  include/pli_vis/third_party/qwt/qwt_widget_overlay.h
+  include/pli_vis/third_party/qxt/QxtGlobal.h
+  include/pli_vis/third_party/qxt/QxtLetterBoxWidget.h
+  include/pli_vis/third_party/qxt/QxtLetterBoxWidgetP.h
+  include/pli_vis/third_party/qxt/QxtSpanSlider.h
+  include/pli_vis/third_party/qxt/QxtSpanSliderP.h
+  include/pli_vis/third_party/tangent-base/analytic_orbit_interpolator.hpp
+  include/pli_vis/third_party/tangent-base/base_operations.hpp
+  include/pli_vis/third_party/tangent-base/base_types.hpp
+  include/pli_vis/third_party/tangent-base/basic_trilinear_interpolator.hpp
+  include/pli_vis/third_party/tangent-base/cartesian_grid.hpp
+  include/pli_vis/third_party/tangent-base/cartesian_locator.hpp
+  include/pli_vis/third_party/tangent-base/default_tracers.hpp
+  include/pli_vis/third_party/tangent-base/dummy_recorder.hpp
+  include/pli_vis/third_party/tangent-base/omp_pos_tracer.hpp
+  include/pli_vis/third_party/tangent-base/particle_population.hpp
+  include/pli_vis/third_party/tangent-base/raw_binary_reader.hpp
+  include/pli_vis/third_party/tangent-base/runge_kutta_4_integrator.hpp
+  include/pli_vis/third_party/tangent-base/simple_tracer.hpp
+  include/pli_vis/third_party/tangent-base/trace_recorder.hpp
+  include/pli_vis/third_party/tangent-base/tracer_base.hpp
+	include/pli_vis/ui/plugins/color_plugin.hpp
+  include/pli_vis/ui/plugins/data_plugin.hpp
+  include/pli_vis/ui/plugins/fom_plugin.hpp
+  include/pli_vis/ui/plugins/global_tractography_plugin.hpp
+  include/pli_vis/ui/plugins/interactor_plugin.hpp
+  include/pli_vis/ui/plugins/odf_plugin.hpp
+  include/pli_vis/ui/plugins/polar_plot_plugin.hpp
+  include/pli_vis/ui/plugins/scalar_plugin.hpp
+  include/pli_vis/ui/plugins/local_tractography_plugin.hpp
+  include/pli_vis/ui/plugins/volume_rendering_plugin.hpp
+  include/pli_vis/ui/plugins/zernike_plugin.hpp
+  include/pli_vis/ui/utility/line_edit.hpp
+  include/pli_vis/ui/utility/plot_interactor.hpp
+  include/pli_vis/ui/utility/text_browser_sink.hpp
+  include/pli_vis/ui/widgets/roi_rectangle.hpp
+  include/pli_vis/ui/widgets/roi_selector.hpp
+  include/pli_vis/ui/widgets/transfer_function_editor.hpp
+  include/pli_vis/ui/widgets/viewer.hpp
+  include/pli_vis/ui/widgets/wait_spinner.hpp
+  include/pli_vis/ui/application.hpp
+  include/pli_vis/ui/plugin.hpp
+  include/pli_vis/ui/plugin_base.hpp
+	include/pli_vis/utility/make_even.hpp
+  include/pli_vis/visualization/algorithms/lineao_streamline_renderer.hpp
+  include/pli_vis/visualization/algorithms/odf_field.hpp
+  include/pli_vis/visualization/algorithms/polar_plot_field.hpp
+  include/pli_vis/visualization/algorithms/scalar_field.hpp
+  include/pli_vis/visualization/algorithms/streamline_renderer.hpp
+  include/pli_vis/visualization/algorithms/vector_field.hpp
+  include/pli_vis/visualization/algorithms/volume_renderer.hpp
+  include/pli_vis/visualization/algorithms/zernike_field.hpp
+  include/pli_vis/visualization/interactors/first_person_interactor.hpp
+  include/pli_vis/visualization/interactors/interactor.hpp
+  include/pli_vis/visualization/interactors/orbit_interactor.hpp
+  include/pli_vis/visualization/interactors/simple_interactor.hpp
+  include/pli_vis/visualization/primitives/camera.hpp
+  include/pli_vis/visualization/primitives/directional_light.hpp
+  include/pli_vis/visualization/primitives/light.hpp
+  include/pli_vis/visualization/primitives/point_light.hpp
+  include/pli_vis/visualization/primitives/spot_light.hpp
+  include/pli_vis/visualization/primitives/transform.hpp
+  include/pli_vis/visualization/utility/render_target.hpp
+  include/pli_vis/visualization/utility/sphere_tessellation.hpp
+  
+  shaders/depth_pass.frag.glsl
+  shaders/depth_pass.vert.glsl
+  shaders/lineao_color_pass.frag.glsl
+  shaders/lineao_color_pass.vert.glsl
+  shaders/lineao_main_pass.frag.glsl
+  shaders/lineao_main_pass.vert.glsl
+  shaders/lineao_normal_depth_pass.frag.glsl
+  shaders/lineao_normal_depth_pass.vert.glsl
+  shaders/lineao_zoom_pass.frag.glsl
+  shaders/lineao_zoom_pass.vert.glsl
+  shaders/odf_field_renderer.frag.glsl
+  shaders/odf_field_renderer.vert.glsl
+  shaders/polar_plot.frag.glsl
+  shaders/polar_plot.vert.glsl
+  shaders/simple_color_texture.frag.glsl
+  shaders/simple_color_texture.vert.glsl
+  shaders/simple_color.frag.glsl
+  shaders/simple_color.vert.glsl
+  shaders/simple_texture.frag.glsl
+  shaders/simple_texture.vert.glsl
+  shaders/streamline_renderer.frag.glsl
+  shaders/streamline_renderer.vert.glsl
+  shaders/view_dependent.frag.glsl
+  shaders/view_dependent.vert.glsl
+  shaders/view_dependent_vector_field.frag.glsl
+  shaders/view_dependent_vector_field.geom.glsl
+  shaders/view_dependent_vector_field.vert.glsl
+  shaders/volume_renderer_prepass.frag.glsl
+  shaders/volume_renderer_prepass.vert.glsl
+  shaders/volume_renderer.frag.glsl
+  shaders/volume_renderer.vert.glsl
+  shaders/zernike.frag.glsl
+  shaders/zernike.vert.glsl
+  
+  source/cuda/pt/tracer.cu
+  source/cuda/zernike/launch.cu
+  source/cuda/odf_field.cu
+  source/cuda/polar_plot.cu
+  source/third_party/glew/glew.c
+  source/third_party/qwt/qwt_abstract_legend.cpp
+  source/third_party/qwt/qwt_abstract_scale.cpp
+  source/third_party/qwt/qwt_abstract_scale_draw.cpp
+  source/third_party/qwt/qwt_abstract_slider.cpp
+  source/third_party/qwt/qwt_analog_clock.cpp
+  source/third_party/qwt/qwt_arrow_button.cpp
+  source/third_party/qwt/qwt_clipper.cpp
+  source/third_party/qwt/qwt_color_map.cpp
+  source/third_party/qwt/qwt_column_symbol.cpp
+  source/third_party/qwt/qwt_compass.cpp
+  source/third_party/qwt/qwt_compass_rose.cpp
+  source/third_party/qwt/qwt_counter.cpp
+  source/third_party/qwt/qwt_curve_fitter.cpp
+  source/third_party/qwt/qwt_date.cpp
+  source/third_party/qwt/qwt_date_scale_draw.cpp
+  source/third_party/qwt/qwt_date_scale_engine.cpp
+  source/third_party/qwt/qwt_dial.cpp
+  source/third_party/qwt/qwt_dial_needle.cpp
+  source/third_party/qwt/qwt_dyngrid_layout.cpp
+  source/third_party/qwt/qwt_event_pattern.cpp
+  source/third_party/qwt/qwt_graphic.cpp
+  source/third_party/qwt/qwt_interval.cpp
+  source/third_party/qwt/qwt_interval_symbol.cpp
+  source/third_party/qwt/qwt_knob.cpp
+  source/third_party/qwt/qwt_legend.cpp
+  source/third_party/qwt/qwt_legend_data.cpp
+  source/third_party/qwt/qwt_legend_label.cpp
+  source/third_party/qwt/qwt_magnifier.cpp
+  source/third_party/qwt/qwt_math.cpp
+  source/third_party/qwt/qwt_matrix_raster_data.cpp
+  source/third_party/qwt/qwt_null_paintdevice.cpp
+  source/third_party/qwt/qwt_painter.cpp
+  source/third_party/qwt/qwt_painter_command.cpp
+  source/third_party/qwt/qwt_panner.cpp
+  source/third_party/qwt/qwt_picker.cpp
+  source/third_party/qwt/qwt_picker_machine.cpp
+  source/third_party/qwt/qwt_pixel_matrix.cpp
+  source/third_party/qwt/qwt_plot.cpp
+  source/third_party/qwt/qwt_plot_abstract_barchart.cpp
+  source/third_party/qwt/qwt_plot_axis.cpp
+  source/third_party/qwt/qwt_plot_barchart.cpp
+  source/third_party/qwt/qwt_plot_canvas.cpp
+  source/third_party/qwt/qwt_plot_curve.cpp
+  source/third_party/qwt/qwt_plot_dict.cpp
+  source/third_party/qwt/qwt_plot_directpainter.cpp
+  source/third_party/qwt/qwt_plot_glcanvas.cpp
+  source/third_party/qwt/qwt_plot_grid.cpp
+  source/third_party/qwt/qwt_plot_histogram.cpp
+  source/third_party/qwt/qwt_plot_intervalcurve.cpp
+  source/third_party/qwt/qwt_plot_item.cpp
+  source/third_party/qwt/qwt_plot_layout.cpp
+  source/third_party/qwt/qwt_plot_legenditem.cpp
+  source/third_party/qwt/qwt_plot_magnifier.cpp
+  source/third_party/qwt/qwt_plot_marker.cpp
+  source/third_party/qwt/qwt_plot_multi_barchart.cpp
+  source/third_party/qwt/qwt_plot_panner.cpp
+  source/third_party/qwt/qwt_plot_picker.cpp
+  source/third_party/qwt/qwt_plot_rasteritem.cpp
+  source/third_party/qwt/qwt_plot_renderer.cpp
+  source/third_party/qwt/qwt_plot_rescaler.cpp
+  source/third_party/qwt/qwt_plot_scaleitem.cpp
+  source/third_party/qwt/qwt_plot_seriesitem.cpp
+  source/third_party/qwt/qwt_plot_shapeitem.cpp
+  source/third_party/qwt/qwt_plot_spectrocurve.cpp
+  source/third_party/qwt/qwt_plot_spectrogram.cpp
+  source/third_party/qwt/qwt_plot_svgitem.cpp
+  source/third_party/qwt/qwt_plot_textlabel.cpp
+  source/third_party/qwt/qwt_plot_tradingcurve.cpp
+  source/third_party/qwt/qwt_plot_xml.cpp
+  source/third_party/qwt/qwt_plot_zoneitem.cpp
+  source/third_party/qwt/qwt_plot_zoomer.cpp
+  source/third_party/qwt/qwt_point_3d.cpp
+  source/third_party/qwt/qwt_point_data.cpp
+  source/third_party/qwt/qwt_point_mapper.cpp
+  source/third_party/qwt/qwt_point_polar.cpp
+  source/third_party/qwt/qwt_raster_data.cpp
+  source/third_party/qwt/qwt_round_scale_draw.cpp
+  source/third_party/qwt/qwt_sampling_thread.cpp
+  source/third_party/qwt/qwt_scale_div.cpp
+  source/third_party/qwt/qwt_scale_draw.cpp
+  source/third_party/qwt/qwt_scale_engine.cpp
+  source/third_party/qwt/qwt_scale_map.cpp
+  source/third_party/qwt/qwt_scale_widget.cpp
+  source/third_party/qwt/qwt_series_data.cpp
+  source/third_party/qwt/qwt_slider.cpp
+  source/third_party/qwt/qwt_spline.cpp
+  source/third_party/qwt/qwt_symbol.cpp
+  source/third_party/qwt/qwt_system_clock.cpp
+  source/third_party/qwt/qwt_text.cpp
+  source/third_party/qwt/qwt_text_engine.cpp
+  source/third_party/qwt/qwt_text_label.cpp
+  source/third_party/qwt/qwt_thermo.cpp
+  source/third_party/qwt/qwt_transform.cpp
+  source/third_party/qwt/qwt_wheel.cpp
+  source/third_party/qwt/qwt_widget_overlay.cpp
+  source/third_party/qxt/QxtLetterBoxWidget.cpp
+  source/third_party/qxt/QxtSpanSlider.cpp
+	source/ui/plugins/color_plugin.cpp
+  source/ui/plugins/data_plugin.cpp
+  source/ui/plugins/fom_plugin.cpp
+  source/ui/plugins/global_tractography_plugin.cpp
+  source/ui/plugins/interactor_plugin.cpp
+  source/ui/plugins/odf_plugin.cpp
+  source/ui/plugins/polar_plot_plugin.cpp
+  source/ui/plugins/scalar_plugin.cpp
+  source/ui/plugins/local_tractography_plugin.cpp
+  source/ui/plugins/volume_rendering_plugin.cpp
+  source/ui/plugins/zernike_plugin.cpp
+  source/ui/utility/plot_interactor.cpp
+  source/ui/widgets/roi_rectangle.cpp
+  source/ui/widgets/roi_selector.cpp
+  source/ui/widgets/transfer_function_editor.cpp
+  source/ui/widgets/viewer.cpp
+  source/ui/widgets/wait_spinner.cpp
+  source/ui/application.cpp
+  source/visualization/algorithms/lineao_streamline_renderer.cpp
+  source/visualization/algorithms/odf_field.cpp
+  source/visualization/algorithms/polar_plot_field.cpp
+  source/visualization/algorithms/scalar_field.cpp
+  source/visualization/algorithms/streamline_renderer.cpp
+  source/visualization/algorithms/vector_field.cpp
+  source/visualization/algorithms/volume_renderer.cpp
+  source/visualization/algorithms/zernike_field.cpp
+  source/visualization/interactors/first_person_interactor.cpp
+  source/visualization/interactors/orbit_interactor.cpp
+  source/visualization/interactors/simple_interactor.cpp
+  source/visualization/primitives/camera.cpp
+  source/visualization/primitives/directional_light.cpp
+  source/visualization/primitives/light.cpp
+  source/visualization/primitives/point_light.cpp
+  source/visualization/primitives/spot_light.cpp
+  source/visualization/primitives/transform.cpp
+  source/visualization/utility/render_target.cpp
+  source/main.cpp
+)
+include(assign_source_group)
+assign_source_group(${PROJECT_SOURCES})
 
-# Setup sources.
-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/third_party)
-include("cmake/_sources.cmake")
+##################################################  Dependencies  ##################################################
+include(import_library)
 
-# Create source groups.
-include("cmake/assign_source_group.cmake")
-assign_source_group(${ProjectSources})
+find_package  (Boost REQUIRED)
+import_library(boost Boost_INCLUDE_DIR)
 
-# Specify output.
-cuda_add_executable(${ProjectName} ${ProjectSources})
+set           (CUDA_SEPARABLE_COMPILATION ON)
+find_package  (CUDA REQUIRED)
+list          (APPEND PROJECT_LIBRARIES 
+               ${CUDA_CUBLAS_LIBRARIES} 
+               ${CUDA_cusolver_LIBRARY} 
+               ${CUDA_cudadevrt_LIBRARY} 
+               ${CUDA_curand_LIBRARY})
 
-# Link libraries.
-target_link_libraries(${ProjectName} ${ProjectLibraries})
+find_package(HDF5 NAMES hdf5 COMPONENTS C shared)
+if(NOT HDF5_INCLUDE_DIR OR NOT HDF5_C_SHARED_LIBRARY)
+  set(HDF5_INCLUDE_DIR      CACHE STRING "")
+  set(HDF5_C_SHARED_LIBRARY CACHE STRING "")
+  message(FATAL_ERROR "HDF5 not found! Please set the include directory and libraries manually.")
+else()
+  import_library(hdf5 HDF5_INCLUDE_DIR HDF5_C_SHARED_LIBRARY)
+endif()
+
+find_package  (OpenMP)
+list          (APPEND CMAKE_CXX_FLAGS ${OpenMP_CXX_FLAGS})
+
+find_package  (OpenGL REQUIRED)
+import_library(opengl OPENGL_INCLUDE_DIRS OPENGL_LIBRARIES)
+
+set           (CMAKE_AUTOMOC ON)
+set           (CMAKE_AUTOUIC ON)
+set           (CMAKE_INCLUDE_CURRENT_DIR ON)
+find_package  (Qt5Widgets REQUIRED)
+list          (APPEND PROJECT_LIBRARIES Qt5::Widgets)
+
+##################################################    Targets     ##################################################
+cuda_add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
+target_include_directories(${PROJECT_NAME} PUBLIC 
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME}/third_party>
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
+  $<INSTALL_INTERFACE:include> 
+  $<INSTALL_INTERFACE:include/${PROJECT_NAME}/third_party>
+  PRIVATE source)
+target_link_libraries     (${PROJECT_NAME} PUBLIC ${PROJECT_LIBRARIES})
+target_compile_definitions(${PROJECT_NAME} PUBLIC -DQWT_NO_SVG -DQT_NO_SVG -DQT_NO_PRINTER -DQT_NO_PRINTDIALOG)
+set_target_properties     (${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)
+
+##################################################    Testing     ##################################################
+if(BUILD_TESTS)
+  enable_testing()
+
+  set(PROJECT_TEST_SOURCES
+
+  )
+
+  foreach(_SOURCE ${PROJECT_TEST_SOURCES})
+    get_filename_component(_NAME ${_SOURCE} NAME_WE)
+    set                   (_SOURCES tests/catch.hpp tests/main.cpp ${_SOURCE})
+    add_executable        (${_NAME} ${_SOURCES})
+    target_link_libraries (${_NAME} ${PROJECT_NAME})
+    add_test              (${_NAME} ${_NAME})
+    set_property          (TARGET ${_NAME} PROPERTY FOLDER "Tests")
+    source_group          ("source" FILES ${_SOURCES})
+  endforeach()
+endif()
+
+##################################################  Installation  ##################################################
+install(TARGETS ${PROJECT_NAME} EXPORT "${PROJECT_NAME}-config"
+  ARCHIVE DESTINATION lib
+  LIBRARY DESTINATION lib
+  RUNTIME DESTINATION bin)
+install(DIRECTORY include/ DESTINATION include)
+install(EXPORT  "${PROJECT_NAME}-config" DESTINATION "cmake")
+export (TARGETS "${PROJECT_NAME}"        FILE        "${PROJECT_NAME}-config.cmake")
diff --git a/cmake/_dependencies.cmake b/cmake/_dependencies.cmake
deleted file mode 100644
index 4cf64b252350596ec8aa61e71090a780889bcea5..0000000000000000000000000000000000000000
--- a/cmake/_dependencies.cmake
+++ /dev/null
@@ -1,34 +0,0 @@
-# Register bundled modules.
-list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
-
-# Include Boost.
-find_package(Boost REQUIRED)
-include_directories(${Boost_INCLUDE_DIR})
-
-# Include Cuda and Cublas.
-set(CUDA_SEPARABLE_COMPILATION ON)
-find_package(CUDA REQUIRED)
-set(ProjectLibraries ${ProjectLibraries} "${CUDA_CUBLAS_LIBRARIES};${CUDA_cusolver_LIBRARY};${CUDA_cudadevrt_LIBRARY}")
-
-# Include HDF5.
-find_package(HDF5 NAMES hdf5 COMPONENTS C shared)
-if(NOT HDF5_INCLUDE_DIR OR NOT HDF5_C_SHARED_LIBRARY)
-  set(HDF5_INCLUDE_DIR CACHE STRING "")
-  set(HDF5_C_SHARED_LIBRARY CACHE STRING "")
-  message(FATAL_ERROR "HDF5 not found! Please set the include directory and libraries manually.")
-else()
-  include_directories(${HDF5_INCLUDE_DIR})
-  set(ProjectLibraries ${ProjectLibraries} ${HDF5_C_SHARED_LIBRARY})
-endif()
-
-# Include OpenGL.
-find_package       (OpenGL REQUIRED)
-include_directories(${OPENGL_INCLUDE_DIRS})
-set(ProjectLibraries ${ProjectLibraries} "${OPENGL_LIBRARIES}")
-
-# Include Qt.
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTOUIC ON)
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-find_package(Qt5Widgets REQUIRED)
-set(ProjectLibraries ${ProjectLibraries} Qt5::Widgets)
diff --git a/cmake/_sources.cmake b/cmake/_sources.cmake
deleted file mode 100644
index 73d1818c0c736e17ea48398b37b3dcc0083fdc8c..0000000000000000000000000000000000000000
--- a/cmake/_sources.cmake
+++ /dev/null
@@ -1,102 +0,0 @@
-set(ProjectSources
-  cmake/_dependencies.cmake
-  cmake/_sources.cmake
-  
-  include/attributes/loggable.hpp
-  include/attributes/renderable.hpp
-  include/cuda/odf_field.h
-  include/cuda/orthtree.h
-  include/cuda/spherical_histogram.h
-  include/cuda/vector_field.h
-  include/io/hdf5_io.hpp
-  include/io/hdf5_io_2.hpp
-  include/io/hdf5_io_base.hpp
-  include/io/hdf5_io_selector.hpp
-  include/math/camera.hpp
-  include/math/linear_math.hpp
-  include/math/transform.hpp
-  include/opengl/auxiliary/glm_uniforms.hpp
-  include/opengl/all.hpp
-  include/opengl/buffer.hpp
-  include/opengl/framebuffer.hpp
-  include/opengl/opengl.hpp
-  include/opengl/program.hpp
-  include/opengl/shader.hpp
-  include/opengl/texture.hpp
-  include/opengl/vertex_array.hpp
-  include/sh/choose.h
-  include/sh/clebsch_gordan.h
-  include/sh/convert.h
-  include/sh/cush.h
-  include/sh/decorators.h
-  include/sh/factorial.h
-  include/sh/launch.h
-  include/sh/legendre.h
-  include/sh/sign.h
-  include/sh/spherical_harmonics.h
-  include/sh/vector_ops.h
-  include/sh/wigner.h
-  include/third_party/glew/GL/glew.h
-  include/third_party/qxt/QxtGlobal.h
-  include/third_party/qxt/QxtLetterBoxWidget.h
-  include/third_party/qxt/QxtLetterBoxWidgetP.h
-  include/third_party/qxt/QxtSpanSlider.h
-  include/third_party/qxt/QxtSpanSliderP.h
-  include/ui/plugins/data_plugin.hpp
-  include/ui/plugins/fom_plugin.hpp
-  include/ui/plugins/fdm_plugin.hpp
-  include/ui/plugins/interactor_plugin.hpp
-  include/ui/plugins/plugin.hpp
-  include/ui/plugins/scalar_plugin.hpp
-  include/ui/plugins/selector_plugin.hpp
-  include/ui/plugins/tractography_plugin.hpp
-  include/ui/overview_image.hpp
-  include/ui/selection_square.hpp
-  include/ui/viewer.hpp
-  include/ui/wait_spinner.hpp
-  include/ui/window.hpp
-  include/utility/line_edit_utility.hpp
-  include/utility/qt_text_browser_sink.hpp
-  include/utility/thread_pool.hpp
-  include/visualization/interactors/first_person_interactor.hpp
-  include/visualization/interactors/orbit_interactor.hpp
-  include/visualization/interactors/simple_interactor.hpp
-  include/visualization/odf_field.hpp
-  include/visualization/scalar_field.hpp
-  include/visualization/vector_field.hpp
-  
-  shaders/odf_field.frag.glsl
-  shaders/odf_field.vert.glsl
-  shaders/scalar_field.frag.glsl
-  shaders/scalar_field.vert.glsl
-  shaders/vector_field.frag.glsl
-  shaders/vector_field.vert.glsl
-
-  source/cuda/odf_field.cu
-  source/cuda/vector_field.cu
-  source/math/camera.cpp
-  source/math/transform.cpp
-  source/third_party/glew/glew.c
-  source/third_party/qxt/QxtLetterBoxWidget.cpp
-  source/third_party/qxt/QxtSpanSlider.cpp
-  source/ui/plugins/data_plugin.cpp
-  source/ui/plugins/fom_plugin.cpp
-  source/ui/plugins/fdm_plugin.cpp
-  source/ui/plugins/interactor_plugin.cpp
-  source/ui/plugins/plugin.cpp
-  source/ui/plugins/scalar_plugin.cpp
-  source/ui/plugins/selector_plugin.cpp
-  source/ui/plugins/tractography_plugin.cpp
-  source/ui/overview_image.cpp
-  source/ui/selection_square.cpp
-  source/ui/viewer.cpp
-  source/ui/wait_spinner.cpp
-  source/ui/window.cpp
-  source/visualization/interactors/first_person_interactor.cpp
-  source/visualization/interactors/orbit_interactor.cpp
-  source/visualization/interactors/simple_interactor.cpp
-  source/visualization/odf_field.cpp
-  source/visualization/scalar_field.cpp
-  source/visualization/vector_field.cpp
-  source/main.cpp
-)
diff --git a/cmake/assign_source_group.cmake b/cmake/assign_source_group.cmake
index eef88e6a54cde9a4d2856182d19a3791a1f09b65..49e5d72ea9f66aa93ce9ee1f0ece280ec7791e80 100644
--- a/cmake/assign_source_group.cmake
+++ b/cmake/assign_source_group.cmake
@@ -1,16 +1,17 @@
+# Assigns the given files to source groups identical to their location.
 function(assign_source_group)
-    foreach(_source IN ITEMS ${ARGN})
-        if (IS_ABSOLUTE "${_source}")
-            file(RELATIVE_PATH _source_rel "${CMAKE_CURRENT_SOURCE_DIR}" "${_source}")
-        else()
-            set(_source_rel "${_source}")
-        endif()
-        get_filename_component(_source_path "${_source_rel}" PATH)
-        if(WIN32)
-          string(REPLACE "/" "\\" _source_path_msvc "${_source_path}")
-          source_group("${_source_path_msvc}" FILES "${_source}")
-        else()
-          source_group("${_source_path}" FILES "${_source}")
-        endif()
-    endforeach()
+  foreach(_SOURCE IN ITEMS ${ARGN})
+    if (IS_ABSOLUTE "${_SOURCE}")
+      file(RELATIVE_PATH _SOURCE_REL "${CMAKE_CURRENT_SOURCE_DIR}" "${_SOURCE}")
+    else()
+      set(_SOURCE_REL "${_SOURCE}")
+    endif()
+    get_filename_component(_SOURCE_PATH "${_SOURCE_REL}" PATH)
+    if(WIN32)
+      string(REPLACE "/" "\\" _SOURCE_PATH_MSVC "${_SOURCE_PATH}")
+      source_group("${_SOURCE_PATH_MSVC}" FILES "${_SOURCE}")
+    else()
+      source_group("${_SOURCE_PATH}" FILES "${_SOURCE}")
+    endif()
+  endforeach()
 endfunction(assign_source_group)
diff --git a/cmake/import_library.cmake b/cmake/import_library.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..05b312bab53144b64b264bdb248cd4719c03b0ce
--- /dev/null
+++ b/cmake/import_library.cmake
@@ -0,0 +1,29 @@
+# Imports a library which is not built with cmake.
+# The imported library is appended to the PROJECT_LIBRARIES variable.
+# Usage:
+#   Header Only:
+#     import_library(ALIAS INCLUDE_DIRS)
+#   Identical Debug and Release:
+#     import_library(ALIAS INCLUDE_DIRS LIBRARIES)
+#   Separate  Debug and Release:
+#     import_library(ALIAS INCLUDE_DIRS DEBUG_LIBRARIES RELEASE_LIBRARIES)
+function(import_library ALIAS INCLUDE_DIRS)
+  set(ALIAS ${PROJECT_NAME}::${ALIAS})
+  add_library(${ALIAS} INTERFACE IMPORTED)
+  set_property(TARGET ${ALIAS} PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${${INCLUDE_DIRS}})
+  
+  set (_EXTRA_ARGS ${ARGN})
+  list(LENGTH _EXTRA_ARGS _EXTRA_ARGS_LENGTH)
+  if    (_EXTRA_ARGS_LENGTH EQUAL 1)
+    list        (GET _EXTRA_ARGS 0 _LIBRARIES)
+    set_property(TARGET ${ALIAS} PROPERTY INTERFACE_LINK_LIBRARIES ${${_LIBRARIES}})
+  elseif(_EXTRA_ARGS_LENGTH EQUAL 2)
+    list        (GET _EXTRA_ARGS 0 _DEBUG_LIBRARIES  )
+    list        (GET _EXTRA_ARGS 1 _RELEASE_LIBRARIES)
+    set_property(TARGET ${ALIAS} PROPERTY INTERFACE_LINK_LIBRARIES  
+      $<$<CONFIG:Debug>:${${_DEBUG_LIBRARIES}}> 
+      $<$<NOT:$<CONFIG:Debug>>:${${_RELEASE_LIBRARIES}}>)
+  endif ()
+  
+  set(PROJECT_LIBRARIES ${PROJECT_LIBRARIES} ${ALIAS} PARENT_SCOPE)
+endfunction(import_library)
diff --git a/include/cuda/odf_field.h b/include/cuda/odf_field.h
deleted file mode 100644
index 1e56208553f287a97e98ff9f91e207d755daa9ae..0000000000000000000000000000000000000000
--- a/include/cuda/odf_field.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef PLI_VIS_SAMPLE_H_
-#define PLI_VIS_SAMPLE_H_
-
-#include <functional>
-#include <math.h>
-
-#include <cusolverDn.h>
-#include <device_launch_parameters.h>
-#include <vector_types.h>
-
-#include <sh/vector_ops.h>
-
-namespace pli
-{
-void calculate_odfs(
-  cublasHandle_t     cublas        ,
-  cusolverDnHandle_t cusolver      ,
-  const uint3&       dimensions    ,
-  const uint3&       vectors_size  , 
-  const uint2&       histogram_bins,
-  const unsigned     maximum_degree,
-  const float*       directions    ,
-  const float*       inclinations  ,
-        float*       coefficients  ,
-        std::function<void(const std::string&)> status_callback = [](const std::string&){});
-
-void calculate_odfs(
-  cublasHandle_t     cublas        ,
-  cusolverDnHandle_t cusolver      ,
-  const uint3&       dimensions    ,
-  const uint3&       vectors_size  , 
-  const uint2&       histogram_bins,
-  const unsigned     maximum_degree,
-  const float3*      unit_vectors  ,
-        float*       coefficients  ,
-        std::function<void(const std::string&)> status_callback = [](const std::string&){});
-
-void sample_odfs(
-  const uint3&       dimensions        ,
-  const unsigned     coefficient_count ,
-  const float*       coefficients      ,
-  const uint2&       tessellations     , 
-  const float3&      vector_spacing    ,
-  const uint3&       vector_dimensions ,
-  const float        scale             ,
-        float3*      points            ,
-        float4*      colors            ,
-        unsigned*    indices           ,
-        bool         clustering        = false,
-        float        cluster_threshold = 0.0  ,
-        std::function<void(const std::string&)> status_callback = [](const std::string&){});
-  
-// Called on a layer_dimensions.x x layer_dimensions.y x layer_dimensions.z 3D grid.
-__global__ void sample_odf_layer(
-  const uint3    layer_dimensions  ,
-  const unsigned layer_offset      ,
-  const unsigned coefficient_count ,
-  float*         coefficients      ,
-  bool           is_2d             ,
-  bool           clustering        ,
-  float          cluster_threshold );
-}
-
-#endif
\ No newline at end of file
diff --git a/include/cuda/orthtree.h b/include/cuda/orthtree.h
deleted file mode 100644
index 5fb6201d3d37192a3d28c399760b016168d45d54..0000000000000000000000000000000000000000
--- a/include/cuda/orthtree.h
+++ /dev/null
@@ -1,218 +0,0 @@
-#ifndef ORTHTREE_H_
-#define ORTHTREE_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <initializer_list>
-#include <math.h>
-#include <type_traits>
-
-#ifdef ORTHTREE_CUDA_SUPPORT
-#include <vector_types.h>
-#endif
-
-// Functions for creating and manipulating a complete (dense) linear orthtree.
-namespace orthtree
-{
-  // Utility functions.
-  inline std::size_t to_linear_index       (std::initializer_list<std::size_t> indices)
-  {
-    // Note that last dimension is indexed first.
-    std::size_t index = 0;
-    for (auto i = 0; i < indices.size(); ++i)
-      index += *(indices.begin() + i) * powf(2, indices.size() - 1 - i);
-    return index;
-  }
-  template<std::size_t dimensions, typename array_type>
-  array_type         to_n_dimensional_index(std::size_t index)
-  {
-    array_type indices; 
-    to_n_dimensional_index<dimensions>(index, indices);
-    return indices;
-  }
-  template<std::size_t dimensions, typename array_type>
-  void               to_n_dimensional_index(std::size_t index, array_type& indices)
-  {
-    // Note that last dimension is indexed first.
-    for (auto i = 0; i < dimensions; ++i)
-    {
-      indices[i] = index      / powf(2, dimensions - 1 - i);
-      index     -= indices[i] * powf(2, dimensions - 1 - i);
-    }
-  }
-
-  // Tree functions.
-  template<std::size_t dimensions>
-  std::size_t subdivision_count   ()
-  {
-    return powf(2, dimensions);
-  }
-  template<std::size_t dimensions>
-  std::size_t depth_size          (std::size_t depth)
-  {
-    return powf(2, dimensions * depth);
-  }
-  template<std::size_t dimensions> 
-  std::size_t depth_start         (std::size_t depth)
-  {
-    return (powf(2, dimensions * depth) - 1.0) / (subdivision_count<dimensions>() - 1);
-  }
-  template<std::size_t dimensions>
-  std::size_t depth_end           (std::size_t depth)
-  {
-    return depth_start<dimensions>(depth) + depth_size<dimensions>(depth);
-  }
-  template<std::size_t dimensions>
-  std::size_t node_count          (std::size_t max_depth)
-  {
-    return depth_start<dimensions>(max_depth + 1);
-  }
-
-  // Node functions.
-  template<std::size_t dimensions>
-  std::size_t depth               (std::size_t index)
-  {
-    return floor(logf(index * (subdivision_count<dimensions>() - 1) + 1) / logf(subdivision_count<dimensions>()));
-  }
-  template<std::size_t dimensions, typename precision>
-  precision   half_size           (std::size_t index)
-  {
-    return 0.5 / powf(2, depth<dimensions>(index));
-  }
-  template<std::size_t dimensions>
-  std::size_t parent_index        (std::size_t index)
-  {
-    return floor(static_cast<float>(index - 1) / subdivision_count<dimensions>());
-  }
-  template<std::size_t dimensions>
-  std::size_t child_index         (std::size_t index, std::size_t                        child_local_index  )
-  {
-    return subdivision_count<dimensions>() * index + child_local_index + 1;
-  }
-  template<std::size_t dimensions>
-  std::size_t child_index         (std::size_t index, std::initializer_list<std::size_t> child_local_indices)
-  {
-    return child_index<dimensions>(index, to_linear_index(child_local_indices));
-  }
-  template<std::size_t dimensions>
-  std::size_t index_in_parent     (std::size_t index)
-  {
-    auto parent = parent_index<dimensions>(index);
-    for (auto i = 0; i < subdivision_count<dimensions>(); i++)
-      if (child_index<dimensions>(parent, i) == index)
-        return i;
-    return 0;
-  }
-  template<std::size_t dimensions, typename array_type>
-  array_type  index_in_parent     (std::size_t index)
-  {
-    return to_n_dimensional_index<dimensions, array_type>(index_in_parent<dimensions>(index));
-  }
-  template<std::size_t dimensions, typename index_type, typename array_type>
-  array_type  position            (std::size_t index)
-  {
-    array_type array;
-    position<dimensions, index_type>(index, array);
-    return array;
-  }
-  template<std::size_t dimensions, typename index_type, typename array_type>
-  void        position            (std::size_t index, array_type& array)
-  {
-    typedef typename std::remove_reference<decltype(array[0])>::type element_type;
-
-    if (index == 0)
-    {
-      for (auto i = 0; i < dimensions; i++)
-        array[i] = 0;
-      return;
-    }
-
-    auto parent_position = position<dimensions, index_type, array_type>(parent_index<dimensions>(index));
-    auto index_in_parent = orthtree::index_in_parent<dimensions, index_type  >(index);
-    auto half_size       = orthtree::half_size      <dimensions, element_type>(index);
-    for (auto i = 0; i < dimensions; i++)
-      array[i] = parent_position[i] + (index_in_parent[i] != 0 ? half_size : -half_size);
-  }
-
-  // Note that last dimension is indexed first.
-#ifdef ORTHTREE_CUDA_SUPPORT
-
-  #define SPECIALIZE_ORTHTREE(TYPE)                                                                     \
-  template<std::size_t dimensions>                                                                      \
-  void to_n_dimensional_index(std::size_t index, TYPE& indices)                                         \
-  {                                                                                                     \
-    auto indices_ptr = &indices.x;                                                                      \
-    for (auto i = 0; i < dimensions; ++i)                                                               \
-    {                                                                                                   \
-      indices_ptr[i] = index / powf(2, dimensions - 1 - i);                                             \
-      index         -= indices_ptr[i] * powf(2, dimensions - 1 - i);                                    \
-    }                                                                                                   \
-  }                                                                                                     \
-  template<std::size_t dimensions, typename index_type>                                                 \
-  void position(std::size_t index, TYPE& array)                                                         \
-  {                                                                                                     \
-    typedef typename std::remove_reference<decltype(array.x)>::type element_type;                       \
-                                                                                                        \
-    auto array_ptr = &array.x;                                                                          \
-                                                                                                        \
-    if (index == 0)                                                                                     \
-    {                                                                                                   \
-      for (auto i = 0; i < dimensions; i++)                                                             \
-        array_ptr[i] = 0;                                                                               \
-      return;                                                                                           \
-    }                                                                                                   \
-                                                                                                        \
-    auto parent_position     = position<dimensions, index_type, TYPE>(parent_index<dimensions>(index)); \
-    auto parent_ptr          = &parent_position.x;                                                      \
-    auto index_in_parent     = orthtree::index_in_parent<dimensions, index_type  >(index);              \
-    auto index_in_parent_ptr = &index_in_parent.x;                                                      \
-    auto half_size           = orthtree::half_size      <dimensions, element_type>(index);              \
-    for (auto i = 0; i < dimensions; i++)                                                               \
-      array_ptr[i] = parent_ptr[i] + (index_in_parent_ptr[i] != 0 ? half_size : -half_size);            \
-  }                                                                                                     \
-
-  SPECIALIZE_ORTHTREE(char2     )
-  SPECIALIZE_ORTHTREE(uchar2    )
-  SPECIALIZE_ORTHTREE(short2    )
-  SPECIALIZE_ORTHTREE(ushort2   )
-  SPECIALIZE_ORTHTREE(int2      )
-  SPECIALIZE_ORTHTREE(uint2     )
-  SPECIALIZE_ORTHTREE(long2     )
-  SPECIALIZE_ORTHTREE(ulong2    )
-  SPECIALIZE_ORTHTREE(float2    )
-  SPECIALIZE_ORTHTREE(longlong2 )
-  SPECIALIZE_ORTHTREE(ulonglong2)
-  SPECIALIZE_ORTHTREE(double2   )
-                                       
-  SPECIALIZE_ORTHTREE(char3     )
-  SPECIALIZE_ORTHTREE(uchar3    )
-  SPECIALIZE_ORTHTREE(short3    )
-  SPECIALIZE_ORTHTREE(ushort3   )
-  SPECIALIZE_ORTHTREE(int3      )
-  SPECIALIZE_ORTHTREE(uint3     )
-  SPECIALIZE_ORTHTREE(long3     )
-  SPECIALIZE_ORTHTREE(ulong3    )
-  SPECIALIZE_ORTHTREE(float3    )
-  SPECIALIZE_ORTHTREE(longlong3 )
-  SPECIALIZE_ORTHTREE(ulonglong3)
-  SPECIALIZE_ORTHTREE(double3   )
-
-  SPECIALIZE_ORTHTREE(char4     )
-  SPECIALIZE_ORTHTREE(uchar4    )
-  SPECIALIZE_ORTHTREE(short4    )
-  SPECIALIZE_ORTHTREE(ushort4   )
-  SPECIALIZE_ORTHTREE(int4      )
-  SPECIALIZE_ORTHTREE(uint4     )
-  SPECIALIZE_ORTHTREE(long4     )
-  SPECIALIZE_ORTHTREE(ulong4    )
-  SPECIALIZE_ORTHTREE(float4    )
-  SPECIALIZE_ORTHTREE(longlong4 )
-  SPECIALIZE_ORTHTREE(ulonglong4)
-  SPECIALIZE_ORTHTREE(double4   )
-
-  #undef SPECIALIZE_ORTHTREE
-
-#endif
-}
-
-#endif
diff --git a/include/cuda/spherical_histogram.h b/include/cuda/spherical_histogram.h
deleted file mode 100644
index 0a9c24b97e70a84d454b70bfbfe8be92a16b6a36..0000000000000000000000000000000000000000
--- a/include/cuda/spherical_histogram.h
+++ /dev/null
@@ -1,136 +0,0 @@
-#ifndef PLI_ODF_SPHERICAL_HISTOGRAM_H_
-#define PLI_ODF_SPHERICAL_HISTOGRAM_H_
-
-#include <device_launch_parameters.h>
-#include <vector_types.h>
-
-namespace pli
-{
-// Call on a bin_dimensions.x x bin_dimensions.y 2D grid.
-template<typename vector_type>
-__global__ void create_bins(
-  uint2        bin_dimensions, 
-  vector_type* bin_vectors   )
-{
-  auto longitude_index = blockIdx.x * blockDim.x + threadIdx.x;
-  auto latitude_index  = blockIdx.y * blockDim.y + threadIdx.y;
-
-  if (longitude_index >= bin_dimensions.x ||
-      latitude_index  >= bin_dimensions.y )
-    return;
-
-  auto& bin_vector = bin_vectors[longitude_index + bin_dimensions.x * latitude_index];
-  bin_vector.x = 1.0;
-  bin_vector.y = 2 * M_PI *  longitude_index     /  bin_dimensions.x;
-
-  if (longitude_index == 0 && latitude_index == 0)
-    bin_vector.z = 0;
-  else if (longitude_index == bin_dimensions.x - 1 && latitude_index == bin_dimensions.y - 1)
-    bin_vector.z = M_PI;
-  else
-    bin_vector.z = M_PI * (latitude_index + 1) / (bin_dimensions.y + 1);
-}
-
-// Call on a vectors_size.x x vectors_size.y x vectors_size.z 3D grid.
-template<typename vector_type, typename magnitude_type>
-__global__ void accumulate(
-  uint3                 vectors_size  ,
-  uint3                 field_offset  ,
-  uint3                 field_size    ,
-  const magnitude_type* longitudes    ,
-  const magnitude_type* latitudes     ,
-  uint2                 bin_dimensions,
-  vector_type*          bin_vectors   ,
-  magnitude_type*       bin_magnitudes)
-{
-  auto x = blockIdx.x * blockDim.x + threadIdx.x;
-  auto y = blockIdx.y * blockDim.y + threadIdx.y;
-  auto z = blockIdx.z * blockDim.z + threadIdx.z;
-
-  if (x >= vectors_size.x || y >= vectors_size.y || z >= vectors_size.z)
-    return;
-
-  auto  vector_index   = z + field_size.z * (y + field_size.y * (x + field_offset.x) + field_offset.y) + field_offset.z;
-  auto& direction      = longitudes[vector_index];
-  auto& inclination    = latitudes [vector_index];
-  
-  magnitude_type max_dot = -1; auto max_index = -1;
-  magnitude_type min_dot =  1; auto min_index = -1;
-  for (auto i = 0; i < bin_dimensions.x * bin_dimensions.y; i++)
-  {
-    auto& bin_vector = bin_vectors[i];
-
-    magnitude_type dot = 
-      cos(inclination) * cos(bin_vector.z) + 
-      sin(inclination) * sin(bin_vector.z) * 
-      cos(direction - bin_vector.y);
-
-    if (dot > max_dot)
-    {
-      max_dot   = dot;
-      max_index = i;
-    }
-    if (dot < min_dot)
-    {
-      min_dot   = dot;
-      min_index = i;
-    }
-  }
-  if (max_index != -1)
-    atomicAdd(&bin_magnitudes[max_index], 1.0);
-  if (min_index != -1)
-    atomicAdd(&bin_magnitudes[min_index], 1.0);
-}
-
-  
-// Call on a vectors_size.x x vectors_size.y x vectors_size.z 3D grid.
-template<typename vector_type, typename magnitude_type>
-__global__ void accumulate(
-  uint3                 vectors_size  ,
-  uint3                 field_offset  ,
-  uint3                 field_size    ,
-  const vector_type*    vectors       ,
-  uint2                 bin_dimensions,
-  vector_type*          bin_vectors   ,
-  magnitude_type*       bin_magnitudes)
-{
-  auto x = blockIdx.x * blockDim.x + threadIdx.x;
-  auto y = blockIdx.y * blockDim.y + threadIdx.y;
-  auto z = blockIdx.z * blockDim.z + threadIdx.z;
-
-  if (x >= vectors_size.x || y >= vectors_size.y || z >= vectors_size.z)
-    return;
-
-  auto  vector_index = z + field_size.z * (y + field_size.y * (x + field_offset.x) + field_offset.y) + field_offset.z;
-  auto& vector       = vectors[vector_index];
-  
-  magnitude_type max_dot = -1; auto max_index = -1;
-  magnitude_type min_dot =  1; auto min_index = -1;
-  for (auto i = 0; i < bin_dimensions.x * bin_dimensions.y; i++)
-  {
-    auto& bin_vector = bin_vectors[i];
-
-    magnitude_type dot = 
-      cos(vector.z) * cos(bin_vector.z) + 
-      sin(vector.z) * sin(bin_vector.z) * 
-      cos(vector.y - bin_vector.y);
-
-    if (dot > max_dot)
-    {
-      max_dot   = dot;
-      max_index = i;
-    }
-    if (dot < min_dot)
-    {
-      min_dot   = dot;
-      min_index = i;
-    }
-  }
-  if (max_index != -1)
-    atomicAdd(&bin_magnitudes[max_index], 1.0);
-  if (min_index != -1)
-    atomicAdd(&bin_magnitudes[min_index], 1.0);
-}
-}
-
-#endif
diff --git a/include/cuda/vector_field.h b/include/cuda/vector_field.h
deleted file mode 100644
index f57faf39d361ababad7a033d71c7a56d88f6427c..0000000000000000000000000000000000000000
--- a/include/cuda/vector_field.h
+++ /dev/null
@@ -1,121 +0,0 @@
-#ifndef PLI_VIS_VECTOR_FIELD_H_
-#define PLI_VIS_VECTOR_FIELD_H_
-
-#define _USE_MATH_DEFINES
-
-#include <functional>
-#include <math.h>
-#include <string>
-
-#include <device_launch_parameters.h>
-#include <vector_types.h>
-
-#include <sh/convert.h>
-#include <sh/vector_ops.h>
-
-namespace pli
-{
-void create_vector_field(
-  const uint3&  dimensions  ,
-  const float*  directions  ,
-  const float*  inclinations,
-  const float3& spacing     ,
-  const float&  scale       ,
-        float3* points      ,
-        float4* colors      ,
-  std::function<void(const std::string&)> status_callback = [](const std::string&){});
-
-void create_vector_field(
-  const uint3&  dimensions  ,
-  const float3* unit_vectors,
-  const float3& spacing     ,
-  const float&  scale       ,
-        float3* points      ,
-        float4* colors      ,
-  std::function<void(const std::string&)> status_callback = [](const std::string&) {});
-
-template<typename scalar_type, typename vector_type, typename color_type>
-__global__ void create_vector_field_internal(
-  const uint3        dimensions  ,
-  const scalar_type* directions  ,
-  const scalar_type* inclinations,
-  const vector_type  spacing     ,
-  const scalar_type  scale       ,
-        vector_type* points      ,
-        color_type*  colors      )
-{
-  auto x = blockIdx.x * blockDim.x + threadIdx.x;
-  auto y = blockIdx.y * blockDim.y + threadIdx.y;
-  auto z = blockIdx.z * blockDim.z + threadIdx.z;
-
-  if (x >= dimensions.x ||
-      y >= dimensions.y ||
-      z >= dimensions.z)
-    return;
-
-  auto        volume_index = z + dimensions.z * (y + dimensions.y * x);
-  scalar_type longitude    = (90.0F + directions  [volume_index]) * M_PI / 180.0F;
-  scalar_type latitude     = (90.0F - inclinations[volume_index]) * M_PI / 180.0F;
-
-  vector_type position = {
-    x * spacing.x,
-    y * spacing.y,
-    z * spacing.z};
-
-  auto minimum_spacing = float(min(spacing.x, min(spacing.y, spacing.z)));
-  auto vector_start    = cush::to_cartesian_coords(float3{ scale * minimum_spacing / 2.0F, longitude, latitude});
-  auto vector_end      = cush::to_cartesian_coords(float3{-scale * minimum_spacing / 2.0F, longitude, latitude});
-  auto unscaled        = cush::to_cartesian_coords(float3{1.0F, longitude, latitude});
-  // auto color        = make_float4(abs(unscaled.x), abs(unscaled.y), abs(unscaled.z), 1.0); // Default
-  auto color           = make_float4(abs(unscaled.x), abs(unscaled.z), abs(unscaled.y), 1.0); // DMRI
-
-  auto point_index = 2 * volume_index;
-  points[point_index    ] = position + vector_start; points[point_index    ].y = -points[point_index    ].y;
-  points[point_index + 1] = position + vector_end  ; points[point_index + 1].y = -points[point_index + 1].y;
-  colors[point_index    ] = color;
-  colors[point_index + 1] = color;
-}
-
-template<typename scalar_type, typename vector_type, typename color_type>
-__global__ void create_vector_field_internal(
-  const uint3        dimensions  ,
-  const vector_type* unit_vectors,
-  const vector_type  spacing     ,
-  const scalar_type  scale       ,
-        vector_type* points      ,
-        color_type*  colors      )
-{
-  auto x = blockIdx.x * blockDim.x + threadIdx.x;
-  auto y = blockIdx.y * blockDim.y + threadIdx.y;
-  auto z = blockIdx.z * blockDim.z + threadIdx.z;
-
-  if (x >= dimensions.x ||
-      y >= dimensions.y ||
-      z >= dimensions.z)
-    return;
-  
-  auto        volume_index     = z + dimensions.z * (y + dimensions.y * x);
-  vector_type vector           = unit_vectors[volume_index] / length(unit_vectors[volume_index]);
-  auto        vector_spherical = cush::to_spherical_coords(vector);
-
-  vector_type position = {
-    x * spacing.x,
-    y * spacing.y,
-    z * spacing.z};
-  
-  auto minimum_spacing = float(min(spacing.x, min(spacing.y, spacing.z)));
-  auto vector_start    = cush::to_cartesian_coords(float3{ scale * minimum_spacing / 2.0F, vector_spherical.y, vector_spherical.z});
-  auto vector_end      = cush::to_cartesian_coords(float3{-scale * minimum_spacing / 2.0F, vector_spherical.y, vector_spherical.z});
-  auto unscaled        = cush::to_cartesian_coords(float3{1.0F, vector_spherical.y, vector_spherical.z});
-  // auto color        = make_float4(abs(unscaled.x), abs(unscaled.y), abs(unscaled.z), 1.0); // Default
-  auto color           = make_float4(abs(unscaled.x), abs(unscaled.z), abs(unscaled.y), 1.0); // DMRI
-  
-  auto point_index = 2 * volume_index;
-  points[point_index    ] = position + vector_start; points[point_index    ].y = -points[point_index    ].y;
-  points[point_index + 1] = position + vector_end  ; points[point_index + 1].y = -points[point_index + 1].y;
-  colors[point_index    ] = color;
-  colors[point_index + 1] = color;
-}
-}
-
-#endif
\ No newline at end of file
diff --git a/include/io/hdf5_io.hpp b/include/io/hdf5_io.hpp
deleted file mode 100644
index 45660e29a7952faaa8fc3209c64d2db16b991c35..0000000000000000000000000000000000000000
--- a/include/io/hdf5_io.hpp
+++ /dev/null
@@ -1,217 +0,0 @@
-#ifndef PLI_IO_HDF5_IO_HPP_
-#define PLI_IO_HDF5_IO_HPP_
-
-#define H5_USE_BOOST_MULTI_ARRAY
-
-#include <array>
-#include <functional>
-#include <iostream>
-
-#include <boost/algorithm/string/replace.hpp>
-#include <boost/format.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/multi_array.hpp>
-
-#include <third_party/highfive/H5DataSet.hpp>
-#include <third_party/highfive/H5File.hpp>
-
-#include "hdf5_io_base.hpp"
-
-namespace pli
-{
-// For Vervet1818 style data (each slice is a separate dataset).
-class hdf5_io : public hdf5_io_base
-{
-public:
-  explicit hdf5_io(
-    std::string filepath,
-    std::string attribute_path_vector_spacing   = std::string(),
-    std::string attribute_path_block_size       = std::string(),
-    std::string dataset_path_mask               = std::string(),
-    std::string dataset_path_transmittance      = std::string(),
-    std::string dataset_path_retardation        = std::string(),
-    std::string dataset_path_fiber_direction    = std::string(),
-    std::string dataset_path_fiber_inclination  = std::string(),
-    std::string dataset_path_fiber_unit_vectors = std::string(),
-    std::string dataset_path_fiber_distribution = std::string())
-    : hdf5_io_base(
-    filepath                       ,
-    attribute_path_vector_spacing  ,
-    attribute_path_block_size      ,
-    dataset_path_mask              ,
-    dataset_path_transmittance     ,
-    dataset_path_retardation       ,
-    dataset_path_fiber_direction   ,
-    dataset_path_fiber_inclination ,
-    dataset_path_fiber_unit_vectors,
-    dataset_path_fiber_distribution)
-  {
-
-  }
-  
-private:
-  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_scalar_dataset_bounds(std::string dataset_path) override
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return {{0, 0, 0}, {0, 0, 0}};
-
-    auto first_slice_name = file_.listObjectNames()[0];
-    auto z_offset         = boost::lexical_cast<std::size_t>(first_slice_name);
-    auto z_size           = file_.getNumberObjects();
-    auto xy_size          = file_.getDataSet((boost::format(boost::replace_first_copy(dataset_path, "%Slice%", "%04d")) % z_offset).str()).getSpace().getDimensions();
-    return {{0, 0, z_offset}, {xy_size[0], xy_size[1], z_offset + z_size}};
-  }
-  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_vector_dataset_bounds(std::string dataset_path) override
-  {
-    std::cout << "Vector datasets are unsupported." << std::endl;
-    return {{0, 0, 0, 0}, {0, 0, 0, 0}};
-  }
-  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_tensor_dataset_bounds(std::string dataset_path) override
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return {{0, 0, 0, 0}, {0, 0, 0, 0}};
-      
-    auto first_slice_name = file_.listObjectNames()[0];
-    auto z_offset         = boost::lexical_cast<std::size_t>(first_slice_name);
-    auto z_size           = file_.getNumberObjects();
-    auto xyt_size         = file_.getDataSet((boost::format(boost::replace_first_copy(dataset_path, "%Slice%", "%04d")) % z_offset).str()).getSpace().getDimensions();
-    return {{0, 0, z_offset, 0}, {xyt_size[0], xyt_size[1], z_offset + z_size, xyt_size[2]}};
-  }
-
-  boost::multi_array<float, 3> load_scalar_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride, bool normalize) override
-  {
-    boost::multi_array<float, 3> data(boost::extents[size[0]][size[1]][size[2]]);
-
-    if (!file_.isValid() || dataset_path.empty())
-      return data;
-
-    dataset_path = boost::replace_first_copy(dataset_path, "%Slice%", "%04d");
-
-    for (std::size_t z = 0; z < size[2]; z+= stride[2])
-    {
-      boost::multi_array<float, 2> slice_data;
-      file_
-        .getDataSet((boost::format(dataset_path) % (z + offset[2])).str())
-        .select({offset[0], offset[1]}, {size[0], size[1]}, std::vector<std::size_t>{stride[0], stride[1]})
-        .read(slice_data);
-      data[boost::indices[index_range()][index_range()][z]] = slice_data;
-    }
-
-    if (normalize && data.num_elements() > 0)
-    {
-      auto max_element = *std::max_element(data.data(), data.data() + data.num_elements());
-      std::transform(data.data(), data.data() + data.num_elements(), data.data(), [max_element](const float& element)
-      {
-        return element / max_element;
-      });
-    }
-
-    return data;
-  }
-  boost::multi_array<float, 4> load_vector_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride, bool normalize) override
-  {
-    std::cout << "Vector datasets are unsupported." << std::endl;
-    return boost::multi_array<float, 4>();
-  }
-  boost::multi_array<float, 4> load_tensor_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride, bool normalize) override
-  {
-    boost::multi_array<float, 4> data(boost::extents[size[0]][size[1]][size[2]][1]);
-
-    if (!file_.isValid() || dataset_path.empty())
-      return data;
-
-    dataset_path = boost::replace_first_copy(dataset_path, "%Slice%", "%04d");
-
-    for (std::size_t z = 0; z < size[2]; z+= stride[2])
-    {
-      auto dataset = file_.getDataSet((boost::format(dataset_path) % (z + offset[2])).str());
-      auto count   = dataset.getSpace().getDimensions()[2];
-      
-      if (z == 0)
-        data.resize(boost::extents[size[0]][size[1]][size[2]][count]);
-      
-      boost::multi_array<float, 3> slice_data;
-      dataset
-        .select({offset[0], offset[1], 0}, {size[0], size[1], count}, std::vector<std::size_t>{stride[0], stride[1], 1})
-        .read  (slice_data);
-      data[boost::indices[index_range()][index_range()][z][index_range()]] = slice_data;
-    }
-
-    if (normalize && data.num_elements() > 0)
-    {
-      auto max_element = *std::max_element(data.data(), data.data() + data.num_elements());
-      std::transform(data.data(), data.data() + data.num_elements(), data.data(), [max_element](const float& element)
-      {
-        return element / max_element;
-      });
-    }
-
-    return data;
-  }
-  
-  void save_scalar_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data) override 
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return;
-
-    dataset_path = boost::replace_first_copy(dataset_path, "%Slice%", "%04d");
-
-    auto shape     = data.shape();
-    auto cast_data = const_cast<boost::multi_array<float, 3>&>(data);
-    for (auto z = 0; z < shape[2]; z++)
-    {
-      auto slice_path = (boost::format(dataset_path) % (z + offset[2])).str();
-      boost::multi_array<float, 2> slice_data(cast_data[boost::indices[index_range()][index_range()][z]]);
-      try
-      {
-        file_
-          .getDataSet   (slice_path)
-          .select       ({offset[0], offset[1]}, {shape[0], shape[1]})
-          .write        (slice_data);
-      }
-      catch (...)
-      {
-        file_
-          .createDataSet(slice_path, HighFive::DataSpace({offset[0] + shape[0], offset[1] + shape[1]}), HighFive::AtomicType<float>())
-          .select       ({offset[0], offset[1]}, {shape[0], shape[1]})
-          .write        (slice_data);
-      }
-    }
-  } 
-  void save_vector_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data) override
-  {
-    std::cout << "Vector datasets are unsupported." << std::endl;
-  }
-  void save_tensor_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data) override 
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return;
-
-    dataset_path = boost::replace_first_copy(dataset_path, "%Slice%", "%04d");
-
-    auto shape     = data.shape();
-    auto cast_data = const_cast<boost::multi_array<float, 4>&>(data);
-    for (auto z = 0; z < shape[2]; z++)
-    {
-      auto slice_path = (boost::format(dataset_path) % (z + offset[2])).str();
-      boost::multi_array<float, 3> slice_data(cast_data[boost::indices[index_range()][index_range()][z][index_range()]]);
-      try
-      {
-        file_
-          .getDataSet   (slice_path)
-          .select       ({offset[0], offset[1], 0}, {shape[0], shape[1], shape[3]})
-          .write        (slice_data);
-      }
-      catch (...)
-      {
-        file_
-          .createDataSet(slice_path, HighFive::DataSpace({offset[0] + shape[0], offset[1] + shape[1], shape[3]}), HighFive::AtomicType<float>())
-          .select       ({offset[0], offset[1], 0}, {shape[0], shape[1], shape[3]})
-          .write        (slice_data);
-      }
-    }
-  }
-};
-}
-
-#endif
diff --git a/include/io/hdf5_io_2.hpp b/include/io/hdf5_io_2.hpp
deleted file mode 100644
index 1d993d82f3e727299d0b157a860ef26bcf3519ae..0000000000000000000000000000000000000000
--- a/include/io/hdf5_io_2.hpp
+++ /dev/null
@@ -1,201 +0,0 @@
-#ifndef PLI_IO_HDF5_IO_2_HPP_
-#define PLI_IO_HDF5_IO_2_HPP_
-
-#define H5_USE_BOOST_MULTI_ARRAY
-
-#include <array>
-#include <functional>
-
-#include <boost/multi_array.hpp>
-
-#include <third_party/highfive/H5DataSet.hpp>
-#include <third_party/highfive/H5File.hpp>
-
-#include "hdf5_io_base.hpp"
-
-namespace pli
-{
-// For MSA0309 style data (Volume. ZXY for scalars, ZVXY for vectors, ZTXY for tensors).
-class hdf5_io_2 : public hdf5_io_base
-{
-public:
-  explicit hdf5_io_2(
-    std::string filepath,
-    std::string attribute_path_vector_spacing   = std::string(),
-    std::string attribute_path_block_size       = std::string(),
-    std::string dataset_path_mask               = std::string(),
-    std::string dataset_path_transmittance      = std::string(),
-    std::string dataset_path_retardation        = std::string(),
-    std::string dataset_path_fiber_direction    = std::string(),
-    std::string dataset_path_fiber_inclination  = std::string(),
-    std::string dataset_path_fiber_unit_vectors = std::string(),
-    std::string dataset_path_fiber_distribution = std::string())
-    : hdf5_io_base(
-      filepath                       ,
-      attribute_path_vector_spacing  ,
-      attribute_path_block_size      ,
-      dataset_path_mask              ,
-      dataset_path_transmittance     ,
-      dataset_path_retardation       ,
-      dataset_path_fiber_direction   ,
-      dataset_path_fiber_inclination ,
-      dataset_path_fiber_unit_vectors,
-      dataset_path_fiber_distribution)
-  {
-
-  }
-  
-private:
-  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_scalar_dataset_bounds(std::string dataset_path) override
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return {{0, 0, 0}, {0, 0, 0}};
-    auto misordered_size = file_.getDataSet(dataset_path).getSpace().getDimensions();
-    return {{0, 0, 0}, {misordered_size[1], misordered_size[2], misordered_size[0]}};
-  }
-  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_vector_dataset_bounds(std::string dataset_path) override
-  {
-    return load_tensor_dataset_bounds(dataset_path);
-  }
-  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_tensor_dataset_bounds(std::string dataset_path) override
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return {{0, 0, 0, 0}, {0, 0, 0, 0}};
-    auto misordered_size = file_.getDataSet(dataset_path).getSpace().getDimensions();
-    return {{0, 0, 0, 0}, {misordered_size[2], misordered_size[3], misordered_size[0], misordered_size[1]}};
-  }
-
-  boost::multi_array<float, 3> load_scalar_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride, bool normalize) override
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return boost::multi_array<float, 3>();
-
-    boost::multi_array<float, 3> misordered_data;
-    file_
-      .getDataSet(dataset_path)
-      .select    ({offset[2], offset[0], offset[1]}, {size[2], size[0], size[1]}, std::vector<std::size_t>{stride[0], stride[1], stride[2]})
-      .read      (misordered_data);
-
-    // Convert ZXY to XYZ.
-    boost::multi_array<float, 3> data(boost::extents[size[0]][size[1]][size[2]]);
-    for (auto x = 0; x < size[0]; x++)
-      for (auto y = 0; y < size[1]; y++)
-        for (auto z = 0; z < size[2]; z++)
-          data[x][y][z] = misordered_data[z][x][y];
-
-    if (normalize && data.num_elements() > 0)
-    {
-      auto max_element = *std::max_element(data.data(), data.data() + data.num_elements());
-      std::transform(data.data(), data.data() + data.num_elements(), data.data(), [max_element](const float& element)
-      {
-        return element / max_element;
-      });
-    }
-
-    return data;
-  }
-  boost::multi_array<float, 4> load_vector_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride, bool normalize) override
-  {
-    return load_tensor_dataset(dataset_path, offset, size, stride, normalize);
-  }
-  boost::multi_array<float, 4> load_tensor_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride, bool normalize) override
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return boost::multi_array<float, 4>();
-
-    auto dataset = file_.getDataSet(dataset_path);
-    auto count   = dataset.getSpace().getDimensions()[1];
-
-    boost::multi_array<float, 4> misordered_data;
-    dataset
-      .select({offset[2], 0, offset[0], offset[1]}, {size[2], count, size[0], size[1]}, std::vector<std::size_t>{stride[2], 1, stride[0], stride[1]})
-      .read  (misordered_data);
-
-    // Convert ZVXY to XYZV.
-    boost::multi_array<float, 4> data(boost::extents[size[0]][size[1]][size[2]][count]);
-    for (auto x = 0; x < size[0]; x++)
-      for (auto y = 0; y < size[1]; y++)
-        for (auto z = 0; z < size[2]; z++)
-          for (auto v = 0; v < count; v++)
-            data[x][y][z][v] = misordered_data[z][v][x][y];
-
-    if (normalize && data.num_elements() > 0)
-    {
-      auto max_element = *std::max_element(data.data(), data.data() + data.num_elements());
-      std::transform(data.data(), data.data() + data.num_elements(), data.data(), [max_element](const float& element)
-      {
-        return element / max_element;
-      });
-    }
-
-    return data;
-  }
-  
-  void save_scalar_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data) override 
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return;
-
-    auto shape = data.shape();
-
-    // Convert XYZ to ZXY.
-    boost::multi_array<float, 3> misordered_data(boost::extents[shape[2]][shape[0]][shape[1]]);
-    for (auto x = 0; x < shape[0]; x++)
-      for (auto y = 0; y < shape[1]; y++)
-        for (auto z = 0; z < shape[2]; z++)
-          misordered_data[z][x][y] = data[x][y][z];
-
-    try
-    {
-      file_
-        .getDataSet   (dataset_path)
-        .select       ({offset[2], offset[0], offset[1]}, {shape[2], shape[0], shape[1]})
-        .write        (misordered_data);
-    }
-    catch (...)
-    {
-      file_
-        .createDataSet(dataset_path, HighFive::DataSpace({offset[2] + shape[2], offset[0] + shape[0], offset[1] + shape[1]}), HighFive::AtomicType<float>())
-        .select       ({offset[2], offset[0], offset[1]}, {shape[2], shape[0], shape[1]})
-        .write        (misordered_data);
-    }
-
-  } 
-  void save_vector_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data) override
-  {
-    save_tensor_dataset(dataset_path, offset, data);
-  }
-  void save_tensor_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data) override 
-  {
-    if (!file_.isValid() || dataset_path.empty())
-      return;
-
-    auto shape = data.shape();
-
-    // Convert XYZV to ZVXY.
-    boost::multi_array<float, 4> misordered_data(boost::extents[shape[2]][shape[3]][shape[0]][shape[1]]);
-    for (auto x = 0; x < shape[0]; x++)
-      for (auto y = 0; y < shape[1]; y++)
-        for (auto z = 0; z < shape[2]; z++)
-          for (auto v = 0; v < shape[3]; v++)
-            misordered_data[z][v][x][y] = data[x][y][z][v];
-
-    try
-    {
-      file_
-        .getDataSet   (dataset_path)
-        .select       ({offset[2], 0, offset[0], offset[1]}, {shape[2], shape[3], shape[0], shape[1]})
-        .write        (misordered_data);
-    }
-    catch (...)
-    {
-      file_
-        .createDataSet(dataset_path, HighFive::DataSpace({offset[2] + shape[2], shape[3], offset[0] + shape[0], offset[1] + shape[1]}), HighFive::AtomicType<float>())
-        .select       ({offset[2], 0, offset[0], offset[1]}, {shape[2], shape[3], shape[0], shape[1]})
-        .write        (misordered_data);
-    }
-  }
-};
-}
-
-#endif
diff --git a/include/io/hdf5_io_base.hpp b/include/io/hdf5_io_base.hpp
deleted file mode 100644
index ad85a678738bafad574cd85c3934daa0e6bc8ef9..0000000000000000000000000000000000000000
--- a/include/io/hdf5_io_base.hpp
+++ /dev/null
@@ -1,295 +0,0 @@
-#ifndef PLI_IO_HDF5_IO_COMMON_HPP_
-#define PLI_IO_HDF5_IO_COMMON_HPP_
-
-#define H5_USE_BOOST_MULTI_ARRAY
-
-#include <array>
-#include <string>
-
-#include <boost/multi_array.hpp>
-
-#include <third_party/highfive/H5Attribute.hpp>
-#include <third_party/highfive/H5File.hpp>
-
-namespace pli
-{
-class hdf5_io_base
-{
-public:
-  explicit hdf5_io_base(
-    std::string filepath,
-    std::string attribute_path_vector_spacing   = std::string(),
-    std::string attribute_path_block_size       = std::string(),
-    std::string dataset_path_mask               = std::string(),
-    std::string dataset_path_transmittance      = std::string(),
-    std::string dataset_path_retardation        = std::string(),
-    std::string dataset_path_fiber_direction    = std::string(),
-    std::string dataset_path_fiber_inclination  = std::string(),
-    std::string dataset_path_fiber_unit_vectors = std::string(),
-    std::string dataset_path_fiber_distribution = std::string())
-    : filepath_                        (filepath                       )
-    , attribute_path_vector_spacing_   (attribute_path_vector_spacing  )
-    , attribute_path_block_size_       (attribute_path_block_size      )
-    , dataset_path_mask_               (dataset_path_mask              )
-    , dataset_path_transmittance_      (dataset_path_transmittance     )
-    , dataset_path_retardation_        (dataset_path_retardation       )
-    , dataset_path_fiber_direction_    (dataset_path_fiber_direction   )
-    , dataset_path_fiber_inclination_  (dataset_path_fiber_inclination )
-    , dataset_path_fiber_unit_vectors_ (dataset_path_fiber_unit_vectors)
-    , dataset_path_fiber_distribution_ (dataset_path_fiber_distribution)
-    , file_                            (filepath, HighFive::File::ReadWrite)
-  {
-
-  }
-  virtual ~hdf5_io_base() = default;
-  
-  void set_filepath                       (const std::string& filepath                       )
-  {
-    filepath_ = filepath;
-    file_     = HighFive::File(filepath_, HighFive::File::ReadWrite);
-  }
-  void set_attribute_path_vector_spacing  (const std::string& attribute_path_vector_spacing  )
-  {
-    attribute_path_vector_spacing_   = attribute_path_vector_spacing;
-  }
-  void set_attribute_path_block_size      (const std::string& attribute_path_block_size      )
-  {
-    attribute_path_block_size_ = attribute_path_block_size;
-  }
-  void set_dataset_path_mask              (const std::string& dataset_path_mask              )
-  {
-    dataset_path_mask_               = dataset_path_mask;
-  }
-  void set_dataset_path_transmittance     (const std::string& dataset_path_transmittance     )
-  {
-    dataset_path_transmittance_      = dataset_path_transmittance;
-  }
-  void set_dataset_path_retardation       (const std::string& dataset_path_retardation       )
-  {
-    dataset_path_retardation_        = dataset_path_retardation;
-  }
-  void set_dataset_path_fiber_direction   (const std::string& dataset_path_fiber_direction   )
-  {
-    dataset_path_fiber_direction_    = dataset_path_fiber_direction;
-  }
-  void set_dataset_path_fiber_inclination (const std::string& dataset_path_fiber_inclination )
-  {
-    dataset_path_fiber_inclination_  = dataset_path_fiber_inclination;
-  }
-  void set_dataset_path_fiber_unit_vectors(const std::string& dataset_path_fiber_unit_vectors)
-  {
-    dataset_path_fiber_unit_vectors_ = dataset_path_fiber_unit_vectors;
-  }
-  void set_dataset_path_fiber_distribution(const std::string& dataset_path_fiber_distribution)
-  {
-    dataset_path_fiber_distribution_ = dataset_path_fiber_distribution;
-  }
-
-  const std::string& filepath                       () const
-  {
-    return filepath_;
-  }
-  const std::string& attribute_path_vector_spacing  () const
-  {
-    return attribute_path_vector_spacing_;
-  }
-  const std::string& attribute_path_block_size      () const
-  {
-    return attribute_path_block_size_;
-  }
-  const std::string& dataset_path_mask              () const
-  {
-    return dataset_path_mask_;
-  }
-  const std::string& dataset_path_transmittance     () const
-  {
-    return dataset_path_transmittance_;
-  }
-  const std::string& dataset_path_retardation       () const
-  {
-    return dataset_path_retardation_;
-  }
-  const std::string& dataset_path_fiber_direction   () const
-  {
-    return dataset_path_fiber_direction_;
-  }
-  const std::string& dataset_path_fiber_inclination () const
-  {
-    return dataset_path_fiber_inclination_;
-  }
-  const std::string& dataset_path_fiber_unit_vectors() const
-  {
-    return dataset_path_fiber_unit_vectors_;
-  }
-  const std::string& dataset_path_fiber_distribution() const
-  {
-    return dataset_path_fiber_distribution_;
-  }
-
-  std::array<float      , 3> load_vector_spacing()
-  {
-    return load_attribute<std::array<float, 3>>(attribute_path_vector_spacing_);
-  }
-  std::array<std::size_t, 3> load_block_size    ()
-  {
-    return load_attribute<std::array<std::size_t, 3>>(attribute_path_block_size_);
-  }
-
-  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_mask_dataset_bounds              ()
-  {
-    return load_scalar_dataset_bounds(dataset_path_mask_);
-  }
-  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_transmittance_dataset_bounds     ()
-  {
-    return load_scalar_dataset_bounds(dataset_path_transmittance_);
-  }
-  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_retardation_dataset_bounds       ()
-  {
-    return load_scalar_dataset_bounds(dataset_path_retardation_);
-  }
-  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_fiber_direction_dataset_bounds   ()
-  {
-    return load_scalar_dataset_bounds(dataset_path_fiber_direction_);
-  }
-  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_fiber_inclination_dataset_bounds ()
-  {
-    return load_scalar_dataset_bounds(dataset_path_fiber_inclination_);
-  }
-  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_fiber_unit_vectors_dataset_bounds()
-  {
-    return load_vector_dataset_bounds(dataset_path_fiber_unit_vectors_);
-  }
-  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_fiber_distribution_dataset_bounds()
-  {
-    return load_tensor_dataset_bounds(dataset_path_fiber_distribution_);
-  }
-
-  boost::multi_array<float, 3> load_mask_dataset              (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true)
-  {
-    return load_scalar_dataset(dataset_path_mask_              , offset, size, stride, normalize);
-  }
-  boost::multi_array<float, 3> load_transmittance_dataset     (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true)
-  {
-    return load_scalar_dataset(dataset_path_transmittance_     , offset, size, stride, normalize);
-  } 
-  boost::multi_array<float, 3> load_retardation_dataset       (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true)
-  {
-    return load_scalar_dataset(dataset_path_retardation_       , offset, size, stride, normalize);
-  }
-  boost::multi_array<float, 3> load_fiber_direction_dataset   (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true)
-  {
-    return load_scalar_dataset(dataset_path_fiber_direction_   , offset, size, stride, normalize);
-  }
-  boost::multi_array<float, 3> load_fiber_inclination_dataset (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true)
-  {
-    return load_scalar_dataset(dataset_path_fiber_inclination_ , offset, size, stride, normalize);
-  } 
-  boost::multi_array<float, 4> load_fiber_unit_vectors_dataset(const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true)
-  {
-    return load_vector_dataset(dataset_path_fiber_unit_vectors_, offset, size, stride, normalize);
-  }
-  boost::multi_array<float, 4> load_fiber_distribution_dataset(const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true)
-  {
-    return load_tensor_dataset(dataset_path_fiber_distribution_, offset, size, stride, normalize);
-  }
- 
-  void save_vector_spacing(const std::array<float      , 3>& data)
-  {
-    return save_attribute(attribute_path_vector_spacing_, data);
-  }    
-  void save_block_size    (const std::array<std::size_t, 3>& data)
-  {
-    return save_attribute(attribute_path_block_size_    , data);
-  }  
-  
-  void save_mask_dataset              (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
-  {
-    save_scalar_dataset(dataset_path_mask_              , offset, data);
-  }  
-  void save_transmittance_dataset     (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
-  {
-    save_scalar_dataset(dataset_path_transmittance_     , offset, data);
-  }
-  void save_retardation_dataset       (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
-  {
-    save_scalar_dataset(dataset_path_retardation_       , offset, data);
-  }
-  void save_fiber_direction_dataset   (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
-  {
-    save_scalar_dataset(dataset_path_fiber_direction_   , offset, data);
-  }
-  void save_fiber_inclination_dataset (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
-  {
-    save_scalar_dataset(dataset_path_fiber_inclination_ , offset, data);
-  }
-  void save_fiber_unit_vectors_dataset(const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data)
-  {
-    save_vector_dataset(dataset_path_fiber_unit_vectors_, offset, data);
-  }
-  void save_fiber_distribution_dataset(const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data)
-  {
-    save_tensor_dataset(dataset_path_fiber_distribution_, offset, data);
-  }
-   
-protected:
-  typedef boost::multi_array_types::index_range index_range;
-
-  virtual std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_scalar_dataset_bounds(std::string dataset_path) = 0;
-  virtual std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_vector_dataset_bounds(std::string dataset_path) = 0;
-  virtual std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_tensor_dataset_bounds(std::string dataset_path) = 0;
-
-  virtual boost::multi_array<float, 3> load_scalar_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1, 1, 1}, bool normalize = true) = 0;
-  virtual boost::multi_array<float, 4> load_vector_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1, 1, 1}, bool normalize = true) = 0;
-  virtual boost::multi_array<float, 4> load_tensor_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1, 1, 1}, bool normalize = true) = 0;
-
-  virtual void save_scalar_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data) = 0;
-  virtual void save_vector_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data) = 0;
-  virtual void save_tensor_dataset(std::string dataset_path, const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data) = 0;
-
-  template<typename attribute_type>
-  attribute_type load_attribute(std::string attribute_path)
-  {
-    attribute_type attribute;
-    if (file_.isValid() && !attribute_path.empty())
-      file_.getAttribute(attribute_path).read(attribute);
-    return attribute;
-  }
-
-  template<typename attribute_type, std::size_t size>
-  void save_attribute(std::string attribute_path, const std::array<attribute_type, size>& attribute)
-  {
-    if (!file_.isValid() || attribute_path.empty())
-      return;
-
-    auto cast_attribute = const_cast<std::array<attribute_type, size>&>(attribute);
-
-    try
-    {
-      file_
-        .getAttribute   (attribute_path)
-        .write          (cast_attribute);
-    }
-    catch (...)
-    {
-      file_
-        .createAttribute(attribute_path, HighFive::DataSpace(size), HighFive::AtomicType<attribute_type>())
-        .write          (cast_attribute);
-    }
-  }
-
-  std::string filepath_;
-  std::string attribute_path_vector_spacing_;
-  std::string attribute_path_block_size_;
-  std::string dataset_path_mask_;
-  std::string dataset_path_transmittance_;
-  std::string dataset_path_retardation_;
-  std::string dataset_path_fiber_direction_;
-  std::string dataset_path_fiber_inclination_;
-  std::string dataset_path_fiber_unit_vectors_;
-  std::string dataset_path_fiber_distribution_;
-
-  HighFive::File file_;
-};
-}
-
-#endif
diff --git a/include/io/hdf5_io_selector.hpp b/include/io/hdf5_io_selector.hpp
deleted file mode 100644
index 0a390e2dce7ca17a0fbd764f9d81d97d2c981090..0000000000000000000000000000000000000000
--- a/include/io/hdf5_io_selector.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef PLI_IO_HDF5_IO_SELECTOR_HPP_
-#define PLI_IO_HDF5_IO_SELECTOR_HPP_
-
-#include <string>
-#include <memory>
-
-#include "hdf5_io.hpp"
-#include "hdf5_io_2.hpp"
-#include "hdf5_io_base.hpp"
-
-namespace pli
-{
-class hdf5_io_selector
-{
-public:
-  static std::unique_ptr<hdf5_io_base> select(const std::string& filepath)
-  {
-    if (filepath.substr(0, 3) == "MSA")
-      return std::make_unique<hdf5_io_2>(filepath);
-    else
-      return std::make_unique<hdf5_io>  (filepath);
-  }
-};
-}
-
-#endif
diff --git a/include/math/camera.hpp b/include/math/camera.hpp
deleted file mode 100644
index 7200b5b421d02ec939b932ac8b83cec7cc0962ab..0000000000000000000000000000000000000000
--- a/include/math/camera.hpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef PLI_VIS_CAMERA_HPP_
-#define PLI_VIS_CAMERA_HPP_
-
-#include "transform.hpp"
-
-namespace pli
-{
-/// A camera is a view transform with an additional projection matrix.
-/// Supports perspective and orthographic projection.
-class camera : public transform
-{
-public:
-  camera();
-
-  bool         orthographic             () const { return orthographic_     ; }
-  float	       near_clip_plane          () const { return near_clip_plane_  ; }
-  float	       far_clip_plane           () const { return far_clip_plane_   ; }
-  float	       aspect_ratio             () const { return aspect_ratio_     ; }
-  float	       vertical_fov             () const { return vertical_fov_     ; }
-  float	       orthographic_size        () const { return orthographic_size_; }
-  
-  const mat4f& projection_matrix        () const { return projection_matrix_; }
-        mat4f  view_projection_matrix   () const { return projection_matrix_ * inverse_absolute_matrix(); }
-
-  void         set_orthographic         (bool  orthographic     );
-  void         set_near_clip_plane      (float near_clip_plane  );
-  void         set_far_clip_plane       (float far_clip_plane   );
-  void         set_aspect_ratio         (float aspect_ratio     );
-  void         set_vertical_fov         (float vertical_fov     );
-  void         set_orthographic_size    (float orthographic_size);
-
-private:
-  void         update_projection_matrix ();
-
-  /// Toggle for orthographic / perspective. Default perspective.
-  bool  orthographic_      = false;
- 
-  /// Shared parameters.
-  float	near_clip_plane_   = 0.0001F;
-  float	far_clip_plane_    = 1000.0F;
-  float	aspect_ratio_      = 4.0F / 3.0F;
-
-  /// When camera is perspective , camera's viewing volume is defined by vertical fov.
-  float	vertical_fov_      = radians(68.0F);
-  
-  /// When camera is orthographic, camera's viewing volume is defined by orthographic size.
-  float	orthographic_size_ = 1;
-
-  mat4f projection_matrix_;
-};
-}
-
-#endif
diff --git a/include/math/linear_math.hpp b/include/math/linear_math.hpp
deleted file mode 100644
index cac5415bd0e77c121b4d15fcb419b263842d3429..0000000000000000000000000000000000000000
--- a/include/math/linear_math.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef PLI_VIS_LINEAR_MATH_HPP_
-#define PLI_VIS_LINEAR_MATH_HPP_
-
-#include <third_party/glm/glm.hpp>
-#include <third_party/glm/gtc/quaternion.hpp>
-#include <third_party/glm/gtx/transform.hpp>
-#include <third_party/glm/gtx/quaternion.hpp>
-
-namespace pli
-{
-using vec2f  = glm::fvec2;
-using vec3f  = glm::fvec3;
-using vec4f  = glm::fvec4;
-using vec2d  = glm::dvec2;
-using vec3d  = glm::dvec3;
-using vec4d  = glm::dvec4;
-using vec2i  = glm::ivec2;
-using vec3i  = glm::ivec3;
-using vec4i  = glm::ivec4;
-using vec2ui = glm::uvec2;
-using vec3ui = glm::uvec3;
-using vec4ui = glm::uvec4;
-
-using mat2f  = glm::fmat2;
-using mat3f  = glm::fmat3;
-using mat4f  = glm::fmat4;
-using mat2d  = glm::dmat2;
-using mat3d  = glm::dmat3;
-using mat4d  = glm::dmat4;
-             
-using quatf  = glm::fquat;
-using quatd  = glm::dquat;
-
-using glm::angleAxis;
-using glm::eulerAngles;
-using glm::inverse;
-using glm::length;
-using glm::lerp;
-using glm::lookAt;
-using glm::mat4_cast;
-using glm::rotation;
-
-using glm::degrees;
-using glm::radians;
-
-using glm::translate;
-using glm::rotate;
-using glm::scale;
-
-using glm::ortho;
-using glm::perspective;
-}
-
-#endif
diff --git a/include/math/transform.hpp b/include/math/transform.hpp
deleted file mode 100644
index fc02c87493b026a6e2162ac59dfe3ed3e8b33b7f..0000000000000000000000000000000000000000
--- a/include/math/transform.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef PLI_VIS_TRANSFORM_HPP_
-#define PLI_VIS_TRANSFORM_HPP_
-
-#include <vector>
-
-#include "linear_math.hpp"
-
-namespace pli
-{
-/// Represents a transformation in 3D space. Supports hierarchies.
-class transform
-{
-public:
-  transform(const vec3f& translation                 , const quatf& rotation       = quatf(0, 0, 0, 1), const vec3f& scale = vec3f(1, 1, 1));
-  transform(const vec3f& translation = vec3f(0, 0, 0), const vec3f& rotation_euler = vec3f(0, 0, 0)   , const vec3f& scale = vec3f(1, 1, 1));
-  
-  const vec3f& translation             () const { return translation_                    ;}
-  const quatf& rotation                () const { return rotation_                       ;}
-        vec3f  rotation_euler          () const { return degrees(eulerAngles(rotation_)) ;}
-  const vec3f& scale                   () const { return scale_                          ;}
-                                       
-  const mat4f& matrix                  () const { return matrix_                         ;}
-        mat4f  inverse_matrix          () const { return inverse(matrix_)                ;}
-  const mat4f& absolute_matrix         () const { return absolute_matrix_                ;}
-        mat4f  inverse_absolute_matrix () const { return inverse(absolute_matrix_)       ;}
-                                       
-        vec3f  forward                 () const { return rotation_ * vec3f(0, 0, 1)      ;}
-        vec3f  up                      () const { return rotation_ * vec3f(0, 1, 0)      ;}
-        vec3f  right                   () const { return rotation_ * vec3f(1, 0, 0)      ;}
-                                       
-  transform*   parent                  () const { return parent_                         ;}
-  std::size_t  child_count             () const { return children_.size()                ;}
-                                       
-  transform&   set_translation         (const vec3f& translation   );
-  transform&   set_rotation            (const quatf& rotation      );
-  transform&   set_rotation_euler      (const vec3f& rotation_euler);
-  transform&   set_scale               (const vec3f& scale         );
-                                       
-  transform&   translate               (const vec3f& amount);
-  transform&   rotate                  (const quatf& amount);
-  transform&   look_at                 (const vec3f& target, const vec3f& up_vector = vec3f(0, 1, 0));
-                                       
-  void         set_parent              (transform*  parent);
-  transform*   child                   (std::size_t index ) const;
-
-protected:
-  void         update_matrix           ();
-  void         update_absolute_matrix  ();
-
-  vec3f                   translation_;   
-  quatf                   rotation_;      
-  vec3f                   scale_;         
-
-  mat4f                   matrix_;        
-  mat4f                   absolute_matrix_;
-
-  transform*              parent_ = nullptr;
-  std::vector<transform*> children_;
-};
-}
-
-#endif
diff --git a/include/attributes/loggable.hpp b/include/pli_vis/aspects/loggable.hpp
similarity index 81%
rename from include/attributes/loggable.hpp
rename to include/pli_vis/aspects/loggable.hpp
index be7861ede3886b9683bbc4e5e423020971bf0b65..bb9ff4b260ef6105ac392963ea644c9ab6e6cdff 100644
--- a/include/attributes/loggable.hpp
+++ b/include/pli_vis/aspects/loggable.hpp
@@ -2,7 +2,6 @@
 #define PLI_VIS_LOGGABLE_HPP_
 
 #include <spdlog/spdlog.h>
-#include <spdlog/logger.h>
 
 namespace pli
 {
@@ -10,7 +9,7 @@ template<typename type>
 class loggable
 {
 public:
-  loggable(std::string name = std::string(), std::shared_ptr<spdlog::sinks::sink> sink = nullptr)
+  explicit loggable(std::string name = std::string(), std::shared_ptr<spdlog::sinks::sink> sink = nullptr)
   {
     if (name.empty())
       name = typeid(type).name();
@@ -19,6 +18,7 @@ public:
     if (!logger_)
       logger_ = sink ? spdlog::create(name, sink) : spdlog::stdout_logger_mt(name);
   }
+  virtual ~loggable() = default;
 
   void set_sink(std::shared_ptr<spdlog::sinks::sink> sink)
   {
diff --git a/include/attributes/renderable.hpp b/include/pli_vis/aspects/renderable.hpp
similarity index 50%
rename from include/attributes/renderable.hpp
rename to include/pli_vis/aspects/renderable.hpp
index 3fa9cd2e9254dad34790a9a872bdb35cbcb6e4af..8cd7239268fe0cdfa7d0d5fb0e39dc0111312bdf 100644
--- a/include/attributes/renderable.hpp
+++ b/include/pli_vis/aspects/renderable.hpp
@@ -1,11 +1,13 @@
 #ifndef PLI_VIS_RENDERABLE_HPP_
 #define PLI_VIS_RENDERABLE_HPP_
 
+#include <pli_vis/visualization/primitives/transform.hpp>
+
 namespace pli
 {
 class camera;
 
-class renderable
+class renderable : public transform
 {
 public:
   virtual ~renderable() = default;
@@ -22,8 +24,18 @@ public:
     return active_;
   }
 
+  void set_color_mapping(int mode, float k, bool inverted)
+  {
+    color_mode_     = mode;
+    color_k_        = k;
+    color_inverted_ = inverted;
+  }
+
 protected:
-  bool active_ = true;
+  bool  active_         = true;
+  int   color_mode_     = 0;
+  float color_k_        = 0.5f;
+  bool  color_inverted_ = false;
 };
 }
 
diff --git a/include/pli_vis/cuda/global_tractography.h b/include/pli_vis/cuda/global_tractography.h
new file mode 100644
index 0000000000000000000000000000000000000000..5983d3d139b74f60651cbfe74bfb81eda9169883
--- /dev/null
+++ b/include/pli_vis/cuda/global_tractography.h
@@ -0,0 +1,171 @@
+#ifndef PLI_VIS_GLOBAL_TRACTOGRAPHY_H_
+#define PLI_VIS_GLOBAL_TRACTOGRAPHY_H_
+
+#include <vector_types.h>
+
+#include <pli_vis/cuda/sh/spherical_harmonics.h>
+#include <pli_vis/cuda/utility/vector_ops.h>
+
+namespace pli
+{
+// Based on the following:
+// - Global reconstruction of neuronal fibres      (Reisert et al. 2009)
+// - Global fiber reconstruction becomes practical (Reisert et al. 2011)
+template<
+  typename scalar_precision = float , 
+  typename vector_precision = float3>
+__host__ __device__ scalar_precision interaction_potential(
+  const vector_precision& lhs_position    ,
+  const vector_precision& lhs_orientation ,
+  const vector_precision& rhs_position    ,
+  const vector_precision& rhs_orientation ,
+  const scalar_precision& bias            ,
+  const scalar_precision& length          = 1)
+{
+  auto midpoint    = (lhs_position + rhs_position) / 2;
+  auto lhs_sign    = dot(lhs_orientation, midpoint - lhs_position) >= 0 ? 1 : -1;
+  auto rhs_sign    = dot(rhs_orientation, midpoint - rhs_position) >= 0 ? 1 : -1;
+  auto lhs_segment = lhs_position + lhs_sign * length * lhs_orientation;
+  auto rhs_segment = rhs_position + rhs_sign * length * rhs_orientation;
+  auto lhs_diff    = lhs_segment - midpoint;
+  auto rhs_diff    = rhs_segment - midpoint;
+  auto lhs_sq_dist = pow(lhs_diff.x, 2) + pow(lhs_diff.y, 2) + pow(lhs_diff.z, 2);
+  auto rhs_sq_dist = pow(rhs_diff.x, 2) + pow(rhs_diff.y, 2) + pow(rhs_diff.z, 2);
+  return (1.0 / pow(length, 2)) * (lhs_sq_dist  + rhs_sq_dist) - bias;
+}
+
+template<
+  typename scalar_precision = float , 
+  typename vector_precision = float3>
+__host__ __device__ scalar_precision internal_energy(
+  const unsigned          count           , 
+  const vector_precision* lhs_positions   , 
+  const vector_precision* lhs_orientations,
+  const vector_precision* rhs_positions   ,
+  const vector_precision* rhs_orientations,
+  const scalar_precision& bias            ,
+  const scalar_precision& length          = 1)
+{
+  scalar_precision value(0);
+  for(auto i = 0; i < count; ++i)
+    value += interaction_potential(
+      lhs_positions   [i], 
+      lhs_orientations[i], 
+      rhs_positions   [i], 
+      rhs_orientations[i], 
+      bias,
+      length);
+  return value;
+}
+
+template<
+  typename scalar_precision = float ,
+  typename vector_precision = float3>
+__host__ __device__ void energy_minimizing_configuration(
+  const vector_precision& position        ,
+  const vector_precision& direction       ,
+        vector_precision& out_position    ,
+        vector_precision& out_direction   ,
+  const scalar_precision& length          = 1)
+{
+  out_position  = position + 2 * length * direction;
+  out_direction = direction;
+}
+
+template<
+  typename scalar_precision = float ,
+  typename vector_precision = float3>
+__host__ __device__ void energy_minimizing_configuration(
+  const vector_precision& lhs_position    ,
+  const vector_precision& lhs_direction   ,
+  const vector_precision& rhs_position    ,
+  const vector_precision& rhs_direction   ,
+        vector_precision& out_position    ,
+        vector_precision& out_direction   ,
+  const scalar_precision& length          = 1)
+{
+  out_position  = (lhs_position + length * lhs_direction + rhs_position + length * rhs_direction) / 2;
+  out_direction = normalize(rhs_position - lhs_position);
+}
+
+template<
+  typename scalar_precision = float ,
+  typename vector_precision = float3>
+__host__ __device__ scalar_precision predicted_signal(
+  const vector_precision& position        ,
+  const vector_precision& direction       ,
+  const unsigned          count           ,
+  const vector_precision* positions       ,
+  const vector_precision* directions      ,
+  const scalar_precision& weight          ,
+  const scalar_precision& c               ,
+  const scalar_precision& sigma           )
+{
+  scalar_precision sum(0);
+  for(auto i = 0; i < count; ++i)
+  {
+    scalar_precision dot_v  = dot(direction, directions[i]);
+    vector_precision diff_x = position - positions[i];
+    sum += exp(-c * pow(dot_v, 2)) * exp(-dot(diff_x, diff_x) / pow(sigma, 2));
+  }
+  return weight * sum;
+}
+
+template<
+  typename scalar_precision = float ,
+  typename vector_precision = float3>  
+__host__ __device__ void predicted_signal(
+  const vector_precision& position        ,
+  const unsigned          bin_count       ,
+  const vector_precision* bin_directions  , 
+        scalar_precision* bin_magnitudes  ,
+  const unsigned          count           ,
+  const vector_precision* positions       ,
+  const vector_precision* directions      ,
+  const scalar_precision& weight          ,
+  const scalar_precision& c               ,
+  const scalar_precision& sigma           )
+{
+  for(auto i = 0; i < bin_count; ++i)
+  {
+    bin_magnitudes[i] = predicted_signal(
+      position         , 
+      bin_directions[i], 
+      count            , 
+      positions        , 
+      directions       , 
+      weight           , 
+      c                , 
+      sigma            );
+  }
+}
+
+template<
+  typename scalar_precision = float>
+__host__ __device__ void external_energy(
+  const unsigned          odf_count       ,
+  const unsigned          odf_max_degree  ,
+  const scalar_precision* original_odfs   ,
+  const scalar_precision* predicted_odfs  ,
+        scalar_precision* out_energies    )
+{
+  auto odf_coefficient_count = coefficient_count(odf_max_degree);
+  for(auto i = 0; i < odf_count; ++i)
+  {
+    auto start_index = odf_coefficient_count * i;
+    out_energies[i]  = l2_distance(odf_coefficient_count, original_odfs[start_index], predicted_odfs[start_index]);
+  }
+}
+
+template<
+  typename scalar_precision = float>
+__host__ __device__ scalar_precision posterior_probability(
+  const scalar_precision& internal_energy,
+  const scalar_precision& external_energy,
+  const scalar_precision& temperature    )
+{
+  return exp((- internal_energy - external_energy) / temperature);
+}
+}
+
+#endif
diff --git a/include/pli_vis/cuda/odf_field.h b/include/pli_vis/cuda/odf_field.h
new file mode 100644
index 0000000000000000000000000000000000000000..fb24818faafa5059fc0c6d0f8b7c0ff3bcaca9ca
--- /dev/null
+++ b/include/pli_vis/cuda/odf_field.h
@@ -0,0 +1,49 @@
+#ifndef PLI_VIS_ODF_FIELD_H_
+#define PLI_VIS_ODF_FIELD_H_
+
+#include <functional>
+
+#include <cublas_v2.h>
+#include <cusolverDn.h>
+#include <vector_types.h>
+
+namespace pli
+{
+void calculate_odfs(
+  cublasHandle_t     cublas            ,
+  cusolverDnHandle_t cusolver          ,
+  const uint3&       dimensions        ,
+  const uint3&       vectors_dimensions,
+  const uint2&       histogram_bins    ,
+  const unsigned     maximum_degree    ,
+  const float3*      vectors           ,
+        float*       coefficients      ,
+        bool         even_only         ,
+        std::function<void(const std::string&)> status_callback = [](const std::string&){});
+
+void sample_odfs(
+  const uint3&       dimensions        ,
+  const unsigned     maximum_degree    ,
+  const float*       coefficients      ,
+  const uint2&       tessellations     , 
+  const uint3&       vector_dimensions ,
+  const float        scale             ,
+        float3*      points            ,
+        float3*      directions        ,
+        unsigned*    indices           ,
+        bool         hierarchical      = false,
+        bool         clustering        = false,
+        float        cluster_threshold = 0.0  ,
+        std::function<void(const std::string&)> status_callback = [](const std::string&){});
+
+void extract_peaks(
+  const uint3&       dimensions    ,
+  const unsigned     maximum_degree,
+  const float*       coefficients  ,
+  const uint2&       tessellations , 
+  const unsigned     maxima_count  ,
+        float3*      maxima        ,
+        std::function<void(const std::string&)> status_callback = [](const std::string&){});
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/cuda/polar_plot.h b/include/pli_vis/cuda/polar_plot.h
new file mode 100644
index 0000000000000000000000000000000000000000..f66d27cf3eee2105c768743e085c2aa97a9a0054
--- /dev/null
+++ b/include/pli_vis/cuda/polar_plot.h
@@ -0,0 +1,18 @@
+#ifndef PLI_VIS_POLAR_PLOT_H_
+#define PLI_VIS_POLAR_PLOT_H_
+
+#include <vector>
+
+#include <vector_types.h>
+
+namespace pli
+{
+std::array<std::vector<float3>, 2> calculate(
+  const std::vector<float3>& vectors           ,
+  const uint2&               vectors_dimensions,
+  const unsigned             superpixel_size   ,
+  const unsigned             angular_partitions,
+  const bool                 symmetric         );
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/cuda/pt/cartesian_locator.h b/include/pli_vis/cuda/pt/cartesian_locator.h
new file mode 100644
index 0000000000000000000000000000000000000000..aaeecbae2127ed6c22fd6204681d0856f9c240c8
--- /dev/null
+++ b/include/pli_vis/cuda/pt/cartesian_locator.h
@@ -0,0 +1,73 @@
+#ifndef CUPT_CARTESIAN_LOCATOR_H_
+#define CUPT_CARTESIAN_LOCATOR_H_
+
+#include <cstddef>
+
+#include <host_defines.h>
+#include <vector_types.h>
+
+#include "../utility/vector_ops.h"
+
+namespace cupt 
+{
+template<class type = float3, class index_type = uint3>
+class cartesian_locator
+{
+public:
+  __host__ __device__ bool        is_within        (const type&       point      )                   const
+  {
+    return 
+      dimensions.x * spacing.x > point.x && point.x >= 0 &&
+      dimensions.y * spacing.y > point.y && point.y >= 0 &&
+      dimensions.z * spacing.z > point.z && point.z >= 0;
+  }
+  __host__ __device__ index_type  grid_coords      (const type&       point      )                   const
+  {
+    const auto coords = floorf(point / spacing);
+    return index_type {
+      static_cast<unsigned>(coords.x),
+      static_cast<unsigned>(coords.y),
+      static_cast<unsigned>(coords.z)};
+  }
+  __host__ __device__ index_type  grid_coords      (const std::size_t id         )                   const
+  {
+    return index_type {id % dimensions.x, id / dimensions.x % dimensions.y, id / dimensions.x / dimensions.y};
+  }
+  __host__ __device__ type        parametric_coords(const type&       point      )                   const
+  {
+    const auto base_point = point_coords(grid_coords(point));
+    return type {
+      (point.x - base_point.x) / spacing.x,
+      (point.y - base_point.y) / spacing.y,
+      (point.z - base_point.z) / spacing.z};
+  }
+  __host__ __device__ type        point_coords     (const index_type& grid_coords)                   const
+  {
+    return type {
+      grid_coords.x * spacing.x,
+      grid_coords.y * spacing.y,
+      grid_coords.z * spacing.z};
+  }
+  __host__ __device__ std::size_t point_id         (const index_type& grid_coords)                   const
+  {
+    return grid_coords.x + dimensions.x * (grid_coords.y + dimensions.y * grid_coords.z);
+  }
+  __host__ __device__ void        cell_point_ids   (const index_type& grid_coords, std::size_t* ids) const
+  {
+    const auto id = point_id(grid_coords);
+    ids[0] = id                     ;
+    ids[1] = id + 1                 ; 
+    ids[2] = id +  dimensions.x     ;
+    ids[3] = id +  dimensions.x + 1 ; 
+    ids[4] = id +  dimensions.x      * dimensions.y      ;
+    ids[5] = id +  dimensions.x      * dimensions.y + 1  ;
+    ids[6] = id + (dimensions.x + 1) * dimensions.y      ;
+    ids[7] = id + (dimensions.x + 1) * dimensions.y + 1  ;
+  }
+
+  index_type dimensions;
+  type       spacing   ;
+};
+}
+
+#endif
diff --git a/include/pli_vis/cuda/pt/runge_kutta_4_integrator.h b/include/pli_vis/cuda/pt/runge_kutta_4_integrator.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb1fc97984ec27a07f8e281383b6a57cb75c5d88
--- /dev/null
+++ b/include/pli_vis/cuda/pt/runge_kutta_4_integrator.h
@@ -0,0 +1,89 @@
+#ifndef CUPT_RUNGE_KUTTA_4_INTEGRATOR_H_
+#define CUPT_RUNGE_KUTTA_4_INTEGRATOR_H_
+
+#include <host_defines.h>
+#include <vector_types.h>
+
+#include "../utility/vector_ops.h"
+
+namespace cupt 
+{
+template <class data_type, class interpolator_type> 
+class runge_kutta_4_integrator 
+{
+public:
+  struct integration_step 
+  {
+    __host__ __device__ integration_step(const data_type& start_point, const float step)
+    : start_point(start_point)
+    , end_point  (start_point)
+    , current_k  ({0.0f, 0.0f, 0.0f})
+    , sum_k      ({0.0f, 0.0f, 0.0f})
+    , time_step  (step)
+    , next_stage (0)
+    {
+      
+    }
+
+    __host__ __device__ void restart_at(data_type& start)
+    {
+      start_point = start;
+      sum_k       = {0.0f, 0.0f, 0.0f};
+      next_stage  = 0;
+    }
+    __host__ __device__ bool done      () const
+    {
+      return next_stage == 4;
+    }
+
+    data_type  start_point;
+    data_type  end_point  ;
+    data_type  current_k  ;
+    data_type  sum_k      ;
+    float      time_step  ;
+    char       next_stage ;
+  };
+
+  __host__ __device__ explicit runge_kutta_4_integrator(const uint3& dimensions, const float3& spacing, const data_type* data)
+  : interpolator_  (dimensions, spacing, data)
+  , stage_factors_ ({0.0f, 0.5f, 0.5f, 1.0f} )
+  , result_factors_({1.0f, 2.0f, 2.0f, 1.0f} )
+  {
+    
+  }
+
+  __host__ __device__ void set_data    (const uint3& dimensions, const float3& spacing, const data_type*  data)
+  {
+    interpolator_ = interpolator_type(dimensions, spacing, data);
+  }
+  __host__ __device__ void compute_step(integration_step& step_data) const 
+  {
+    for (auto stage = 0; stage < 4; ++stage) 
+    {
+      auto stage_factor = 0.0f, result_factor = 0.0f;
+      if      (stage == 0) { stage_factor = stage_factors_.x; result_factor = result_factors_.x; }
+      else if (stage == 1) { stage_factor = stage_factors_.y; result_factor = result_factors_.y; }
+      else if (stage == 2) { stage_factor = stage_factors_.z; result_factor = result_factors_.z; }
+      else if (stage == 3) { stage_factor = stage_factors_.w; result_factor = result_factors_.w; }
+
+      float3 current_point = step_data.start_point + stage_factor * step_data.current_k;
+
+      if (!interpolator_.is_valid(current_point))
+        return;
+      
+      float3 current_vector = interpolator_.interpolate(current_point);
+      step_data.current_k   = step_data.time_step * current_vector;
+      step_data.sum_k       = step_data.sum_k + result_factor * step_data.current_k;
+      step_data.next_stage  = stage + 1;
+    }
+    step_data.end_point = step_data.start_point + 1.0f / 6.0f * step_data.sum_k;
+  }
+
+private:
+  interpolator_type interpolator_  ;
+  float4            stage_factors_ ;
+  float4            result_factors_;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/cuda/pt/tracer.h b/include/pli_vis/cuda/pt/tracer.h
new file mode 100644
index 0000000000000000000000000000000000000000..49e21d7b20248d2de7ff56cf18211f07cd2259a1
--- /dev/null
+++ b/include/pli_vis/cuda/pt/tracer.h
@@ -0,0 +1,20 @@
+#ifndef CUPT_TRACER_H_
+#define CUPT_TRACER_H_
+
+#include <cstddef>
+#include <vector>
+
+#include <vector_types.h>
+
+namespace cupt 
+{
+std::vector<std::vector<float3>> trace(
+  const std::size_t          iteration_count,
+  const float                step_size      ,
+  const uint3                data_dimensions,
+  const float3               data_spacing   ,
+  const std::vector<float3>& data           ,
+  const std::vector<float3>& seeds          );
+}
+
+#endif
diff --git a/include/pli_vis/cuda/pt/trilinear_interpolator.h b/include/pli_vis/cuda/pt/trilinear_interpolator.h
new file mode 100644
index 0000000000000000000000000000000000000000..bb871fcff10f9591d8cac7c68cce0c5e7a8b9265
--- /dev/null
+++ b/include/pli_vis/cuda/pt/trilinear_interpolator.h
@@ -0,0 +1,59 @@
+#ifndef CUPT_TRILINEAR_INTERPOLATOR_H_
+#define CUPT_TRILINEAR_INTERPOLATOR_H_
+
+#include <cstddef>
+
+#include <host_defines.h>
+#include <vector_types.h>
+
+#include "../utility/vector_ops.h"
+#include "cartesian_locator.h"
+
+namespace cupt 
+{
+template<class type = float3>
+class trilinear_interpolator 
+{
+public:
+  __host__ __device__ explicit trilinear_interpolator(const uint3& dimensions, const float3& spacing, const type* data) 
+  : data_   {data}
+  , locator_{dimensions, spacing}
+  {
+    
+  }
+  __host__ __device__ bool     is_valid              (const type& point) const
+  {
+    return locator_.is_within(point);
+  }
+  __host__ __device__ type     interpolate           (const type& point) const
+  {
+    std::size_t cell_point_ids[8];
+    locator_.cell_point_ids(locator_.grid_coords(point), cell_point_ids);
+
+    type vectors[8];
+    for (auto i = 0; i < 8; ++i)
+      vectors[i] = data_[cell_point_ids[i]];
+
+    const type weights = locator_.parametric_coords(point);
+
+    fold_along_axis<4>(vectors, weights.x);
+    fold_along_axis<2>(vectors, weights.y);
+    fold_along_axis<1>(vectors, weights.z);
+
+    return vectors[0];
+  }
+
+private:
+  template <std::size_t dimensions>
+  __host__ __device__ void     fold_along_axis       (type* values, const float weight) const 
+  {
+    for (auto i = 0; i < dimensions; ++i)
+      values[i] = lerp(values[2 * i], values[2 * i + 1], weight);
+  }
+
+  const type*             data_   ;
+  cartesian_locator<type> locator_;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/sh/choose.h b/include/pli_vis/cuda/sh/choose.h
similarity index 60%
rename from include/sh/choose.h
rename to include/pli_vis/cuda/sh/choose.h
index 53bd5534c11b9665dc169ab1bba6dc5f6b574373..86438ae07c9df904887f8c0863ec3333f55f23f1 100644
--- a/include/sh/choose.h
+++ b/include/pli_vis/cuda/sh/choose.h
@@ -1,19 +1,20 @@
-#ifndef CUSH_CHOOSE_H_
-#define CUSH_CHOOSE_H_
+#ifndef PLI_VIS_CHOOSE_H_
+#define PLI_VIS_CHOOSE_H_
 
-#include <sh/decorators.h>
-#include <sh/factorial.h>
+#include <host_defines.h>
 
-namespace cush
+#include <pli_vis/cuda/sh/factorial.h>
+
+namespace pli
 {
 // Based on GNU Scientific Library's implementation.
 template<typename precision = double>
-INLINE COMMON precision choose   (unsigned int n, unsigned int m)
+__host__ __device__ precision choose   (unsigned int n, unsigned int m)
 {
   return factorial<precision>(n) / (factorial<precision>(m) * factorial<precision>(n - m));
 }
 template<typename precision = double>
-INLINE COMMON precision ln_choose(unsigned int n, unsigned int m)
+__host__ __device__ precision ln_choose(unsigned int n, unsigned int m)
 {
   if (m == n || m == 0)
     return precision(0);
diff --git a/include/pli_vis/cuda/sh/clebsch_gordan.h b/include/pli_vis/cuda/sh/clebsch_gordan.h
new file mode 100644
index 0000000000000000000000000000000000000000..7fdcee73ad6b03b5529865f17da3503b79838b58
--- /dev/null
+++ b/include/pli_vis/cuda/sh/clebsch_gordan.h
@@ -0,0 +1,24 @@
+#ifndef PLI_VIS_CLEBSCH_GORDAN_H_
+#define PLI_VIS_CLEBSCH_GORDAN_H_
+
+#include <host_defines.h>
+#include <math.h>
+
+#include <pli_vis/cuda/sh/wigner.h>
+
+namespace pli
+{
+// Based on "Wigner 3j-Symbol." of Eric Weisstein at http://mathworld.wolfram.com/Wigner3j-Symbol.html
+template<typename precision>
+__host__ __device__ precision clebsch_gordan(
+  unsigned int l1, unsigned int l2, unsigned int l3,
+  unsigned int m1, unsigned int m2, unsigned int m3)
+{
+  return pow (precision(-1.0) , m3 + l1 - l2) *   
+         sqrt(precision( 2.0) * l3 + precision(1.0)) *
+         wigner_3j<precision>(precision(2.0) * l1, precision(2.0) * l2,  precision(2.0) * l3, 
+                              precision(2.0) * m1, precision(2.0) * m2, -precision(2.0) * m3);
+}
+}
+
+#endif
diff --git a/include/sh/factorial.h b/include/pli_vis/cuda/sh/factorial.h
similarity index 55%
rename from include/sh/factorial.h
rename to include/pli_vis/cuda/sh/factorial.h
index 20da36d93658d1f6bad6ef9c41d1aa5f7e779ab5..6c194ad251a413cae194685c992a74cc92d6ecf1 100644
--- a/include/sh/factorial.h
+++ b/include/pli_vis/cuda/sh/factorial.h
@@ -1,14 +1,13 @@
-#ifndef CUSH_FACTORIAL_H_
-#define CUSH_FACTORIAL_H_
+#ifndef PLI_VIS_FACTORIAL_H_
+#define PLI_VIS_FACTORIAL_H_
 
+#include <host_defines.h>
 #include <math.h>
 
-#include <sh/decorators.h>
-
-namespace cush
+namespace pli
 {
 template<typename precision = double>
-INLINE COMMON precision factorial          (unsigned int n)
+__host__ __device__ precision factorial          (unsigned int n)
 {
   precision out(1.0);
   for (auto i = 2; i <= n; i++)
@@ -16,13 +15,13 @@ INLINE COMMON precision factorial          (unsigned int n)
   return out;
 }
 template<typename precision = double>
-INLINE COMMON precision ln_factorial       (unsigned int n)
+__host__ __device__ precision ln_factorial       (unsigned int n)
 {
   return log(factorial<precision>(n));
 }
 
 template<typename precision = double>
-INLINE COMMON precision double_factorial   (unsigned int n)
+__host__ __device__ precision double_factorial   (unsigned int n)
 {
   precision out(1.0);
   while (n > 1)
@@ -33,7 +32,7 @@ INLINE COMMON precision double_factorial   (unsigned int n)
   return out;
 }
 template<typename precision = double>
-INLINE COMMON precision ln_double_factorial(unsigned int n)
+__host__ __device__ precision ln_double_factorial(unsigned int n)
 {
   return log(double_factorial<precision>(n));
 }
diff --git a/include/pli_vis/cuda/sh/launch.h b/include/pli_vis/cuda/sh/launch.h
new file mode 100644
index 0000000000000000000000000000000000000000..8180690bf85bb729e85a90fe54ac82702c05e138
--- /dev/null
+++ b/include/pli_vis/cuda/sh/launch.h
@@ -0,0 +1,46 @@
+#ifndef PLI_VIS_LAUNCH_H_
+#define PLI_VIS_LAUNCH_H_
+
+#include <vector_types.h>
+
+namespace pli
+{
+__forceinline__ __host__ __device__ unsigned block_size_1d()
+{
+  return 64;
+}
+__forceinline__ __host__ __device__ dim3     block_size_2d()
+{
+  return {32, 32, 1};
+}
+__forceinline__ __host__ __device__ dim3     block_size_3d()
+{
+  return {16, 16, 4};
+}
+  
+__forceinline__ __host__ __device__ unsigned grid_size_1d(unsigned target_dimension )
+{
+  auto block_size = block_size_1d();
+  return unsigned((target_dimension + block_size - 1) / block_size);
+}
+__forceinline__ __host__ __device__ dim3     grid_size_2d(dim3     target_dimensions)
+{
+  auto block_size = block_size_2d();
+  return {
+    unsigned((target_dimensions.x + block_size.x - 1) / block_size.x),
+    unsigned((target_dimensions.y + block_size.y - 1) / block_size.y),
+    1u
+  };
+}
+__forceinline__ __host__ __device__ dim3     grid_size_3d(dim3     target_dimensions)
+{
+  auto block_size = block_size_3d();
+  return {
+    unsigned((target_dimensions.x + block_size.x - 1) / block_size.x),
+    unsigned((target_dimensions.y + block_size.y - 1) / block_size.y),
+    unsigned((target_dimensions.z + block_size.z - 1) / block_size.z)
+  };
+}
+}
+
+#endif
diff --git a/include/sh/legendre.h b/include/pli_vis/cuda/sh/legendre.h
similarity index 73%
rename from include/sh/legendre.h
rename to include/pli_vis/cuda/sh/legendre.h
index 7ffee9ed88c289426661cbc51ff982440123c1d2..8e1359c510021b2a1dd7c0b8e4ff84b0666ddb47 100644
--- a/include/sh/legendre.h
+++ b/include/pli_vis/cuda/sh/legendre.h
@@ -1,17 +1,17 @@
-#ifndef CUSH_LEGENDRE_H_
-#define CUSH_LEGENDRE_H_
+#ifndef PLI_VIS_LEGENDRE_H_
+#define PLI_VIS_LEGENDRE_H_
 
+#include <host_defines.h>
 #include <math.h>
 
-#include <sh/decorators.h>
-#include <sh/factorial.h>
+#include <pli_vis/cuda/sh/factorial.h>
 
-namespace cush
+namespace pli
 {
 // Based on the recurrence relations defined in page 10 of
 // "Spherical Harmonic Lighting: The Gritty Details" by Robin Green.
 template<typename precision>
-INLINE COMMON precision associated_legendre(const int l, const int m, const precision& x)
+__host__ __device__ precision associated_legendre(const int l, const int m, const precision& x)
 {
   precision p_mm(1.0);
   if (l > 0)
diff --git a/include/sh/spherical_harmonics.h b/include/pli_vis/cuda/sh/spherical_harmonics.h
similarity index 79%
rename from include/sh/spherical_harmonics.h
rename to include/pli_vis/cuda/sh/spherical_harmonics.h
index 33482c3e9352f70f71f95fc79c38532cff795a47..83f6e7f0f6d6e759885f69a153a4dac4f1f086fd 100644
--- a/include/sh/spherical_harmonics.h
+++ b/include/pli_vis/cuda/sh/spherical_harmonics.h
@@ -1,35 +1,35 @@
-#ifndef CUSH_SPHERICAL_HARMONICS_H_
-#define CUSH_SPHERICAL_HARMONICS_H_
+#ifndef PLI_VIS_SPHERICAL_HARMONICS_H_
+#define PLI_VIS_SPHERICAL_HARMONICS_H_
 
 #define _USE_MATH_DEFINES
 
-#include <math.h>
-
 #include <cuda_runtime_api.h>
 #include <device_launch_parameters.h>
+#include <math.h>
 #include <vector_types.h>
+#include <thrust/sort.h>
 
-#include <sh/clebsch_gordan.h>
-#include <sh/decorators.h>
-#include <sh/launch.h>
-#include <sh/legendre.h>
+#include <pli_vis/cuda/sh/clebsch_gordan.h>
+#include <pli_vis/cuda/sh/factorial.h>
+#include <pli_vis/cuda/sh/launch.h>
+#include <pli_vis/cuda/sh/legendre.h>
 
 // Based on "Spherical Harmonic Lighting: The Gritty Details" by Robin Green.
-namespace cush
+namespace pli
 {
-INLINE COMMON unsigned int maximum_degree   (const unsigned int coefficient_count)
+__forceinline__ __host__ __device__ unsigned int maximum_degree   (const unsigned int coefficient_count)
 {
   return unsigned(sqrtf(float(coefficient_count)) - 1);
 }
-INLINE COMMON unsigned int coefficient_count(const unsigned int max_l)
+__forceinline__ __host__ __device__ unsigned int coefficient_count(const unsigned int max_l)
 {
   return (max_l + 1) * (max_l + 1);
 }
-INLINE COMMON unsigned int coefficient_index(const unsigned int l, const int m)
+__forceinline__ __host__ __device__ unsigned int coefficient_index(const unsigned int l, const int m)
 {
   return l * (l + 1) + m;
 }
-INLINE COMMON int2         coefficient_lm   (const unsigned int index)
+__forceinline__ __host__ __device__ int2         coefficient_lm   (const unsigned int index)
 {
   int2 lm;
   lm.x = int(floor(sqrtf(float(index))));
@@ -38,7 +38,7 @@ INLINE COMMON int2         coefficient_lm   (const unsigned int index)
 }
 
 template<typename precision>
-COMMON precision evaluate(
+__host__ __device__ precision evaluate(
   const unsigned int l    ,
   const          int m    ,
   const precision&   theta,
@@ -53,7 +53,7 @@ COMMON precision evaluate(
   return kml * associated_legendre(l, 0, cos(phi));
 }
 template<typename precision>
-COMMON precision evaluate(
+__host__ __device__ precision evaluate(
   const unsigned int index,
   const precision&   theta,
   const precision&   phi  )
@@ -64,7 +64,7 @@ COMMON precision evaluate(
 
 // Not used internally as the two for loops can also be further parallelized.
 template<typename precision>
-COMMON precision evaluate_sum(
+__host__ __device__ precision evaluate_sum(
   const unsigned int max_l       ,
   const precision&   theta       ,
   const precision&   phi         ,
@@ -78,7 +78,7 @@ COMMON precision evaluate_sum(
 }
 
 template<typename precision>
-COMMON precision is_zero(
+__host__ __device__ precision is_zero(
   const unsigned int coefficient_count,
   const precision*   coefficients )
 {
@@ -89,7 +89,7 @@ COMMON precision is_zero(
 }
 
 template<typename precision>
-COMMON precision l1_distance(
+__host__ __device__ precision l1_distance(
   const unsigned int coefficient_count,
   const precision*   lhs_coefficients ,
   const precision*   rhs_coefficients )
@@ -102,7 +102,7 @@ COMMON precision l1_distance(
 
 // Based on "Rotation Invariant Spherical Harmonic Representation of 3D Shape Descriptors" by Kazhdan et al.
 template<typename precision>
-COMMON precision l2_distance(
+__host__ __device__ precision l2_distance(
   const unsigned int coefficient_count,
   const precision*   lhs_coefficients ,
   const precision*   rhs_coefficients )
@@ -115,7 +115,7 @@ COMMON precision l2_distance(
 
 // Call on a vector_count x coefficient_count(max_l) 2D grid.
 template<typename vector_type, typename precision>
-GLOBAL void calculate_matrix(
+__global__ void calculate_matrix(
   const unsigned int vector_count     ,
   const unsigned int coefficient_count,
   const vector_type* vectors          , 
@@ -136,7 +136,7 @@ GLOBAL void calculate_matrix(
 }
 // Call on a dimensions.x x dimensions.y x dimensions.z 3D grid.
 template<typename vector_type, typename precision>
-GLOBAL void calculate_matrices(
+__global__ void calculate_matrices(
   const uint3        dimensions       ,
   const unsigned int vector_count     , 
   const unsigned int coefficient_count,
@@ -162,7 +162,7 @@ GLOBAL void calculate_matrices(
 
 // Call on a tessellations.x x tessellations.y 2D grid.
 template<typename point_type>
-GLOBAL void sample(
+__global__ void sample(
   const unsigned int l             ,
   const int          m             ,
   const uint2        tessellations ,
@@ -193,13 +193,13 @@ GLOBAL void sample(
 }
 // Call on a tessellations.x x tessellations.y x coefficient_count(max_l) 3D grid.
 template<typename precision, typename point_type>
-GLOBAL void sample_sum(
+__global__ void sample_sum(
   const unsigned int coefficient_count   ,
   const uint2        tessellations       ,
   const precision*   coefficients        ,
   point_type*        output_points       ,
-  unsigned int*      output_indices      ,
-  const unsigned int base_index          = 0)
+  unsigned int*      output_indices      = nullptr,
+  const unsigned int base_index          = 0      )
 {
   auto longitude         = blockIdx.x * blockDim.x + threadIdx.x;
   auto latitude          = blockIdx.y * blockDim.y + threadIdx.y;
@@ -210,10 +210,8 @@ GLOBAL void sample_sum(
       coefficient_index >= coefficient_count)
     return;
 
-  auto point_offset = latitude + longitude * tessellations.y;
-  auto index_offset = 6 * point_offset;
-
-  auto& point = output_points[point_offset];
+  auto  point_offset = latitude + longitude * tessellations.y;
+  auto& point        = output_points[point_offset];
 
   if (coefficient_index == 0)
     point.x = 0;
@@ -222,8 +220,9 @@ GLOBAL void sample_sum(
   point.z =     M_PI * latitude  / (tessellations.y - 1);
   atomicAdd(&point.x, evaluate(coefficient_index, point.y, point.z) * coefficients[coefficient_index]);
 
-  if (coefficient_index == 0)
+  if (output_indices != nullptr && coefficient_index == 0)
   {
+    auto index_offset = 6 * point_offset;
     output_indices[index_offset    ] = base_index +  longitude                        * tessellations.y +  latitude,
     output_indices[index_offset + 1] = base_index +  longitude                        * tessellations.y + (latitude + 1) % tessellations.y,
     output_indices[index_offset + 2] = base_index + (longitude + 1) % tessellations.x * tessellations.y + (latitude + 1) % tessellations.y,
@@ -234,7 +233,7 @@ GLOBAL void sample_sum(
 }
 // Call on a dimensions.x x dimensions.y x dimensions.z 3D grid.
 template<typename precision, typename point_type>
-GLOBAL void sample_sums(
+__global__ void sample_sums(
   const uint3        dimensions         ,
   const unsigned int coefficient_count  ,
   const uint2        tessellations      ,
@@ -279,11 +278,58 @@ GLOBAL void sample_sums(
       output_points[points_offset + i].x = output_points[points_offset + i].x / maxima;
   }
 }
+// Call on a dimensions.x x dimensions.y x dimensions.z 3D grid.
+template<typename precision, typename vector_type>
+__global__ void extract_maxima(
+  // Input data parameters.
+  const uint3        dimensions       ,
+  const unsigned int coefficient_count,
+  const precision*   coefficients     ,
+  // Peak extraction parameters.
+  const uint2        tessellations    ,
+  const unsigned int maxima_count     ,
+  // Output data parameters.
+  vector_type*       maxima           )
+{
+  auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  auto z = blockIdx.z * blockDim.z + threadIdx.z;
+  
+  if (x >= dimensions.x || 
+      y >= dimensions.y || 
+      z >= dimensions.z )
+    return;
+  
+  auto volume_index        = z + dimensions.z * (y + dimensions.y * x);
+  auto coefficients_offset = volume_index * coefficient_count;
+  auto points_size         = tessellations.x * tessellations.y;
+
+  float3* points;
+  cudaMalloc(reinterpret_cast<void**>(&points), points_size * sizeof(float3));
+
+  sample_sum<<<grid_size_3d(dim3(tessellations.x, tessellations.y, coefficient_count)), block_size_3d()>>>(
+    coefficient_count                 ,
+    tessellations                     ,
+    coefficients + coefficients_offset,
+    points                            );
+  cudaDeviceSynchronize();
+
+  thrust::sort(thrust::seq, points, points + points_size, 
+  [&] (const float3& lhs, const float3& rhs)
+  {
+    return lhs.x > rhs.x;
+  });
+
+  for(auto i = 0; i < maxima_count; i++)
+    maxima[volume_index * maxima_count + i] = points[i];
+
+  cudaFree(points);
+}
 
 // Call on a coefficient_count x coefficient_count x coefficient_count 3D grid.
 // Based on Modern Quantum Mechanics 2nd Edition page 216 by Jun John Sakurai.
 template<typename precision, typename atomics_precision = float>
-GLOBAL void product(
+__global__ void product(
   const unsigned int coefficient_count,
   const precision*   lhs_coefficients ,
   const precision*   rhs_coefficients ,
@@ -309,7 +355,7 @@ GLOBAL void product(
 }
 // Call on a dimensions.x x dimensions.y x dimensions.z 3D grid.
 template<typename precision, typename atomics_precision = float>
-GLOBAL void product(
+__global__ void product(
   const uint3        dimensions       ,
   const unsigned int coefficient_count,
   const precision*   lhs_coefficients ,
diff --git a/include/sh/wigner.h b/include/pli_vis/cuda/sh/wigner.h
similarity index 82%
rename from include/sh/wigner.h
rename to include/pli_vis/cuda/sh/wigner.h
index 2f39853a97b7eae45a11e5b75a01b756cbcefe27..1351f0d2ae260f1a53ae5b20948d9e90f5c23fdb 100644
--- a/include/sh/wigner.h
+++ b/include/pli_vis/cuda/sh/wigner.h
@@ -1,17 +1,17 @@
-#ifndef CUSH_WIGNER_H_
-#define CUSH_WIGNER_H_
+#ifndef PLI_VIS_WIGNER_H_
+#define PLI_VIS_WIGNER_H_
 
+#include <host_defines.h>
 #include <math.h>
 
-#include <sh/choose.h>
-#include <sh/decorators.h>
+#include <pli_vis/cuda/sh/choose.h>
 
-namespace cush
+namespace pli
 {
 // Based on GNU Scientific Library's implementation. Input is in half-integer units!
 template<typename precision>
-COMMON precision wigner_3j(int two_l1, int two_l2, int two_l3,
-                           int two_m1, int two_m2, int two_m3)
+__host__ __device__ precision wigner_3j(int two_l1, int two_l2, int two_l3,
+                                        int two_m1, int two_m2, int two_m3)
 {
   if (two_l1 < 0                    ||
       two_l2 < 0                    ||
@@ -42,8 +42,8 @@ COMMON precision wigner_3j(int two_l1, int two_l2, int two_l3,
   auto lpm2  = ( two_l2 + two_m2) / 2;
   auto lpm3  = ( two_l3 + two_m3) / 2;
   auto lsum  = ( two_l1 + two_l2 + two_l3) / 2;
-  int  kmin  = fmax(fmax(0.0f      , lpm2 - lmm3), lmm1 - lpm3);
-  int  kmax  = fmin(fmin((float)lc3, lmm1       ), lpm2       );
+  int  kmin  = fmax(fmax(0.0f                   , lpm2 - lmm3), lmm1 - lpm3);
+  int  kmax  = fmin(fmin(static_cast<float>(lc3), lmm1       ), lpm2       );
   auto sign  = kmin - lpm1 + lmm2 & 1 ? -1 : 1;
 
   auto bcn1  = ln_choose(two_l1  , lc3 );
diff --git a/include/pli_vis/cuda/spherical_histogram.h b/include/pli_vis/cuda/spherical_histogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..fde37d670b580effbfd0e980e5a6f07f5302bda2
--- /dev/null
+++ b/include/pli_vis/cuda/spherical_histogram.h
@@ -0,0 +1,79 @@
+#ifndef PLI_VIS_SPHERICAL_HISTOGRAM_H_
+#define PLI_VIS_SPHERICAL_HISTOGRAM_H_
+
+#define _USE_MATH_DEFINES
+
+#include <math.h>
+
+#include <device_launch_parameters.h>
+#include <vector_types.h>
+
+namespace pli
+{
+// Call on a bin_dimensions.x x bin_dimensions.y 2D grid. 
+// Vectors are in spherical coordinates.
+template<typename vector_type>
+__global__ void create_bins_kernel(
+  uint2        bin_dimensions, 
+  vector_type* bin_vectors   )
+{
+  auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  auto y = blockIdx.y * blockDim.y + threadIdx.y;
+
+  if (x >= bin_dimensions.x || y >= bin_dimensions.y)
+    return;
+
+  auto& bin_vector = bin_vectors[x + bin_dimensions.x * y];
+  bin_vector.x = 1.0;
+  bin_vector.y = 2 * M_PI * x / bin_dimensions.x;
+
+  if (x == 0 && y == 0)
+    bin_vector.z = 0;
+  else if (x == bin_dimensions.x - 1 && y == bin_dimensions.y - 1)
+    bin_vector.z = M_PI;
+  else
+    bin_vector.z = M_PI * (y + 1) / (bin_dimensions.y + 1);
+}
+
+// Call on a vectors_size.x x vectors_size.y x vectors_size.z 3D grid.
+// Vectors are in spherical coordinates.
+template<typename vector_type, typename magnitude_type>
+__global__ void accumulate_kernel(
+  uint3                 vectors_size  ,
+  uint3                 field_offset  ,
+  uint3                 field_size    ,
+  const vector_type*    vectors       ,
+  unsigned              bin_count     ,
+  vector_type*          bin_vectors   ,
+  magnitude_type*       bin_magnitudes)
+{
+  auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  auto z = blockIdx.z * blockDim.z + threadIdx.z;
+
+  if (x >= vectors_size.x || y >= vectors_size.y || z >= vectors_size.z)
+    return;
+
+  auto  vector_index = z + field_size.z * (y + field_size.y * (x + field_offset.x) + field_offset.y) + field_offset.z;
+  auto& vector       = vectors[vector_index];
+
+  magnitude_type max_dot = -1; auto max_index = -1;
+  magnitude_type min_dot =  1; auto min_index = -1;
+  for (auto i = 0; i < bin_count; i++)
+  {
+    auto& bin_vector = bin_vectors[i];
+
+    magnitude_type dot = 
+      cos(vector.z) * cos(bin_vector.z) + 
+      sin(vector.z) * sin(bin_vector.z) * 
+      cos(vector.y  - bin_vector.y);
+
+    if (dot > max_dot) { max_dot = dot; max_index = i; }
+    if (dot < min_dot) { min_dot = dot; min_index = i; }
+  }
+  if (max_index != -1) atomicAdd(&bin_magnitudes[max_index], 1.0);
+  if (min_index != -1) atomicAdd(&bin_magnitudes[min_index], 1.0);
+}
+}
+
+#endif
diff --git a/include/sh/convert.h b/include/pli_vis/cuda/utility/convert.h
similarity index 85%
rename from include/sh/convert.h
rename to include/pli_vis/cuda/utility/convert.h
index 3748f5a405754a322e9cd1e3d0da02b09b469e07..df50fe1aff4e41f7d5b8466e1c9e3e285b535fe0 100644
--- a/include/sh/convert.h
+++ b/include/pli_vis/cuda/utility/convert.h
@@ -1,16 +1,14 @@
-#ifndef CUSH_CONVERT_HPP_
-#define CUSH_CONVERT_HPP_
+#ifndef PLI_VIS_CONVERT_HPP_
+#define PLI_VIS_CONVERT_HPP_
 
 #include <math.h>
 
 #include <vector_types.h>
 
-#include <sh/decorators.h>
-
-namespace cush
+namespace pli
 {
 template<typename input_type, typename output_type = input_type>
-COMMON output_type to_spherical_coords(const input_type& input)
+__host__ __device__ output_type to_spherical_coords(const input_type& input)
 {
   output_type output;
   output[0] = sqrt (pow(input[0], 2) + pow(input[1], 2) + pow(input[2], 2));
@@ -19,7 +17,7 @@ COMMON output_type to_spherical_coords(const input_type& input)
   return output;
 }
 template<typename input_type, typename output_type = input_type>
-COMMON output_type to_cartesian_coords(const input_type& input)
+__host__ __device__ output_type to_cartesian_coords(const input_type& input)
 {
   output_type output;
   output[0] = input[0] * cos(input[1]) * sin(input[2]);
@@ -30,7 +28,7 @@ COMMON output_type to_cartesian_coords(const input_type& input)
 
 #define SPECIALIZE_CONVERT(TYPE)                                         \
 template <typename output_type = TYPE>                                   \
-COMMON output_type to_spherical_coords(const TYPE& input)                \
+__host__ __device__ output_type to_spherical_coords(const TYPE& input)   \
 {                                                                        \
   output_type output;                                                    \
   output.x = sqrt (pow(input.x, 2) + pow(input.y, 2) + pow(input.z, 2)); \
@@ -39,7 +37,7 @@ COMMON output_type to_spherical_coords(const TYPE& input)                \
   return output;                                                         \
 }                                                                        \
 template<typename output_type = TYPE>                                    \
-COMMON output_type to_cartesian_coords(const TYPE& input)                \
+__host__ __device__ output_type to_cartesian_coords(const TYPE& input)   \
 {                                                                        \
   output_type output;                                                    \
   output.x = input.x * cos(input.y) * sin(input.z);                      \
diff --git a/include/sh/vector_ops.h b/include/pli_vis/cuda/utility/vector_ops.h
similarity index 99%
rename from include/sh/vector_ops.h
rename to include/pli_vis/cuda/utility/vector_ops.h
index de66f85d21960bfee9d6993947502c6bcfe377dd..dd92d9041449b1b30bce2494352f833b6769b7ea 100644
--- a/include/sh/vector_ops.h
+++ b/include/pli_vis/cuda/utility/vector_ops.h
@@ -1,5 +1,5 @@
-#ifndef CUSH_VECTOR_OPS_H
-#define CUSH_VECTOR_OPS_H
+#ifndef PLI_VIS_VECTOR_OPS_H_
+#define PLI_VIS_VECTOR_OPS_H_
 
 #include <math.h>
 
diff --git a/include/pli_vis/cuda/zernike/disk.h b/include/pli_vis/cuda/zernike/disk.h
new file mode 100644
index 0000000000000000000000000000000000000000..bc18901c0bd391f2d915264b86887be35ad584c1
--- /dev/null
+++ b/include/pli_vis/cuda/zernike/disk.h
@@ -0,0 +1,34 @@
+#ifndef DISK_H_
+#define DISK_H_
+
+#define _USE_MATH_DEFINES
+
+#include <math.h>
+
+#include <device_launch_parameters.h>
+#include <host_defines.h>
+#include <vector_types.h>
+
+// References:
+// - Weisstein, Disk Point Picking, MathWorld - A Wolfram Web Resource.
+namespace zer
+{
+// This kernel requires a dimensions.x x dimensions.y 2D grid.
+template<typename precision>
+__global__ void sample_disk(uint2 dimensions, precision* samples, bool uniform)
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  if (x >= dimensions.x || y >= dimensions.y)
+    return;
+
+  const auto rho   = uniform ? sqrtf(static_cast<float>(x) / dimensions.x) : static_cast<float>(x) / dimensions.x;
+  const auto theta = 2.0f * M_PI * y / dimensions.y; 
+
+  const auto sample_index = y + dimensions.y * x;
+  samples[sample_index].x = rho * cos(theta);
+  samples[sample_index].y = rho * sin(theta);
+}
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/cuda/zernike/launch.h b/include/pli_vis/cuda/zernike/launch.h
new file mode 100644
index 0000000000000000000000000000000000000000..69363f2b0ef727961633cad5870bca4572f3c3f2
--- /dev/null
+++ b/include/pli_vis/cuda/zernike/launch.h
@@ -0,0 +1,32 @@
+#ifndef ZERNIKE_LAUNCH_H_
+#define ZERNIKE_LAUNCH_H_
+
+#include <thrust/device_vector.h>
+#include <vector_types.h>
+
+namespace zer
+{
+thrust::device_vector<float> launch(
+  const thrust::device_vector<float3>& vectors                       ,
+  const uint2&                         vectors_size                  ,
+  const uint2&                         superpixel_size               ,
+  const uint2&                         disk_partitions = {100u, 360u},
+  const unsigned                       maximum_degree  = 10          ,
+  bool                                 symmetric       = true        ,
+  bool                                 normalize       = true        ,
+  bool                                 even_only       = false       ,
+  bool                                 edge_only       = false       );
+  
+std::vector<float> launch(
+  const std::vector<float3>&           vectors                       ,
+  const uint2&                         vectors_size                  ,
+  const uint2&                         superpixel_size               ,
+  const uint2&                         disk_partitions = {100u, 360u},
+  const unsigned                       maximum_degree  = 10          ,
+  bool                                 symmetric       = true        ,
+  bool                                 normalize       = true        ,
+  bool                                 even_only       = false       ,
+  bool                                 edge_only       = false       );
+}
+
+#endif
diff --git a/include/pli_vis/cuda/zernike/zernike.h b/include/pli_vis/cuda/zernike/zernike.h
new file mode 100644
index 0000000000000000000000000000000000000000..9544d3ad9fa13655cd24690a8689632f51b610a3
--- /dev/null
+++ b/include/pli_vis/cuda/zernike/zernike.h
@@ -0,0 +1,205 @@
+#ifndef ZERNIKE_H_
+#define ZERNIKE_H_
+
+#include <math.h>
+
+#include <device_launch_parameters.h>
+#include <host_defines.h>
+#include <vector_types.h>
+
+// References:
+// - Lakshminarayanan & Fleck, Zernike Polynomials: A Guide, Journal of Modern Optics 2011.7.
+// Notes:
+// - Rho   is restricted to the unit circle.
+// - Theta is measured clockwise from the vertical axis and is in radians.
+namespace zer
+{
+// Launch utility.
+__forceinline__ __host__ __device__ unsigned block_size_1d()
+{
+  return 64;
+}
+__forceinline__ __host__ __device__ dim3     block_size_2d()
+{
+  return {32, 32, 1};
+}
+__forceinline__ __host__ __device__ dim3     block_size_3d()
+{
+  return {16, 16, 4};
+}
+__forceinline__ __host__ __device__ unsigned grid_size_1d (const unsigned& target_dimension )
+{
+  const auto block_size = block_size_1d();
+  return (target_dimension + block_size - 1u) / block_size;
+}
+__forceinline__ __host__ __device__ dim3     grid_size_2d (const dim3&     target_dimensions)
+{
+  const auto block_size = block_size_2d();
+  return 
+  {
+    (target_dimensions.x + block_size.x - 1u) / block_size.x,
+    (target_dimensions.y + block_size.y - 1u) / block_size.y,
+    1u
+  };
+}
+__forceinline__ __host__ __device__ dim3     grid_size_3d (const dim3&     target_dimensions)
+{
+  const auto block_size = block_size_3d();
+  return 
+  {
+    (target_dimensions.x + block_size.x - 1) / block_size.x,
+    (target_dimensions.y + block_size.y - 1) / block_size.y,
+    (target_dimensions.z + block_size.z - 1) / block_size.z
+  };
+}
+
+// Reflection utility.
+template <class T, class M> 
+M member_type(M T:: *);
+
+// Factorial utility.
+template<typename precision>
+__host__ __device__ precision factorial(unsigned n)
+{
+  precision out(1);
+  for (auto i = 2; i <= n; i++)
+    out *= i;
+  return out;
+}
+
+// Zernike moments implementation.
+__forceinline__ __host__ __device__ unsigned maximum_degree(const unsigned& count)
+{
+  return round(sqrtf(2 * count) - 1);
+}
+__forceinline__ __host__ __device__ unsigned expansion_size(const unsigned& max_n)
+{
+  return (max_n + 1) * (max_n + 2) / 2;
+}
+__forceinline__ __host__ __device__ unsigned linear_index  (const int2&     nm   )
+{
+  return (nm.x * (nm.x + 2) + nm.y) / 2;
+}
+__forceinline__ __host__ __device__ int2     quantum_index (const unsigned& i    )
+{
+  int2 nm;
+  nm.x = ceil((-3 + sqrtf(9 + 8 * i)) / 2);
+  nm.y = 2 * i - nm.x * (nm.x + 2);
+  return nm;
+}
+
+template<typename precision>
+__host__ __device__ precision mode    (const int2& nm, const precision& rho)
+{
+  precision out(0);
+  for(unsigned i = 0; i <= (nm.x - nm.y) / 2; i++)
+    out += pow(rho, nm.x - 2 * i) * 
+     (pow(-1, i) * factorial<precision>(nm.x - i)) / 
+     (factorial<precision>(i) * 
+      factorial<precision>(0.5 * (nm.x + nm.y) - i) * 
+      factorial<precision>(0.5 * (nm.x - nm.y) - i));
+  return out;
+}
+template<typename precision>
+__host__ __device__ decltype(member_type(&precision::x)) evaluate(const int2& nm, const precision& rt )
+{
+  return (nm.y >= 0 ? 1.0f : -1.0f) * sqrtf((2.0f * nm.x + 2.0f) / (1.0f + (nm.y == 0 ? 1.0f : 0.0f))) * mode(int2{abs(nm.x), abs(nm.y)}, rt.x) * (nm.y >= 0 ? cos(abs(nm.y) * rt.y) : sin(abs(nm.y) * rt.y));
+}
+
+// This kernel requires a sample_count x expansion_size 2D grid.
+template<typename precision, typename sample_type>
+__global__ void compute_basis(
+  const unsigned     sample_count     , 
+  const sample_type* samples          ,
+  const unsigned     expansion_size   ,
+  precision*         basis            ,
+  bool               even_only        = false,
+  bool               edge_only        = false)
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  if (x >= sample_count || y >= expansion_size)
+    return;
+
+  const auto index = quantum_index(y);
+  if(even_only && index.x % 2 != 0)
+    return;
+  if(edge_only && abs(index.x) != abs(index.y))
+    return;
+
+  atomicAdd(&basis[x + sample_count * y], evaluate(index, samples[x]));
+}
+// This kernel requires a dimensions.x x dimensions.y x dimensions.z 3D grid.
+template<typename precision, typename sample_type>
+__global__ void compute_bases(
+  const uint3        dimensions       ,
+  const unsigned     samples_per_voxel, 
+  const sample_type* samples          ,
+  const unsigned     expansion_size   ,
+  precision*         bases            ,
+  bool               even_only        = false,
+  bool               edge_only        = false)
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  const auto z = blockIdx.z * blockDim.z + threadIdx.z;
+  if (x >= dimensions.x || y >= dimensions.y || z >= dimensions.z)
+    return;
+
+  const auto samples_offset = samples_per_voxel * (z + dimensions.z * (y + dimensions.y * x));
+  const auto bases_offset   = samples_offset    * expansion_size;
+  compute_basis<<<grid_size_2d(dim3(samples_per_voxel, expansion_size)), block_size_2d()>>>(
+    samples_per_voxel       ,
+    samples + samples_offset,
+    expansion_size          , 
+    bases   + bases_offset  ,
+    even_only               ,
+    edge_only               );
+}
+
+// This kernel requires a expansion_size x samples_per_voxel 2D grid.
+template<typename precision, typename coefficient_type>
+__global__ void reconstruct(
+  const unsigned          expansion_size   ,
+  const coefficient_type* coefficients     ,
+  const unsigned          samples_per_voxel,
+  const precision*        samples          ,
+  precision*              outputs          )
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  if (x >= expansion_size || y >= samples_per_voxel)
+    return;
+
+  atomicAdd(&outputs[y], coefficients[x] * evaluate(x, samples[y]));
+}
+// This kernel requires a dimensions.x x dimensions.y x dimensions.z 3D grid.
+template<typename precision, typename coefficient_type>
+__global__ void reconstruct(
+  const uint3             dimensions       ,
+  const unsigned          expansion_size   ,
+  const coefficient_type* coefficients     ,
+  const unsigned          samples_per_voxel,
+  const precision*        samples          ,
+  precision*              outputs          )
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  const auto z = blockIdx.z * blockDim.z + threadIdx.z;
+  if (x >= dimensions.x || y >= dimensions.y || z >= dimensions.z)
+    return;
+  
+  const auto volume_index        = z + dimensions.z * (y + dimensions.y * x);
+  const auto coefficients_offset = volume_index * expansion_size   ;
+  const auto samples_offset      = volume_index * samples_per_voxel;
+  reconstruct<<<grid_size_2d(dim3(expansion_size, samples_per_voxel)), block_size_2d()>>>(
+    expansion_size                    ,
+    coefficients + coefficients_offset, 
+    samples_per_voxel                 ,
+    samples      + samples_offset     ,
+    outputs      + samples_offset     );
+  cudaDeviceSynchronize();
+}
+}
+
+#endif
diff --git a/include/pli_vis/io/io.hpp b/include/pli_vis/io/io.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5b5233fc66a7f717e2c3be1636b33a227d330c31
--- /dev/null
+++ b/include/pli_vis/io/io.hpp
@@ -0,0 +1,253 @@
+#ifndef PLI_VIS_IO_HPP_
+#define PLI_VIS_IO_HPP_
+
+#define H5_USE_BOOST_MULTI_ARRAY
+
+#include <array>
+#include <string>
+
+#include <boost/multi_array.hpp>
+
+#include <pli_vis/third_party/highfive/H5File.hpp>
+#include <pli_vis/io/io_slice_impl.hpp>
+#include <pli_vis/io/io_volume_impl.hpp>
+
+namespace pli
+{
+class io
+{
+public:
+  explicit io(
+    std::string filepath            = std::string(),
+    std::string transmittance_path  = std::string(),
+    std::string retardation_path    = std::string(),
+    std::string direction_path      = std::string(),
+    std::string inclination_path    = std::string(),
+    std::string mask_path           = std::string(),
+    std::string unit_vector_path    = std::string(),
+    std::string distribution_path   = std::string())
+    : filepath_           (filepath           )
+    , transmittance_path_ (transmittance_path )
+    , retardation_path_   (retardation_path   )
+    , direction_path_     (direction_path     )
+    , inclination_path_   (inclination_path   )
+    , mask_path_          (mask_path          )
+    , unit_vector_path_   (unit_vector_path   )
+    , distribution_path_  (distribution_path  )
+  {
+    try         { file_ = std::make_unique<HighFive::File>(filepath_, HighFive::File::ReadWrite); }
+    catch (...) { file_ = nullptr; std::cout << "Invalid file" << std::endl; }
+  }
+  virtual ~io() = default;
+  
+  void set_filepath           (const std::string& filepath           )
+  {
+    filepath_ = filepath;
+    try        { file_ = std::make_unique<HighFive::File>(filepath_, HighFive::File::ReadWrite); }
+    catch(...) { file_ = nullptr; std::cout << "Invalid file" << std::endl; }
+  }
+  void set_transmittance_path (const std::string& transmittance_path )
+  {
+    transmittance_path_      = transmittance_path;
+  }
+  void set_retardation_path   (const std::string& retardation_path   )
+  {
+    retardation_path_        = retardation_path;
+  }
+  void set_direction_path     (const std::string& direction_path     )
+  {
+    direction_path_    = direction_path;
+  }
+  void set_inclination_path   (const std::string& inclination_path   )
+  {
+    inclination_path_  = inclination_path;
+  }
+  void set_mask_path          (const std::string& mask_path          )
+  {
+    mask_path_               = mask_path;
+  }
+  void set_unit_vector_path   (const std::string& unit_vector_path   )
+  {
+    unit_vector_path_ = unit_vector_path;
+  }
+  void set_distribution_path  (const std::string& distribution_path  )
+  {
+    distribution_path_ = distribution_path;
+  }
+
+  const std::string& filepath           () const
+  {
+    return filepath_;
+  }
+  const std::string& transmittance_path () const
+  {
+    return transmittance_path_;
+  }
+  const std::string& retardation_path   () const
+  {
+    return retardation_path_;
+  }
+  const std::string& direction_path     () const
+  {
+    return direction_path_;
+  }
+  const std::string& inclination_path   () const
+  {
+    return inclination_path_;
+  }
+  const std::string& mask_path          () const
+  {
+    return mask_path_;
+  }
+  const std::string& unit_vector_path   () const
+  {
+    return unit_vector_path_;
+  }
+  const std::string& distribution_path  () const
+  {
+    return distribution_path_;
+  }
+
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_transmittance_bounds() const
+  {
+    if(!file_) return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>();
+    try        {       return io_slice_impl ::load_scalar_dataset_bounds(*file_, transmittance_path_); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset_bounds(*file_, transmittance_path_); } catch(...) { return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>(); } }
+  }
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_retardation_bounds  () const
+  {
+    if (!file_) return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>();
+    try        {       return io_slice_impl ::load_scalar_dataset_bounds(*file_, retardation_path_); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset_bounds(*file_, retardation_path_); } catch(...) { return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>(); } }
+  }
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_direction_bounds    () const
+  {
+    if (!file_) return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>();
+    try        {       return io_slice_impl ::load_scalar_dataset_bounds(*file_, direction_path_); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset_bounds(*file_, direction_path_); } catch(...) { return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>(); } }
+  }
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_inclination_bounds  () const
+  {
+    if (!file_) return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>();
+    try        {       return io_slice_impl ::load_scalar_dataset_bounds(*file_, inclination_path_); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset_bounds(*file_, inclination_path_); } catch(...) { return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>(); } }
+  }
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_mask_bounds         () const
+  {
+    if (!file_) return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>();
+    try        {       return io_slice_impl ::load_scalar_dataset_bounds(*file_, mask_path_); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset_bounds(*file_, mask_path_); } catch(...) { return std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>(); } }
+  }
+  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_unit_vector_bounds  () const
+  {
+    if (!file_) return std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>>();
+    try        {       return io_slice_impl ::load_vector_dataset_bounds(*file_, unit_vector_path_); }
+    catch(...) { try { return io_volume_impl::load_vector_dataset_bounds(*file_, unit_vector_path_); } catch(...) { return std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>>(); } }
+  }
+  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_distribution_bounds () const
+  {
+    if (!file_) return std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>>();
+    try        {       return io_slice_impl ::load_tensor_dataset_bounds(*file_, distribution_path_); }
+    catch(...) { try { return io_volume_impl::load_tensor_dataset_bounds(*file_, distribution_path_); } catch(...) { return std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>>(); } }
+  }
+
+  boost::multi_array<float, 3> load_transmittance(const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true ) const
+  {
+    if (!file_) return boost::multi_array<float, 3>();
+    try        {       return io_slice_impl ::load_scalar_dataset(*file_, transmittance_path_, offset, size, stride, normalize); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset(*file_, transmittance_path_, offset, size, stride, normalize); } catch(...) { return boost::multi_array<float, 3>(); } }
+  } 
+  boost::multi_array<float, 3> load_retardation  (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true ) const
+  {
+    if (!file_) return boost::multi_array<float, 3>();
+    try        {       return io_slice_impl ::load_scalar_dataset(*file_, retardation_path_  , offset, size, stride, normalize); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset(*file_, retardation_path_  , offset, size, stride, normalize); } catch(...) { return boost::multi_array<float, 3>(); } }
+  }
+  boost::multi_array<float, 3> load_direction    (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = false) const
+  {
+    if (!file_) return boost::multi_array<float, 3>();
+    try        {       return io_slice_impl ::load_scalar_dataset(*file_, direction_path_, offset, size, stride, normalize); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset(*file_, direction_path_, offset, size, stride, normalize); } catch(...) { return boost::multi_array<float, 3>(); } }
+  }
+  boost::multi_array<float, 3> load_inclination  (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = false) const
+  {
+    if (!file_) return boost::multi_array<float, 3>();
+    try        {       return io_slice_impl ::load_scalar_dataset(*file_, inclination_path_, offset, size, stride, normalize); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset(*file_, inclination_path_, offset, size, stride, normalize); } catch(...) { return boost::multi_array<float, 3>(); } }
+  } 
+  boost::multi_array<float, 3> load_mask         (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = false) const
+  {
+    if (!file_) return boost::multi_array<float, 3>();
+    try        {       return io_slice_impl ::load_scalar_dataset(*file_, mask_path_, offset, size, stride, normalize); }
+    catch(...) { try { return io_volume_impl::load_scalar_dataset(*file_, mask_path_, offset, size, stride, normalize); } catch(...) { return boost::multi_array<float, 3>(); } }
+  }
+  boost::multi_array<float, 4> load_unit_vector  (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true ) const
+  {
+    if (!file_) return boost::multi_array<float, 4>();
+    try        {       return io_slice_impl ::load_vector_dataset(*file_, unit_vector_path_, offset, size, stride, normalize); }
+    catch(...) { try { return io_volume_impl::load_vector_dataset(*file_, unit_vector_path_, offset, size, stride, normalize); } catch(...) { return boost::multi_array<float, 4>(); } }
+  }
+  boost::multi_array<float, 4> load_distribution (const std::array<std::size_t, 3>& offset, const std::array<std::size_t, 3>& size, const std::array<std::size_t, 3>& stride = {1,1,1}, bool normalize = true ) const
+  {
+    if (!file_) return boost::multi_array<float, 4>();
+    try        {       return io_slice_impl ::load_tensor_dataset(*file_, distribution_path_, offset, size, stride, normalize); }
+    catch(...) { try { return io_volume_impl::load_tensor_dataset(*file_, distribution_path_, offset, size, stride, normalize); } catch(...) { return boost::multi_array<float, 4>(); } }
+  }
+ 
+  void save_transmittance(const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
+  {
+    if (!file_) return;
+    try        {       io_slice_impl ::save_scalar_dataset(*file_, transmittance_path_, offset, data); }
+    catch(...) { try { io_volume_impl::save_scalar_dataset(*file_, transmittance_path_, offset, data); } catch(...) { } }
+  }
+  void save_retardation  (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
+  {
+    if (!file_) return;
+    try        {       io_slice_impl ::save_scalar_dataset(*file_, retardation_path_, offset, data); }
+    catch(...) { try { io_volume_impl::save_scalar_dataset(*file_, retardation_path_, offset, data); } catch(...) { } }
+  }
+  void save_direction    (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
+  {
+    if (!file_) return;
+    try        {       io_slice_impl ::save_scalar_dataset(*file_, direction_path_, offset, data); }
+    catch(...) { try { io_volume_impl::save_scalar_dataset(*file_, direction_path_, offset, data); } catch(...) { } }
+  }
+  void save_inclination  (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
+  {
+    if (!file_) return;
+    try        {       io_slice_impl ::save_scalar_dataset(*file_, inclination_path_, offset, data); }
+    catch(...) { try { io_volume_impl::save_scalar_dataset(*file_, inclination_path_, offset, data); } catch(...) { } }
+  }
+  void save_mask         (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 3>& data)
+  {
+    if (!file_) return;
+    try        {       io_slice_impl ::save_scalar_dataset(*file_, mask_path_, offset, data); }
+    catch(...) { try { io_volume_impl::save_scalar_dataset(*file_, mask_path_, offset, data); } catch(...) { } }
+  }  
+  void save_unit_vector  (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data)
+  {
+    if (!file_) return;
+    try        {       io_slice_impl ::save_vector_dataset(*file_, unit_vector_path_, offset, data); }
+    catch(...) { try { io_volume_impl::save_vector_dataset(*file_, unit_vector_path_, offset, data); } catch(...) { } }
+  }
+  void save_distribution (const std::array<std::size_t, 3>& offset, const boost::multi_array<float, 4>& data)
+  {
+    if (!file_) return;
+    try        {       io_slice_impl ::save_tensor_dataset(*file_, distribution_path_, offset, data); }
+    catch(...) { try { io_volume_impl::save_tensor_dataset(*file_, distribution_path_, offset, data); } catch(...) { } }
+  }
+  
+protected:
+  std::string filepath_;
+  std::string transmittance_path_;
+  std::string retardation_path_;
+  std::string direction_path_;
+  std::string inclination_path_;
+  std::string mask_path_;
+  std::string unit_vector_path_;
+  std::string distribution_path_;
+  std::unique_ptr<HighFive::File> file_;
+};
+}
+
+#endif
diff --git a/include/pli_vis/io/io_slice_impl.hpp b/include/pli_vis/io/io_slice_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..141d2b326dc2716537cf096a9875ca08855b7e66
--- /dev/null
+++ b/include/pli_vis/io/io_slice_impl.hpp
@@ -0,0 +1,210 @@
+#ifndef PLI_VIS_IO_SLICE_IMPL_HPP_
+#define PLI_VIS_IO_SLICE_IMPL_HPP_
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <iostream>
+#include <string>
+#include <utility>
+
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/multi_array.hpp>
+
+namespace pli
+{
+// Implementation for Vervet1818 type data (where each slice is a separate dataset).
+class io_slice_impl
+{
+public:
+  typedef boost::multi_array_types::index_range index_range;
+
+  static std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_scalar_dataset_bounds(
+    const HighFive::File& file        , 
+    const std::string&    dataset_path)
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return {{0, 0, 0}, {0, 0, 0}};
+    auto slice_offset     = boost::lexical_cast<std::size_t>(file.listObjectNames()[0]);
+    auto slice_count      = file.getNumberObjects();
+    auto slice_dimensions = file.getDataSet((boost::format(boost::replace_first_copy(dataset_path, "%Slice%", "%04d")) % slice_offset).str()).getSpace().getDimensions();
+    return {{0, 0, slice_offset}, {slice_dimensions[0], slice_dimensions[1], slice_offset + slice_count}};
+  }
+  static std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_vector_dataset_bounds(
+    const HighFive::File& file        , 
+    const std::string&    dataset_path)
+  {
+    return load_tensor_dataset_bounds(file, dataset_path);
+  }
+  static std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_tensor_dataset_bounds(
+    const HighFive::File& file        , 
+    const std::string&    dataset_path)
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return {{0, 0, 0, 0}, {0, 0, 0, 0}};
+    auto slice_offset     = boost::lexical_cast<std::size_t>(file.listObjectNames()[0]);
+    auto slice_count      = file.getNumberObjects();
+    auto slice_dimensions = file.getDataSet((boost::format(boost::replace_first_copy(dataset_path, "%Slice%", "%04d")) % slice_offset).str()).getSpace().getDimensions();
+    return {{0, 0, slice_offset, 0}, {slice_dimensions[0], slice_dimensions[1], slice_offset + slice_count, slice_dimensions[2]}};
+  }
+
+  static boost::multi_array<float, 3> load_scalar_dataset(
+    const HighFive::File&             file        , 
+    const std::string&                dataset_path, 
+    const std::array<std::size_t, 3>& offset      , 
+    const std::array<std::size_t, 3>& size        , 
+    const std::array<std::size_t, 3>& stride      , 
+    bool                              normalize   )
+  {
+    boost::multi_array<float, 3> data(boost::extents[size[0]][size[1]][size[2]]);
+
+    if (!file.isValid() || dataset_path.empty())
+      return data;
+
+    for (std::size_t z = 0; z < size[2]; z+= stride[2])
+    {
+      boost::multi_array<float, 2> slice_data;
+      file
+        .getDataSet((boost::format(boost::replace_first_copy(dataset_path, "%Slice%", "%04d")) % (z + offset[2])).str())
+        .select    ({offset[0], offset[1]}, {size[0], size[1]}, std::vector<std::size_t>{stride[0], stride[1]})
+        .read      (slice_data);
+      data[boost::indices[index_range()][index_range()][z]] = slice_data;
+    }
+
+    if (normalize && data.num_elements() > 0)
+    {
+      auto max_element = *std::max_element(data.data(), data.data() + data.num_elements());
+      std::transform(data.data(), data.data() + data.num_elements(), data.data(), [max_element](const float& element)
+      {
+        return element / max_element;
+      });
+    }
+
+    return data;
+  }
+  static boost::multi_array<float, 4> load_vector_dataset(
+    const HighFive::File&             file        , 
+    const std::string&                dataset_path, 
+    const std::array<std::size_t, 3>& offset      , 
+    const std::array<std::size_t, 3>& size        , 
+    const std::array<std::size_t, 3>& stride      , 
+    bool                              normalize   )
+  {
+    return load_tensor_dataset(file, dataset_path, offset, size, stride, normalize);
+  }
+  static boost::multi_array<float, 4> load_tensor_dataset(
+    const HighFive::File&             file        , 
+    const std::string&                dataset_path, 
+    const std::array<std::size_t, 3>& offset      , 
+    const std::array<std::size_t, 3>& size        , 
+    const std::array<std::size_t, 3>& stride      , 
+    bool                              normalize   )
+  {
+    boost::multi_array<float, 4> data(boost::extents[size[0]][size[1]][size[2]][1]);
+
+    if (!file.isValid() || dataset_path.empty())
+      return data;
+
+    for (std::size_t z = 0; z < size[2]; z+= stride[2])
+    {
+      boost::multi_array<float, 3> slice_data;
+      auto dataset = file.getDataSet((boost::format(boost::replace_first_copy(dataset_path, "%Slice%", "%04d")) % (z + offset[2])).str());
+      auto count   = dataset.getSpace().getDimensions()[2];
+      
+      if (z == 0) 
+        data.resize(boost::extents[size[0]][size[1]][size[2]][count]);   
+      
+      dataset
+        .select({offset[0], offset[1], 0}, {size[0], size[1], count}, std::vector<std::size_t>{stride[0], stride[1], 1})
+        .read  (slice_data);
+      data[boost::indices[index_range()][index_range()][z][index_range()]] = slice_data;
+    }
+
+    if (normalize && data.num_elements() > 0)
+    {
+      auto max_element = *std::max_element(data.data(), data.data() + data.num_elements());
+      std::transform(data.data(), data.data() + data.num_elements(), data.data(), [max_element](const float& element)
+      {
+        return element / max_element;
+      });
+    }
+
+    return data;
+  }
+  
+  static void save_scalar_dataset(
+    HighFive::File&                     file        , 
+    const std::string&                  dataset_path, 
+    const std::array<std::size_t, 3>&   offset      , 
+    const boost::multi_array<float, 3>& data        ) 
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return;
+
+    auto shape     = data.shape();
+    auto cast_data = const_cast<boost::multi_array<float, 3>&>(data);
+    for (auto z = 0; z < shape[2]; z++)
+    {
+      auto slice_path = (boost::format(boost::replace_first_copy(dataset_path, "%Slice%", "%04d")) % (z + offset[2])).str();
+      boost::multi_array<float, 2> slice_data(cast_data[boost::indices[index_range()][index_range()][z]]);
+      try
+      {
+        file
+          .getDataSet   (slice_path)
+          .select       ({offset[0], offset[1]}, {shape[0], shape[1]})
+          .write        (slice_data);
+      }
+      catch (...)
+      {
+        file
+          .createDataSet(slice_path, HighFive::DataSpace({offset[0] + shape[0], offset[1] + shape[1]}), HighFive::AtomicType<float>())
+          .select       ({offset[0], offset[1]}, {shape[0], shape[1]})
+          .write        (slice_data);
+      }
+    }
+  } 
+  static void save_vector_dataset(
+    HighFive::File&                     file        , 
+    const std::string&                  dataset_path, 
+    const std::array<std::size_t, 3>&   offset      , 
+    const boost::multi_array<float, 4>& data        )
+  {
+    save_tensor_dataset(file, dataset_path, offset, data);
+  }
+  static void save_tensor_dataset(
+    HighFive::File&                     file        , 
+    const std::string&                  dataset_path, 
+    const std::array<std::size_t, 3>&   offset      , 
+    const boost::multi_array<float, 4>& data        ) 
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return;
+
+    auto shape     = data.shape();
+    auto cast_data = const_cast<boost::multi_array<float, 4>&>(data);
+    for (auto z = 0; z < shape[2]; z++)
+    {
+      auto slice_path = (boost::format(boost::replace_first_copy(dataset_path, "%Slice%", "%04d")) % (z + offset[2])).str();
+      boost::multi_array<float, 3> slice_data(cast_data[boost::indices[index_range()][index_range()][z][index_range()]]);
+      try
+      {
+        file
+          .getDataSet   (slice_path)
+          .select       ({offset[0], offset[1], 0}, {shape[0], shape[1], shape[3]})
+          .write        (slice_data);
+      }
+      catch (...)
+      {
+        file
+          .createDataSet(slice_path, HighFive::DataSpace({offset[0] + shape[0], offset[1] + shape[1], shape[3]}), HighFive::AtomicType<float>())
+          .select       ({offset[0], offset[1], 0}, {shape[0], shape[1], shape[3]})
+          .write        (slice_data);
+      }
+    }
+  }
+};
+}
+
+#endif
diff --git a/include/pli_vis/io/io_volume_impl.hpp b/include/pli_vis/io/io_volume_impl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..600873ef2e97e657df59df95fc4e27a13c338d2c
--- /dev/null
+++ b/include/pli_vis/io/io_volume_impl.hpp
@@ -0,0 +1,206 @@
+#ifndef PLI_VIS_IO_VOLUME_IMPL_HPP_
+#define PLI_VIS_IO_VOLUME_IMPL_HPP_
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <string>
+#include <utility>
+
+#include <boost/multi_array.hpp>
+
+namespace pli
+{
+// Implementation for MSA0309 type data (where all information is stored in a single volume, ZXY for scalars, ZWXY for vectors, ZWXY for tensors).
+class io_volume_impl
+{
+public:
+  static std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> load_scalar_dataset_bounds(
+    const HighFive::File& file        , 
+    const std::string&    dataset_path)
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return {{0, 0, 0}, {0, 0, 0}};
+    auto unordered_size = file.getDataSet(dataset_path).getSpace().getDimensions();
+    return {{0, 0, 0}, {unordered_size[1], unordered_size[2], unordered_size[0]}};
+  }
+  static std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_vector_dataset_bounds(
+    const HighFive::File& file        , 
+    const std::string&    dataset_path)
+  {
+    return load_tensor_dataset_bounds(file, dataset_path);
+  }
+  static std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> load_tensor_dataset_bounds(
+    const HighFive::File& file        , 
+    const std::string&    dataset_path)
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return {{0, 0, 0, 0}, {0, 0, 0, 0}};
+    auto unordered_size = file.getDataSet(dataset_path).getSpace().getDimensions();
+    return {{0, 0, 0, 0}, {unordered_size[2], unordered_size[3], unordered_size[0], unordered_size[1]}};
+  }
+
+  static boost::multi_array<float, 3> load_scalar_dataset(
+    const HighFive::File&             file        , 
+    const std::string&                dataset_path, 
+    const std::array<std::size_t, 3>& offset      , 
+    const std::array<std::size_t, 3>& size        , 
+    const std::array<std::size_t, 3>& stride      , 
+    bool                              normalize   )
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return boost::multi_array<float, 3>();
+
+    boost::multi_array<float, 3> unordered_data;
+    file
+      .getDataSet(dataset_path)
+      .select    ({offset[2], offset[0], offset[1]}, {size[2], size[0], size[1]}, std::vector<std::size_t>{stride[2], stride[0], stride[1]})
+      .read      (unordered_data);
+
+    // Convert ZXY to XYZ.
+    boost::multi_array<float, 3> data(boost::extents[size[0]][size[1]][size[2]]);
+    for (auto x = 0; x < size[0]; x++)
+      for (auto y = 0; y < size[1]; y++)
+        for (auto z = 0; z < size[2]; z++)
+          data[x][y][z] = unordered_data[z][x][y];
+
+    if (normalize && data.num_elements() > 0)
+    {
+      auto max_element = *std::max_element(data.data(), data.data() + data.num_elements());
+      std::transform(data.data(), data.data() + data.num_elements(), data.data(), [max_element](const float& element)
+      {
+        return element / max_element;
+      });
+    }
+
+    return data;
+  }
+  static boost::multi_array<float, 4> load_vector_dataset(
+    const HighFive::File&             file        , 
+    const std::string&                dataset_path, 
+    const std::array<std::size_t, 3>& offset      , 
+    const std::array<std::size_t, 3>& size        , 
+    const std::array<std::size_t, 3>& stride      , 
+    bool                              normalize   )
+  {
+    return load_tensor_dataset(file, dataset_path, offset, size, stride, normalize);
+  }
+  static boost::multi_array<float, 4> load_tensor_dataset(
+    const HighFive::File&             file        , 
+    const std::string&                dataset_path, 
+    const std::array<std::size_t, 3>& offset      , 
+    const std::array<std::size_t, 3>& size        , 
+    const std::array<std::size_t, 3>& stride      , 
+    bool                              normalize   )
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return boost::multi_array<float, 4>();
+
+    auto dataset = file.getDataSet(dataset_path);
+    auto count   = dataset.getSpace().getDimensions()[1];
+
+    boost::multi_array<float, 4> unordered_data;
+    dataset
+      .select({offset[2], 0, offset[0], offset[1]}, {size[2], count, size[0], size[1]}, std::vector<std::size_t>{stride[2], 1, stride[0], stride[1]})
+      .read  (unordered_data);
+
+    // Convert ZVXY to XYZV.
+    boost::multi_array<float, 4> data(boost::extents[size[0]][size[1]][size[2]][count]);
+    for (auto x = 0; x < size[0]; x++)
+      for (auto y = 0; y < size[1]; y++)
+        for (auto z = 0; z < size[2]; z++)
+          for (auto v = 0; v < count; v++)
+            data[x][y][z][v] = unordered_data[z][v][x][y];
+
+    if (normalize && data.num_elements() > 0)
+    {
+      auto max_element = *std::max_element(data.data(), data.data() + data.num_elements());
+      std::transform(data.data(), data.data() + data.num_elements(), data.data(), [max_element](const float& element)
+      {
+        return element / max_element;
+      });
+    }
+
+    return data;
+  }
+  
+  static void save_scalar_dataset(
+    HighFive::File&                     file        , 
+    const std::string&                  dataset_path, 
+    const std::array<std::size_t, 3>&   offset      , 
+    const boost::multi_array<float, 3>& data        ) 
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return;
+
+    auto shape = data.shape();
+
+    // Convert XYZ to ZXY.
+    boost::multi_array<float, 3> unordered_data(boost::extents[shape[2]][shape[0]][shape[1]]);
+    for (auto x = 0; x < shape[0]; x++)
+      for (auto y = 0; y < shape[1]; y++)
+        for (auto z = 0; z < shape[2]; z++)
+          unordered_data[z][x][y] = data[x][y][z];
+
+    try
+    {
+      file
+        .getDataSet   (dataset_path)
+        .select       ({offset[2], offset[0], offset[1]}, {shape[2], shape[0], shape[1]})
+        .write        (unordered_data);
+    }
+    catch (...)
+    {
+      file
+        .createDataSet(dataset_path, HighFive::DataSpace({offset[2] + shape[2], offset[0] + shape[0], offset[1] + shape[1]}), HighFive::AtomicType<float>())
+        .select       ({offset[2], offset[0], offset[1]}, {shape[2], shape[0], shape[1]})
+        .write        (unordered_data);
+    }
+
+  } 
+  static void save_vector_dataset(
+    HighFive::File&                     file        , 
+    const std::string&                  dataset_path, 
+    const std::array<std::size_t, 3>&   offset      , 
+    const boost::multi_array<float, 4>& data        )
+  {
+    save_tensor_dataset(file, dataset_path, offset, data);
+  }
+  static void save_tensor_dataset(
+    HighFive::File&                     file        , 
+    const std::string&                  dataset_path, 
+    const std::array<std::size_t, 3>&   offset      , 
+    const boost::multi_array<float, 4>& data        ) 
+  {
+    if (!file.isValid() || dataset_path.empty())
+      return;
+
+    auto shape = data.shape();
+
+    // Convert XYZV to ZVXY.
+    boost::multi_array<float, 4> unordered_data(boost::extents[shape[2]][shape[3]][shape[0]][shape[1]]);
+    for (auto x = 0; x < shape[0]; x++)
+      for (auto y = 0; y < shape[1]; y++)
+        for (auto z = 0; z < shape[2]; z++)
+          for (auto v = 0; v < shape[3]; v++)
+            unordered_data[z][v][x][y] = data[x][y][z][v];
+
+    try
+    {
+      file
+        .getDataSet   (dataset_path)
+        .select       ({offset[2], 0, offset[0], offset[1]}, {shape[2], shape[3], shape[0], shape[1]})
+        .write        (unordered_data);
+    }
+    catch (...)
+    {
+      file
+        .createDataSet(dataset_path, HighFive::DataSpace({offset[2] + shape[2], shape[3], offset[0] + shape[0], offset[1] + shape[1]}), HighFive::AtomicType<float>())
+        .select       ({offset[2], 0, offset[0], offset[1]}, {shape[2], shape[3], shape[0], shape[1]})
+        .write        (unordered_data);
+    }
+  }
+};
+}
+
+#endif
diff --git a/include/opengl/all.hpp b/include/pli_vis/opengl/all.hpp
similarity index 100%
rename from include/opengl/all.hpp
rename to include/pli_vis/opengl/all.hpp
diff --git a/include/opengl/auxiliary/glm_uniforms.hpp b/include/pli_vis/opengl/auxiliary/glm_uniforms.hpp
similarity index 100%
rename from include/opengl/auxiliary/glm_uniforms.hpp
rename to include/pli_vis/opengl/auxiliary/glm_uniforms.hpp
diff --git a/include/opengl/buffer.hpp b/include/pli_vis/opengl/buffer.hpp
similarity index 89%
rename from include/opengl/buffer.hpp
rename to include/pli_vis/opengl/buffer.hpp
index e6fb6846c6b2bd88faec4352d3720ae5ec82ccfc..de5337baa129e7534c115c6ed6ed31d0028fd500 100644
--- a/include/opengl/buffer.hpp
+++ b/include/pli_vis/opengl/buffer.hpp
@@ -115,10 +115,11 @@ private:
   cudaGraphicsResource* resource_ = nullptr;
 };
 
-typedef buffer<GL_ARRAY_BUFFER>         array_buffer;
-typedef buffer<GL_ELEMENT_ARRAY_BUFFER> index_buffer;
-typedef buffer<GL_PIXEL_PACK_BUFFER>    pixel_pack_buffer;
-typedef buffer<GL_PIXEL_UNPACK_BUFFER>  pixel_unpack_buffer;
+typedef buffer<GL_ARRAY_BUFFER>          array_buffer;
+typedef buffer<GL_ELEMENT_ARRAY_BUFFER>  index_buffer;
+typedef buffer<GL_PIXEL_PACK_BUFFER>     pixel_pack_buffer;
+typedef buffer<GL_PIXEL_UNPACK_BUFFER>   pixel_unpack_buffer;
+typedef buffer<GL_SHADER_STORAGE_BUFFER> shader_storage_buffer;
 
 typedef array_buffer vertex_buffer;
 }
diff --git a/include/opengl/framebuffer.hpp b/include/pli_vis/opengl/framebuffer.hpp
similarity index 100%
rename from include/opengl/framebuffer.hpp
rename to include/pli_vis/opengl/framebuffer.hpp
diff --git a/include/opengl/opengl.hpp b/include/pli_vis/opengl/opengl.hpp
similarity index 91%
rename from include/opengl/opengl.hpp
rename to include/pli_vis/opengl/opengl.hpp
index edc4f23030097b49cbb34389afbe322e5963327a..f7cb55aa6a5cbccba7b1685208b457c54843c6d3 100644
--- a/include/opengl/opengl.hpp
+++ b/include/pli_vis/opengl/opengl.hpp
@@ -5,7 +5,7 @@
 
 #include <iostream>
 
-#include <third_party/glew/GL/glew.h>
+#include <glew/GL/glew.h>
 
 namespace opengl
 {
diff --git a/include/opengl/program.hpp b/include/pli_vis/opengl/program.hpp
similarity index 99%
rename from include/opengl/program.hpp
rename to include/pli_vis/opengl/program.hpp
index a03c49a440133c2279f66363472ac2b140c19099..63c120b862f8be5bd80992125218ef447a70f9e2 100644
--- a/include/opengl/program.hpp
+++ b/include/pli_vis/opengl/program.hpp
@@ -54,7 +54,7 @@ public:
   {
     glUseProgram(0  );
   }
-                                      
+
   void   set_attribute_buffer         (const std::string& name, GLuint size, GLuint type, bool normalize = true, GLuint stride = 0, GLuint offset = 0)
   {
     auto location = get_attribute_location(name);
diff --git a/include/opengl/shader.hpp b/include/pli_vis/opengl/shader.hpp
similarity index 100%
rename from include/opengl/shader.hpp
rename to include/pli_vis/opengl/shader.hpp
diff --git a/include/opengl/texture.hpp b/include/pli_vis/opengl/texture.hpp
similarity index 86%
rename from include/opengl/texture.hpp
rename to include/pli_vis/opengl/texture.hpp
index db06fa65886688122092093be4f66f7875cad7f5..dc191e6b42a9c838f4dcfd3db25e50225ca3bcfa 100644
--- a/include/opengl/texture.hpp
+++ b/include/pli_vis/opengl/texture.hpp
@@ -17,7 +17,7 @@ public:
   {
 
   }
-  ~texture()
+  virtual ~texture()
   {
     if (managed_)
       glDeleteTextures(1, &id_);
@@ -100,6 +100,25 @@ public:
     glGenerateMipmap(target);
   }
 
+  static GLint width ()
+  {
+    GLint  result;
+    glGetTexLevelParameteriv(target, 0, GL_TEXTURE_WIDTH , &result);
+    return result;
+  }
+  static GLint height()
+  {
+    GLint  result;
+    glGetTexLevelParameteriv(target, 0, GL_TEXTURE_HEIGHT, &result);
+    return result;
+  }
+  static GLint depth ()
+  {
+    GLint  result;
+    glGetTexLevelParameteriv(target, 0, GL_TEXTURE_DEPTH , &result);
+    return result;
+  }
+
 protected:
   GLuint id_      = 0;
   bool   managed_ = true;
diff --git a/include/opengl/vertex_array.hpp b/include/pli_vis/opengl/vertex_array.hpp
similarity index 70%
rename from include/opengl/vertex_array.hpp
rename to include/pli_vis/opengl/vertex_array.hpp
index 236754bd5e74c895d48e65f0d77962758298a370..780d3ac077901bf6775cc8806016ff7a51a4bb21 100644
--- a/include/opengl/vertex_array.hpp
+++ b/include/pli_vis/opengl/vertex_array.hpp
@@ -20,21 +20,27 @@ public:
       glDeleteVertexArrays(1, &id_);
   }
 
-  void   bind     ()
+  void bind  ()
   {
     glBindVertexArray(id_);
   }
-  void   unbind   ()
+  void unbind()
   {
     glBindVertexArray(0  );
   }
+  
+  template <typename buffer_type>
+  void set_element_buffer(const buffer_type& buffer)
+  {
+     glVertexArrayElementBuffer(id_, buffer.id());
+  }
 
-  bool   is_valid () const
+  bool is_valid() const
   {
     return glIsVertexArray(id_) != 0;
   }
 
-  GLuint id       () const
+  GLuint id() const
   {
     return id_;
   }
diff --git a/include/third_party/glew/GL/eglew.h b/include/pli_vis/third_party/glew/GL/eglew.h
similarity index 100%
rename from include/third_party/glew/GL/eglew.h
rename to include/pli_vis/third_party/glew/GL/eglew.h
diff --git a/include/third_party/glew/GL/glew.h b/include/pli_vis/third_party/glew/GL/glew.h
similarity index 100%
rename from include/third_party/glew/GL/glew.h
rename to include/pli_vis/third_party/glew/GL/glew.h
diff --git a/include/third_party/glew/GL/glxew.h b/include/pli_vis/third_party/glew/GL/glxew.h
similarity index 100%
rename from include/third_party/glew/GL/glxew.h
rename to include/pli_vis/third_party/glew/GL/glxew.h
diff --git a/include/third_party/glew/GL/wglew.h b/include/pli_vis/third_party/glew/GL/wglew.h
similarity index 100%
rename from include/third_party/glew/GL/wglew.h
rename to include/pli_vis/third_party/glew/GL/wglew.h
diff --git a/include/third_party/glm/CMakeLists.txt b/include/pli_vis/third_party/glm/CMakeLists.txt
similarity index 100%
rename from include/third_party/glm/CMakeLists.txt
rename to include/pli_vis/third_party/glm/CMakeLists.txt
diff --git a/include/third_party/glm/common.hpp b/include/pli_vis/third_party/glm/common.hpp
similarity index 100%
rename from include/third_party/glm/common.hpp
rename to include/pli_vis/third_party/glm/common.hpp
diff --git a/include/third_party/glm/detail/_features.hpp b/include/pli_vis/third_party/glm/detail/_features.hpp
similarity index 100%
rename from include/third_party/glm/detail/_features.hpp
rename to include/pli_vis/third_party/glm/detail/_features.hpp
diff --git a/include/third_party/glm/detail/_fixes.hpp b/include/pli_vis/third_party/glm/detail/_fixes.hpp
similarity index 100%
rename from include/third_party/glm/detail/_fixes.hpp
rename to include/pli_vis/third_party/glm/detail/_fixes.hpp
diff --git a/include/third_party/glm/detail/_noise.hpp b/include/pli_vis/third_party/glm/detail/_noise.hpp
similarity index 100%
rename from include/third_party/glm/detail/_noise.hpp
rename to include/pli_vis/third_party/glm/detail/_noise.hpp
diff --git a/include/third_party/glm/detail/_swizzle.hpp b/include/pli_vis/third_party/glm/detail/_swizzle.hpp
similarity index 100%
rename from include/third_party/glm/detail/_swizzle.hpp
rename to include/pli_vis/third_party/glm/detail/_swizzle.hpp
diff --git a/include/third_party/glm/detail/_swizzle_func.hpp b/include/pli_vis/third_party/glm/detail/_swizzle_func.hpp
similarity index 100%
rename from include/third_party/glm/detail/_swizzle_func.hpp
rename to include/pli_vis/third_party/glm/detail/_swizzle_func.hpp
diff --git a/include/third_party/glm/detail/_vectorize.hpp b/include/pli_vis/third_party/glm/detail/_vectorize.hpp
similarity index 100%
rename from include/third_party/glm/detail/_vectorize.hpp
rename to include/pli_vis/third_party/glm/detail/_vectorize.hpp
diff --git a/include/third_party/glm/detail/dummy.cpp b/include/pli_vis/third_party/glm/detail/dummy.cpp
similarity index 100%
rename from include/third_party/glm/detail/dummy.cpp
rename to include/pli_vis/third_party/glm/detail/dummy.cpp
diff --git a/include/third_party/glm/detail/func_common.hpp b/include/pli_vis/third_party/glm/detail/func_common.hpp
similarity index 100%
rename from include/third_party/glm/detail/func_common.hpp
rename to include/pli_vis/third_party/glm/detail/func_common.hpp
diff --git a/include/third_party/glm/detail/func_common.inl b/include/pli_vis/third_party/glm/detail/func_common.inl
similarity index 100%
rename from include/third_party/glm/detail/func_common.inl
rename to include/pli_vis/third_party/glm/detail/func_common.inl
diff --git a/include/third_party/glm/detail/func_common_simd.inl b/include/pli_vis/third_party/glm/detail/func_common_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/func_common_simd.inl
rename to include/pli_vis/third_party/glm/detail/func_common_simd.inl
diff --git a/include/third_party/glm/detail/func_exponential.hpp b/include/pli_vis/third_party/glm/detail/func_exponential.hpp
similarity index 100%
rename from include/third_party/glm/detail/func_exponential.hpp
rename to include/pli_vis/third_party/glm/detail/func_exponential.hpp
diff --git a/include/third_party/glm/detail/func_exponential.inl b/include/pli_vis/third_party/glm/detail/func_exponential.inl
similarity index 100%
rename from include/third_party/glm/detail/func_exponential.inl
rename to include/pli_vis/third_party/glm/detail/func_exponential.inl
diff --git a/include/third_party/glm/detail/func_exponential_simd.inl b/include/pli_vis/third_party/glm/detail/func_exponential_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/func_exponential_simd.inl
rename to include/pli_vis/third_party/glm/detail/func_exponential_simd.inl
diff --git a/include/third_party/glm/detail/func_geometric.hpp b/include/pli_vis/third_party/glm/detail/func_geometric.hpp
similarity index 100%
rename from include/third_party/glm/detail/func_geometric.hpp
rename to include/pli_vis/third_party/glm/detail/func_geometric.hpp
diff --git a/include/third_party/glm/detail/func_geometric.inl b/include/pli_vis/third_party/glm/detail/func_geometric.inl
similarity index 100%
rename from include/third_party/glm/detail/func_geometric.inl
rename to include/pli_vis/third_party/glm/detail/func_geometric.inl
diff --git a/include/third_party/glm/detail/func_geometric_simd.inl b/include/pli_vis/third_party/glm/detail/func_geometric_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/func_geometric_simd.inl
rename to include/pli_vis/third_party/glm/detail/func_geometric_simd.inl
diff --git a/include/third_party/glm/detail/func_integer.hpp b/include/pli_vis/third_party/glm/detail/func_integer.hpp
similarity index 100%
rename from include/third_party/glm/detail/func_integer.hpp
rename to include/pli_vis/third_party/glm/detail/func_integer.hpp
diff --git a/include/third_party/glm/detail/func_integer.inl b/include/pli_vis/third_party/glm/detail/func_integer.inl
similarity index 100%
rename from include/third_party/glm/detail/func_integer.inl
rename to include/pli_vis/third_party/glm/detail/func_integer.inl
diff --git a/include/third_party/glm/detail/func_integer_simd.inl b/include/pli_vis/third_party/glm/detail/func_integer_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/func_integer_simd.inl
rename to include/pli_vis/third_party/glm/detail/func_integer_simd.inl
diff --git a/include/third_party/glm/detail/func_matrix.hpp b/include/pli_vis/third_party/glm/detail/func_matrix.hpp
similarity index 100%
rename from include/third_party/glm/detail/func_matrix.hpp
rename to include/pli_vis/third_party/glm/detail/func_matrix.hpp
diff --git a/include/third_party/glm/detail/func_matrix.inl b/include/pli_vis/third_party/glm/detail/func_matrix.inl
similarity index 100%
rename from include/third_party/glm/detail/func_matrix.inl
rename to include/pli_vis/third_party/glm/detail/func_matrix.inl
diff --git a/include/third_party/glm/detail/func_matrix_simd.inl b/include/pli_vis/third_party/glm/detail/func_matrix_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/func_matrix_simd.inl
rename to include/pli_vis/third_party/glm/detail/func_matrix_simd.inl
diff --git a/include/third_party/glm/detail/func_packing.hpp b/include/pli_vis/third_party/glm/detail/func_packing.hpp
similarity index 100%
rename from include/third_party/glm/detail/func_packing.hpp
rename to include/pli_vis/third_party/glm/detail/func_packing.hpp
diff --git a/include/third_party/glm/detail/func_packing.inl b/include/pli_vis/third_party/glm/detail/func_packing.inl
similarity index 100%
rename from include/third_party/glm/detail/func_packing.inl
rename to include/pli_vis/third_party/glm/detail/func_packing.inl
diff --git a/include/third_party/glm/detail/func_packing_simd.inl b/include/pli_vis/third_party/glm/detail/func_packing_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/func_packing_simd.inl
rename to include/pli_vis/third_party/glm/detail/func_packing_simd.inl
diff --git a/include/third_party/glm/detail/func_trigonometric.hpp b/include/pli_vis/third_party/glm/detail/func_trigonometric.hpp
similarity index 100%
rename from include/third_party/glm/detail/func_trigonometric.hpp
rename to include/pli_vis/third_party/glm/detail/func_trigonometric.hpp
diff --git a/include/third_party/glm/detail/func_trigonometric.inl b/include/pli_vis/third_party/glm/detail/func_trigonometric.inl
similarity index 100%
rename from include/third_party/glm/detail/func_trigonometric.inl
rename to include/pli_vis/third_party/glm/detail/func_trigonometric.inl
diff --git a/include/third_party/glm/detail/func_trigonometric_simd.inl b/include/pli_vis/third_party/glm/detail/func_trigonometric_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/func_trigonometric_simd.inl
rename to include/pli_vis/third_party/glm/detail/func_trigonometric_simd.inl
diff --git a/include/third_party/glm/detail/func_vector_relational.hpp b/include/pli_vis/third_party/glm/detail/func_vector_relational.hpp
similarity index 100%
rename from include/third_party/glm/detail/func_vector_relational.hpp
rename to include/pli_vis/third_party/glm/detail/func_vector_relational.hpp
diff --git a/include/third_party/glm/detail/func_vector_relational.inl b/include/pli_vis/third_party/glm/detail/func_vector_relational.inl
similarity index 100%
rename from include/third_party/glm/detail/func_vector_relational.inl
rename to include/pli_vis/third_party/glm/detail/func_vector_relational.inl
diff --git a/include/third_party/glm/detail/func_vector_relational_simd.inl b/include/pli_vis/third_party/glm/detail/func_vector_relational_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/func_vector_relational_simd.inl
rename to include/pli_vis/third_party/glm/detail/func_vector_relational_simd.inl
diff --git a/include/third_party/glm/detail/glm.cpp b/include/pli_vis/third_party/glm/detail/glm.cpp
similarity index 100%
rename from include/third_party/glm/detail/glm.cpp
rename to include/pli_vis/third_party/glm/detail/glm.cpp
diff --git a/include/third_party/glm/detail/precision.hpp b/include/pli_vis/third_party/glm/detail/precision.hpp
similarity index 100%
rename from include/third_party/glm/detail/precision.hpp
rename to include/pli_vis/third_party/glm/detail/precision.hpp
diff --git a/include/third_party/glm/detail/setup.hpp b/include/pli_vis/third_party/glm/detail/setup.hpp
similarity index 100%
rename from include/third_party/glm/detail/setup.hpp
rename to include/pli_vis/third_party/glm/detail/setup.hpp
diff --git a/include/third_party/glm/detail/type_float.hpp b/include/pli_vis/third_party/glm/detail/type_float.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_float.hpp
rename to include/pli_vis/third_party/glm/detail/type_float.hpp
diff --git a/include/third_party/glm/detail/type_gentype.hpp b/include/pli_vis/third_party/glm/detail/type_gentype.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_gentype.hpp
rename to include/pli_vis/third_party/glm/detail/type_gentype.hpp
diff --git a/include/third_party/glm/detail/type_gentype.inl b/include/pli_vis/third_party/glm/detail/type_gentype.inl
similarity index 100%
rename from include/third_party/glm/detail/type_gentype.inl
rename to include/pli_vis/third_party/glm/detail/type_gentype.inl
diff --git a/include/third_party/glm/detail/type_half.hpp b/include/pli_vis/third_party/glm/detail/type_half.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_half.hpp
rename to include/pli_vis/third_party/glm/detail/type_half.hpp
diff --git a/include/third_party/glm/detail/type_half.inl b/include/pli_vis/third_party/glm/detail/type_half.inl
similarity index 100%
rename from include/third_party/glm/detail/type_half.inl
rename to include/pli_vis/third_party/glm/detail/type_half.inl
diff --git a/include/third_party/glm/detail/type_int.hpp b/include/pli_vis/third_party/glm/detail/type_int.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_int.hpp
rename to include/pli_vis/third_party/glm/detail/type_int.hpp
diff --git a/include/third_party/glm/detail/type_mat.hpp b/include/pli_vis/third_party/glm/detail/type_mat.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat.hpp
diff --git a/include/third_party/glm/detail/type_mat.inl b/include/pli_vis/third_party/glm/detail/type_mat.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat.inl
rename to include/pli_vis/third_party/glm/detail/type_mat.inl
diff --git a/include/third_party/glm/detail/type_mat2x2.hpp b/include/pli_vis/third_party/glm/detail/type_mat2x2.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat2x2.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat2x2.hpp
diff --git a/include/third_party/glm/detail/type_mat2x2.inl b/include/pli_vis/third_party/glm/detail/type_mat2x2.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat2x2.inl
rename to include/pli_vis/third_party/glm/detail/type_mat2x2.inl
diff --git a/include/third_party/glm/detail/type_mat2x3.hpp b/include/pli_vis/third_party/glm/detail/type_mat2x3.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat2x3.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat2x3.hpp
diff --git a/include/third_party/glm/detail/type_mat2x3.inl b/include/pli_vis/third_party/glm/detail/type_mat2x3.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat2x3.inl
rename to include/pli_vis/third_party/glm/detail/type_mat2x3.inl
diff --git a/include/third_party/glm/detail/type_mat2x4.hpp b/include/pli_vis/third_party/glm/detail/type_mat2x4.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat2x4.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat2x4.hpp
diff --git a/include/third_party/glm/detail/type_mat2x4.inl b/include/pli_vis/third_party/glm/detail/type_mat2x4.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat2x4.inl
rename to include/pli_vis/third_party/glm/detail/type_mat2x4.inl
diff --git a/include/third_party/glm/detail/type_mat3x2.hpp b/include/pli_vis/third_party/glm/detail/type_mat3x2.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat3x2.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat3x2.hpp
diff --git a/include/third_party/glm/detail/type_mat3x2.inl b/include/pli_vis/third_party/glm/detail/type_mat3x2.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat3x2.inl
rename to include/pli_vis/third_party/glm/detail/type_mat3x2.inl
diff --git a/include/third_party/glm/detail/type_mat3x3.hpp b/include/pli_vis/third_party/glm/detail/type_mat3x3.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat3x3.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat3x3.hpp
diff --git a/include/third_party/glm/detail/type_mat3x3.inl b/include/pli_vis/third_party/glm/detail/type_mat3x3.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat3x3.inl
rename to include/pli_vis/third_party/glm/detail/type_mat3x3.inl
diff --git a/include/third_party/glm/detail/type_mat3x4.hpp b/include/pli_vis/third_party/glm/detail/type_mat3x4.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat3x4.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat3x4.hpp
diff --git a/include/third_party/glm/detail/type_mat3x4.inl b/include/pli_vis/third_party/glm/detail/type_mat3x4.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat3x4.inl
rename to include/pli_vis/third_party/glm/detail/type_mat3x4.inl
diff --git a/include/third_party/glm/detail/type_mat4x2.hpp b/include/pli_vis/third_party/glm/detail/type_mat4x2.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat4x2.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat4x2.hpp
diff --git a/include/third_party/glm/detail/type_mat4x2.inl b/include/pli_vis/third_party/glm/detail/type_mat4x2.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat4x2.inl
rename to include/pli_vis/third_party/glm/detail/type_mat4x2.inl
diff --git a/include/third_party/glm/detail/type_mat4x3.hpp b/include/pli_vis/third_party/glm/detail/type_mat4x3.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat4x3.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat4x3.hpp
diff --git a/include/third_party/glm/detail/type_mat4x3.inl b/include/pli_vis/third_party/glm/detail/type_mat4x3.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat4x3.inl
rename to include/pli_vis/third_party/glm/detail/type_mat4x3.inl
diff --git a/include/third_party/glm/detail/type_mat4x4.hpp b/include/pli_vis/third_party/glm/detail/type_mat4x4.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_mat4x4.hpp
rename to include/pli_vis/third_party/glm/detail/type_mat4x4.hpp
diff --git a/include/third_party/glm/detail/type_mat4x4.inl b/include/pli_vis/third_party/glm/detail/type_mat4x4.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat4x4.inl
rename to include/pli_vis/third_party/glm/detail/type_mat4x4.inl
diff --git a/include/third_party/glm/detail/type_mat4x4_simd.inl b/include/pli_vis/third_party/glm/detail/type_mat4x4_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/type_mat4x4_simd.inl
rename to include/pli_vis/third_party/glm/detail/type_mat4x4_simd.inl
diff --git a/include/third_party/glm/detail/type_vec.hpp b/include/pli_vis/third_party/glm/detail/type_vec.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_vec.hpp
rename to include/pli_vis/third_party/glm/detail/type_vec.hpp
diff --git a/include/third_party/glm/detail/type_vec.inl b/include/pli_vis/third_party/glm/detail/type_vec.inl
similarity index 100%
rename from include/third_party/glm/detail/type_vec.inl
rename to include/pli_vis/third_party/glm/detail/type_vec.inl
diff --git a/include/third_party/glm/detail/type_vec1.hpp b/include/pli_vis/third_party/glm/detail/type_vec1.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_vec1.hpp
rename to include/pli_vis/third_party/glm/detail/type_vec1.hpp
diff --git a/include/third_party/glm/detail/type_vec1.inl b/include/pli_vis/third_party/glm/detail/type_vec1.inl
similarity index 100%
rename from include/third_party/glm/detail/type_vec1.inl
rename to include/pli_vis/third_party/glm/detail/type_vec1.inl
diff --git a/include/third_party/glm/detail/type_vec2.hpp b/include/pli_vis/third_party/glm/detail/type_vec2.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_vec2.hpp
rename to include/pli_vis/third_party/glm/detail/type_vec2.hpp
diff --git a/include/third_party/glm/detail/type_vec2.inl b/include/pli_vis/third_party/glm/detail/type_vec2.inl
similarity index 100%
rename from include/third_party/glm/detail/type_vec2.inl
rename to include/pli_vis/third_party/glm/detail/type_vec2.inl
diff --git a/include/third_party/glm/detail/type_vec3.hpp b/include/pli_vis/third_party/glm/detail/type_vec3.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_vec3.hpp
rename to include/pli_vis/third_party/glm/detail/type_vec3.hpp
diff --git a/include/third_party/glm/detail/type_vec3.inl b/include/pli_vis/third_party/glm/detail/type_vec3.inl
similarity index 100%
rename from include/third_party/glm/detail/type_vec3.inl
rename to include/pli_vis/third_party/glm/detail/type_vec3.inl
diff --git a/include/third_party/glm/detail/type_vec4.hpp b/include/pli_vis/third_party/glm/detail/type_vec4.hpp
similarity index 100%
rename from include/third_party/glm/detail/type_vec4.hpp
rename to include/pli_vis/third_party/glm/detail/type_vec4.hpp
diff --git a/include/third_party/glm/detail/type_vec4.inl b/include/pli_vis/third_party/glm/detail/type_vec4.inl
similarity index 100%
rename from include/third_party/glm/detail/type_vec4.inl
rename to include/pli_vis/third_party/glm/detail/type_vec4.inl
diff --git a/include/third_party/glm/detail/type_vec4_simd.inl b/include/pli_vis/third_party/glm/detail/type_vec4_simd.inl
similarity index 100%
rename from include/third_party/glm/detail/type_vec4_simd.inl
rename to include/pli_vis/third_party/glm/detail/type_vec4_simd.inl
diff --git a/include/third_party/glm/exponential.hpp b/include/pli_vis/third_party/glm/exponential.hpp
similarity index 100%
rename from include/third_party/glm/exponential.hpp
rename to include/pli_vis/third_party/glm/exponential.hpp
diff --git a/include/third_party/glm/ext.hpp b/include/pli_vis/third_party/glm/ext.hpp
similarity index 100%
rename from include/third_party/glm/ext.hpp
rename to include/pli_vis/third_party/glm/ext.hpp
diff --git a/include/third_party/glm/fwd.hpp b/include/pli_vis/third_party/glm/fwd.hpp
similarity index 100%
rename from include/third_party/glm/fwd.hpp
rename to include/pli_vis/third_party/glm/fwd.hpp
diff --git a/include/third_party/glm/geometric.hpp b/include/pli_vis/third_party/glm/geometric.hpp
similarity index 100%
rename from include/third_party/glm/geometric.hpp
rename to include/pli_vis/third_party/glm/geometric.hpp
diff --git a/include/third_party/glm/glm.hpp b/include/pli_vis/third_party/glm/glm.hpp
similarity index 100%
rename from include/third_party/glm/glm.hpp
rename to include/pli_vis/third_party/glm/glm.hpp
diff --git a/include/third_party/glm/gtc/bitfield.hpp b/include/pli_vis/third_party/glm/gtc/bitfield.hpp
similarity index 100%
rename from include/third_party/glm/gtc/bitfield.hpp
rename to include/pli_vis/third_party/glm/gtc/bitfield.hpp
diff --git a/include/third_party/glm/gtc/bitfield.inl b/include/pli_vis/third_party/glm/gtc/bitfield.inl
similarity index 100%
rename from include/third_party/glm/gtc/bitfield.inl
rename to include/pli_vis/third_party/glm/gtc/bitfield.inl
diff --git a/include/third_party/glm/gtc/color_encoding.inl b/include/pli_vis/third_party/glm/gtc/color_encoding.inl
similarity index 100%
rename from include/third_party/glm/gtc/color_encoding.inl
rename to include/pli_vis/third_party/glm/gtc/color_encoding.inl
diff --git a/include/third_party/glm/gtc/color_space.hpp b/include/pli_vis/third_party/glm/gtc/color_space.hpp
similarity index 100%
rename from include/third_party/glm/gtc/color_space.hpp
rename to include/pli_vis/third_party/glm/gtc/color_space.hpp
diff --git a/include/third_party/glm/gtc/color_space.inl b/include/pli_vis/third_party/glm/gtc/color_space.inl
similarity index 100%
rename from include/third_party/glm/gtc/color_space.inl
rename to include/pli_vis/third_party/glm/gtc/color_space.inl
diff --git a/include/third_party/glm/gtc/constants.hpp b/include/pli_vis/third_party/glm/gtc/constants.hpp
similarity index 100%
rename from include/third_party/glm/gtc/constants.hpp
rename to include/pli_vis/third_party/glm/gtc/constants.hpp
diff --git a/include/third_party/glm/gtc/constants.inl b/include/pli_vis/third_party/glm/gtc/constants.inl
similarity index 100%
rename from include/third_party/glm/gtc/constants.inl
rename to include/pli_vis/third_party/glm/gtc/constants.inl
diff --git a/include/third_party/glm/gtc/epsilon.hpp b/include/pli_vis/third_party/glm/gtc/epsilon.hpp
similarity index 100%
rename from include/third_party/glm/gtc/epsilon.hpp
rename to include/pli_vis/third_party/glm/gtc/epsilon.hpp
diff --git a/include/third_party/glm/gtc/epsilon.inl b/include/pli_vis/third_party/glm/gtc/epsilon.inl
similarity index 100%
rename from include/third_party/glm/gtc/epsilon.inl
rename to include/pli_vis/third_party/glm/gtc/epsilon.inl
diff --git a/include/third_party/glm/gtc/functions.hpp b/include/pli_vis/third_party/glm/gtc/functions.hpp
similarity index 100%
rename from include/third_party/glm/gtc/functions.hpp
rename to include/pli_vis/third_party/glm/gtc/functions.hpp
diff --git a/include/third_party/glm/gtc/functions.inl b/include/pli_vis/third_party/glm/gtc/functions.inl
similarity index 100%
rename from include/third_party/glm/gtc/functions.inl
rename to include/pli_vis/third_party/glm/gtc/functions.inl
diff --git a/include/third_party/glm/gtc/integer.hpp b/include/pli_vis/third_party/glm/gtc/integer.hpp
similarity index 100%
rename from include/third_party/glm/gtc/integer.hpp
rename to include/pli_vis/third_party/glm/gtc/integer.hpp
diff --git a/include/third_party/glm/gtc/integer.inl b/include/pli_vis/third_party/glm/gtc/integer.inl
similarity index 100%
rename from include/third_party/glm/gtc/integer.inl
rename to include/pli_vis/third_party/glm/gtc/integer.inl
diff --git a/include/third_party/glm/gtc/matrix_access.hpp b/include/pli_vis/third_party/glm/gtc/matrix_access.hpp
similarity index 100%
rename from include/third_party/glm/gtc/matrix_access.hpp
rename to include/pli_vis/third_party/glm/gtc/matrix_access.hpp
diff --git a/include/third_party/glm/gtc/matrix_access.inl b/include/pli_vis/third_party/glm/gtc/matrix_access.inl
similarity index 100%
rename from include/third_party/glm/gtc/matrix_access.inl
rename to include/pli_vis/third_party/glm/gtc/matrix_access.inl
diff --git a/include/third_party/glm/gtc/matrix_integer.hpp b/include/pli_vis/third_party/glm/gtc/matrix_integer.hpp
similarity index 100%
rename from include/third_party/glm/gtc/matrix_integer.hpp
rename to include/pli_vis/third_party/glm/gtc/matrix_integer.hpp
diff --git a/include/third_party/glm/gtc/matrix_inverse.hpp b/include/pli_vis/third_party/glm/gtc/matrix_inverse.hpp
similarity index 100%
rename from include/third_party/glm/gtc/matrix_inverse.hpp
rename to include/pli_vis/third_party/glm/gtc/matrix_inverse.hpp
diff --git a/include/third_party/glm/gtc/matrix_inverse.inl b/include/pli_vis/third_party/glm/gtc/matrix_inverse.inl
similarity index 100%
rename from include/third_party/glm/gtc/matrix_inverse.inl
rename to include/pli_vis/third_party/glm/gtc/matrix_inverse.inl
diff --git a/include/third_party/glm/gtc/matrix_transform.hpp b/include/pli_vis/third_party/glm/gtc/matrix_transform.hpp
similarity index 100%
rename from include/third_party/glm/gtc/matrix_transform.hpp
rename to include/pli_vis/third_party/glm/gtc/matrix_transform.hpp
diff --git a/include/third_party/glm/gtc/matrix_transform.inl b/include/pli_vis/third_party/glm/gtc/matrix_transform.inl
similarity index 100%
rename from include/third_party/glm/gtc/matrix_transform.inl
rename to include/pli_vis/third_party/glm/gtc/matrix_transform.inl
diff --git a/include/third_party/glm/gtc/noise.hpp b/include/pli_vis/third_party/glm/gtc/noise.hpp
similarity index 100%
rename from include/third_party/glm/gtc/noise.hpp
rename to include/pli_vis/third_party/glm/gtc/noise.hpp
diff --git a/include/third_party/glm/gtc/noise.inl b/include/pli_vis/third_party/glm/gtc/noise.inl
similarity index 100%
rename from include/third_party/glm/gtc/noise.inl
rename to include/pli_vis/third_party/glm/gtc/noise.inl
diff --git a/include/third_party/glm/gtc/packing.hpp b/include/pli_vis/third_party/glm/gtc/packing.hpp
similarity index 100%
rename from include/third_party/glm/gtc/packing.hpp
rename to include/pli_vis/third_party/glm/gtc/packing.hpp
diff --git a/include/third_party/glm/gtc/packing.inl b/include/pli_vis/third_party/glm/gtc/packing.inl
similarity index 100%
rename from include/third_party/glm/gtc/packing.inl
rename to include/pli_vis/third_party/glm/gtc/packing.inl
diff --git a/include/third_party/glm/gtc/quaternion.hpp b/include/pli_vis/third_party/glm/gtc/quaternion.hpp
similarity index 100%
rename from include/third_party/glm/gtc/quaternion.hpp
rename to include/pli_vis/third_party/glm/gtc/quaternion.hpp
diff --git a/include/third_party/glm/gtc/quaternion.inl b/include/pli_vis/third_party/glm/gtc/quaternion.inl
similarity index 100%
rename from include/third_party/glm/gtc/quaternion.inl
rename to include/pli_vis/third_party/glm/gtc/quaternion.inl
diff --git a/include/third_party/glm/gtc/quaternion_simd.inl b/include/pli_vis/third_party/glm/gtc/quaternion_simd.inl
similarity index 100%
rename from include/third_party/glm/gtc/quaternion_simd.inl
rename to include/pli_vis/third_party/glm/gtc/quaternion_simd.inl
diff --git a/include/third_party/glm/gtc/random.hpp b/include/pli_vis/third_party/glm/gtc/random.hpp
similarity index 100%
rename from include/third_party/glm/gtc/random.hpp
rename to include/pli_vis/third_party/glm/gtc/random.hpp
diff --git a/include/third_party/glm/gtc/random.inl b/include/pli_vis/third_party/glm/gtc/random.inl
similarity index 100%
rename from include/third_party/glm/gtc/random.inl
rename to include/pli_vis/third_party/glm/gtc/random.inl
diff --git a/include/third_party/glm/gtc/reciprocal.hpp b/include/pli_vis/third_party/glm/gtc/reciprocal.hpp
similarity index 100%
rename from include/third_party/glm/gtc/reciprocal.hpp
rename to include/pli_vis/third_party/glm/gtc/reciprocal.hpp
diff --git a/include/third_party/glm/gtc/reciprocal.inl b/include/pli_vis/third_party/glm/gtc/reciprocal.inl
similarity index 100%
rename from include/third_party/glm/gtc/reciprocal.inl
rename to include/pli_vis/third_party/glm/gtc/reciprocal.inl
diff --git a/include/third_party/glm/gtc/round.hpp b/include/pli_vis/third_party/glm/gtc/round.hpp
similarity index 100%
rename from include/third_party/glm/gtc/round.hpp
rename to include/pli_vis/third_party/glm/gtc/round.hpp
diff --git a/include/third_party/glm/gtc/round.inl b/include/pli_vis/third_party/glm/gtc/round.inl
similarity index 100%
rename from include/third_party/glm/gtc/round.inl
rename to include/pli_vis/third_party/glm/gtc/round.inl
diff --git a/include/third_party/glm/gtc/type_aligned.hpp b/include/pli_vis/third_party/glm/gtc/type_aligned.hpp
similarity index 100%
rename from include/third_party/glm/gtc/type_aligned.hpp
rename to include/pli_vis/third_party/glm/gtc/type_aligned.hpp
diff --git a/include/third_party/glm/gtc/type_precision.hpp b/include/pli_vis/third_party/glm/gtc/type_precision.hpp
similarity index 100%
rename from include/third_party/glm/gtc/type_precision.hpp
rename to include/pli_vis/third_party/glm/gtc/type_precision.hpp
diff --git a/include/third_party/glm/gtc/type_precision.inl b/include/pli_vis/third_party/glm/gtc/type_precision.inl
similarity index 100%
rename from include/third_party/glm/gtc/type_precision.inl
rename to include/pli_vis/third_party/glm/gtc/type_precision.inl
diff --git a/include/third_party/glm/gtc/type_ptr.hpp b/include/pli_vis/third_party/glm/gtc/type_ptr.hpp
similarity index 100%
rename from include/third_party/glm/gtc/type_ptr.hpp
rename to include/pli_vis/third_party/glm/gtc/type_ptr.hpp
diff --git a/include/third_party/glm/gtc/type_ptr.inl b/include/pli_vis/third_party/glm/gtc/type_ptr.inl
similarity index 100%
rename from include/third_party/glm/gtc/type_ptr.inl
rename to include/pli_vis/third_party/glm/gtc/type_ptr.inl
diff --git a/include/third_party/glm/gtc/ulp.hpp b/include/pli_vis/third_party/glm/gtc/ulp.hpp
similarity index 100%
rename from include/third_party/glm/gtc/ulp.hpp
rename to include/pli_vis/third_party/glm/gtc/ulp.hpp
diff --git a/include/third_party/glm/gtc/ulp.inl b/include/pli_vis/third_party/glm/gtc/ulp.inl
similarity index 100%
rename from include/third_party/glm/gtc/ulp.inl
rename to include/pli_vis/third_party/glm/gtc/ulp.inl
diff --git a/include/third_party/glm/gtc/vec1.hpp b/include/pli_vis/third_party/glm/gtc/vec1.hpp
similarity index 100%
rename from include/third_party/glm/gtc/vec1.hpp
rename to include/pli_vis/third_party/glm/gtc/vec1.hpp
diff --git a/include/third_party/glm/gtc/vec1.inl b/include/pli_vis/third_party/glm/gtc/vec1.inl
similarity index 100%
rename from include/third_party/glm/gtc/vec1.inl
rename to include/pli_vis/third_party/glm/gtc/vec1.inl
diff --git a/include/third_party/glm/gtx/associated_min_max.hpp b/include/pli_vis/third_party/glm/gtx/associated_min_max.hpp
similarity index 100%
rename from include/third_party/glm/gtx/associated_min_max.hpp
rename to include/pli_vis/third_party/glm/gtx/associated_min_max.hpp
diff --git a/include/third_party/glm/gtx/associated_min_max.inl b/include/pli_vis/third_party/glm/gtx/associated_min_max.inl
similarity index 100%
rename from include/third_party/glm/gtx/associated_min_max.inl
rename to include/pli_vis/third_party/glm/gtx/associated_min_max.inl
diff --git a/include/third_party/glm/gtx/bit.hpp b/include/pli_vis/third_party/glm/gtx/bit.hpp
similarity index 100%
rename from include/third_party/glm/gtx/bit.hpp
rename to include/pli_vis/third_party/glm/gtx/bit.hpp
diff --git a/include/third_party/glm/gtx/bit.inl b/include/pli_vis/third_party/glm/gtx/bit.inl
similarity index 100%
rename from include/third_party/glm/gtx/bit.inl
rename to include/pli_vis/third_party/glm/gtx/bit.inl
diff --git a/include/third_party/glm/gtx/closest_point.hpp b/include/pli_vis/third_party/glm/gtx/closest_point.hpp
similarity index 100%
rename from include/third_party/glm/gtx/closest_point.hpp
rename to include/pli_vis/third_party/glm/gtx/closest_point.hpp
diff --git a/include/third_party/glm/gtx/closest_point.inl b/include/pli_vis/third_party/glm/gtx/closest_point.inl
similarity index 100%
rename from include/third_party/glm/gtx/closest_point.inl
rename to include/pli_vis/third_party/glm/gtx/closest_point.inl
diff --git a/include/third_party/glm/gtx/color_space.hpp b/include/pli_vis/third_party/glm/gtx/color_space.hpp
similarity index 100%
rename from include/third_party/glm/gtx/color_space.hpp
rename to include/pli_vis/third_party/glm/gtx/color_space.hpp
diff --git a/include/third_party/glm/gtx/color_space.inl b/include/pli_vis/third_party/glm/gtx/color_space.inl
similarity index 100%
rename from include/third_party/glm/gtx/color_space.inl
rename to include/pli_vis/third_party/glm/gtx/color_space.inl
diff --git a/include/third_party/glm/gtx/color_space_YCoCg.hpp b/include/pli_vis/third_party/glm/gtx/color_space_YCoCg.hpp
similarity index 100%
rename from include/third_party/glm/gtx/color_space_YCoCg.hpp
rename to include/pli_vis/third_party/glm/gtx/color_space_YCoCg.hpp
diff --git a/include/third_party/glm/gtx/color_space_YCoCg.inl b/include/pli_vis/third_party/glm/gtx/color_space_YCoCg.inl
similarity index 100%
rename from include/third_party/glm/gtx/color_space_YCoCg.inl
rename to include/pli_vis/third_party/glm/gtx/color_space_YCoCg.inl
diff --git a/include/third_party/glm/gtx/common.hpp b/include/pli_vis/third_party/glm/gtx/common.hpp
similarity index 100%
rename from include/third_party/glm/gtx/common.hpp
rename to include/pli_vis/third_party/glm/gtx/common.hpp
diff --git a/include/third_party/glm/gtx/common.inl b/include/pli_vis/third_party/glm/gtx/common.inl
similarity index 100%
rename from include/third_party/glm/gtx/common.inl
rename to include/pli_vis/third_party/glm/gtx/common.inl
diff --git a/include/third_party/glm/gtx/compatibility.hpp b/include/pli_vis/third_party/glm/gtx/compatibility.hpp
similarity index 100%
rename from include/third_party/glm/gtx/compatibility.hpp
rename to include/pli_vis/third_party/glm/gtx/compatibility.hpp
diff --git a/include/third_party/glm/gtx/compatibility.inl b/include/pli_vis/third_party/glm/gtx/compatibility.inl
similarity index 100%
rename from include/third_party/glm/gtx/compatibility.inl
rename to include/pli_vis/third_party/glm/gtx/compatibility.inl
diff --git a/include/third_party/glm/gtx/component_wise.hpp b/include/pli_vis/third_party/glm/gtx/component_wise.hpp
similarity index 100%
rename from include/third_party/glm/gtx/component_wise.hpp
rename to include/pli_vis/third_party/glm/gtx/component_wise.hpp
diff --git a/include/third_party/glm/gtx/component_wise.inl b/include/pli_vis/third_party/glm/gtx/component_wise.inl
similarity index 100%
rename from include/third_party/glm/gtx/component_wise.inl
rename to include/pli_vis/third_party/glm/gtx/component_wise.inl
diff --git a/include/third_party/glm/gtx/dual_quaternion.hpp b/include/pli_vis/third_party/glm/gtx/dual_quaternion.hpp
similarity index 100%
rename from include/third_party/glm/gtx/dual_quaternion.hpp
rename to include/pli_vis/third_party/glm/gtx/dual_quaternion.hpp
diff --git a/include/third_party/glm/gtx/dual_quaternion.inl b/include/pli_vis/third_party/glm/gtx/dual_quaternion.inl
similarity index 100%
rename from include/third_party/glm/gtx/dual_quaternion.inl
rename to include/pli_vis/third_party/glm/gtx/dual_quaternion.inl
diff --git a/include/third_party/glm/gtx/euler_angles.hpp b/include/pli_vis/third_party/glm/gtx/euler_angles.hpp
similarity index 100%
rename from include/third_party/glm/gtx/euler_angles.hpp
rename to include/pli_vis/third_party/glm/gtx/euler_angles.hpp
diff --git a/include/third_party/glm/gtx/euler_angles.inl b/include/pli_vis/third_party/glm/gtx/euler_angles.inl
similarity index 100%
rename from include/third_party/glm/gtx/euler_angles.inl
rename to include/pli_vis/third_party/glm/gtx/euler_angles.inl
diff --git a/include/third_party/glm/gtx/extend.hpp b/include/pli_vis/third_party/glm/gtx/extend.hpp
similarity index 100%
rename from include/third_party/glm/gtx/extend.hpp
rename to include/pli_vis/third_party/glm/gtx/extend.hpp
diff --git a/include/third_party/glm/gtx/extend.inl b/include/pli_vis/third_party/glm/gtx/extend.inl
similarity index 100%
rename from include/third_party/glm/gtx/extend.inl
rename to include/pli_vis/third_party/glm/gtx/extend.inl
diff --git a/include/third_party/glm/gtx/extended_min_max.hpp b/include/pli_vis/third_party/glm/gtx/extended_min_max.hpp
similarity index 100%
rename from include/third_party/glm/gtx/extended_min_max.hpp
rename to include/pli_vis/third_party/glm/gtx/extended_min_max.hpp
diff --git a/include/third_party/glm/gtx/extended_min_max.inl b/include/pli_vis/third_party/glm/gtx/extended_min_max.inl
similarity index 100%
rename from include/third_party/glm/gtx/extended_min_max.inl
rename to include/pli_vis/third_party/glm/gtx/extended_min_max.inl
diff --git a/include/third_party/glm/gtx/fast_exponential.hpp b/include/pli_vis/third_party/glm/gtx/fast_exponential.hpp
similarity index 100%
rename from include/third_party/glm/gtx/fast_exponential.hpp
rename to include/pli_vis/third_party/glm/gtx/fast_exponential.hpp
diff --git a/include/third_party/glm/gtx/fast_exponential.inl b/include/pli_vis/third_party/glm/gtx/fast_exponential.inl
similarity index 100%
rename from include/third_party/glm/gtx/fast_exponential.inl
rename to include/pli_vis/third_party/glm/gtx/fast_exponential.inl
diff --git a/include/third_party/glm/gtx/fast_square_root.hpp b/include/pli_vis/third_party/glm/gtx/fast_square_root.hpp
similarity index 100%
rename from include/third_party/glm/gtx/fast_square_root.hpp
rename to include/pli_vis/third_party/glm/gtx/fast_square_root.hpp
diff --git a/include/third_party/glm/gtx/fast_square_root.inl b/include/pli_vis/third_party/glm/gtx/fast_square_root.inl
similarity index 100%
rename from include/third_party/glm/gtx/fast_square_root.inl
rename to include/pli_vis/third_party/glm/gtx/fast_square_root.inl
diff --git a/include/third_party/glm/gtx/fast_trigonometry.hpp b/include/pli_vis/third_party/glm/gtx/fast_trigonometry.hpp
similarity index 100%
rename from include/third_party/glm/gtx/fast_trigonometry.hpp
rename to include/pli_vis/third_party/glm/gtx/fast_trigonometry.hpp
diff --git a/include/third_party/glm/gtx/fast_trigonometry.inl b/include/pli_vis/third_party/glm/gtx/fast_trigonometry.inl
similarity index 100%
rename from include/third_party/glm/gtx/fast_trigonometry.inl
rename to include/pli_vis/third_party/glm/gtx/fast_trigonometry.inl
diff --git a/include/third_party/glm/gtx/float_notmalize.inl b/include/pli_vis/third_party/glm/gtx/float_notmalize.inl
similarity index 100%
rename from include/third_party/glm/gtx/float_notmalize.inl
rename to include/pli_vis/third_party/glm/gtx/float_notmalize.inl
diff --git a/include/third_party/glm/gtx/gradient_paint.hpp b/include/pli_vis/third_party/glm/gtx/gradient_paint.hpp
similarity index 100%
rename from include/third_party/glm/gtx/gradient_paint.hpp
rename to include/pli_vis/third_party/glm/gtx/gradient_paint.hpp
diff --git a/include/third_party/glm/gtx/gradient_paint.inl b/include/pli_vis/third_party/glm/gtx/gradient_paint.inl
similarity index 100%
rename from include/third_party/glm/gtx/gradient_paint.inl
rename to include/pli_vis/third_party/glm/gtx/gradient_paint.inl
diff --git a/include/third_party/glm/gtx/handed_coordinate_space.hpp b/include/pli_vis/third_party/glm/gtx/handed_coordinate_space.hpp
similarity index 100%
rename from include/third_party/glm/gtx/handed_coordinate_space.hpp
rename to include/pli_vis/third_party/glm/gtx/handed_coordinate_space.hpp
diff --git a/include/third_party/glm/gtx/handed_coordinate_space.inl b/include/pli_vis/third_party/glm/gtx/handed_coordinate_space.inl
similarity index 100%
rename from include/third_party/glm/gtx/handed_coordinate_space.inl
rename to include/pli_vis/third_party/glm/gtx/handed_coordinate_space.inl
diff --git a/include/third_party/glm/gtx/hash.hpp b/include/pli_vis/third_party/glm/gtx/hash.hpp
similarity index 100%
rename from include/third_party/glm/gtx/hash.hpp
rename to include/pli_vis/third_party/glm/gtx/hash.hpp
diff --git a/include/third_party/glm/gtx/hash.inl b/include/pli_vis/third_party/glm/gtx/hash.inl
similarity index 100%
rename from include/third_party/glm/gtx/hash.inl
rename to include/pli_vis/third_party/glm/gtx/hash.inl
diff --git a/include/third_party/glm/gtx/integer.hpp b/include/pli_vis/third_party/glm/gtx/integer.hpp
similarity index 100%
rename from include/third_party/glm/gtx/integer.hpp
rename to include/pli_vis/third_party/glm/gtx/integer.hpp
diff --git a/include/third_party/glm/gtx/integer.inl b/include/pli_vis/third_party/glm/gtx/integer.inl
similarity index 100%
rename from include/third_party/glm/gtx/integer.inl
rename to include/pli_vis/third_party/glm/gtx/integer.inl
diff --git a/include/third_party/glm/gtx/intersect.hpp b/include/pli_vis/third_party/glm/gtx/intersect.hpp
similarity index 100%
rename from include/third_party/glm/gtx/intersect.hpp
rename to include/pli_vis/third_party/glm/gtx/intersect.hpp
diff --git a/include/third_party/glm/gtx/intersect.inl b/include/pli_vis/third_party/glm/gtx/intersect.inl
similarity index 100%
rename from include/third_party/glm/gtx/intersect.inl
rename to include/pli_vis/third_party/glm/gtx/intersect.inl
diff --git a/include/third_party/glm/gtx/io.hpp b/include/pli_vis/third_party/glm/gtx/io.hpp
similarity index 100%
rename from include/third_party/glm/gtx/io.hpp
rename to include/pli_vis/third_party/glm/gtx/io.hpp
diff --git a/include/third_party/glm/gtx/io.inl b/include/pli_vis/third_party/glm/gtx/io.inl
similarity index 100%
rename from include/third_party/glm/gtx/io.inl
rename to include/pli_vis/third_party/glm/gtx/io.inl
diff --git a/include/third_party/glm/gtx/log_base.hpp b/include/pli_vis/third_party/glm/gtx/log_base.hpp
similarity index 100%
rename from include/third_party/glm/gtx/log_base.hpp
rename to include/pli_vis/third_party/glm/gtx/log_base.hpp
diff --git a/include/third_party/glm/gtx/log_base.inl b/include/pli_vis/third_party/glm/gtx/log_base.inl
similarity index 100%
rename from include/third_party/glm/gtx/log_base.inl
rename to include/pli_vis/third_party/glm/gtx/log_base.inl
diff --git a/include/third_party/glm/gtx/matrix_cross_product.hpp b/include/pli_vis/third_party/glm/gtx/matrix_cross_product.hpp
similarity index 100%
rename from include/third_party/glm/gtx/matrix_cross_product.hpp
rename to include/pli_vis/third_party/glm/gtx/matrix_cross_product.hpp
diff --git a/include/third_party/glm/gtx/matrix_cross_product.inl b/include/pli_vis/third_party/glm/gtx/matrix_cross_product.inl
similarity index 100%
rename from include/third_party/glm/gtx/matrix_cross_product.inl
rename to include/pli_vis/third_party/glm/gtx/matrix_cross_product.inl
diff --git a/include/third_party/glm/gtx/matrix_decompose.hpp b/include/pli_vis/third_party/glm/gtx/matrix_decompose.hpp
similarity index 100%
rename from include/third_party/glm/gtx/matrix_decompose.hpp
rename to include/pli_vis/third_party/glm/gtx/matrix_decompose.hpp
diff --git a/include/third_party/glm/gtx/matrix_decompose.inl b/include/pli_vis/third_party/glm/gtx/matrix_decompose.inl
similarity index 100%
rename from include/third_party/glm/gtx/matrix_decompose.inl
rename to include/pli_vis/third_party/glm/gtx/matrix_decompose.inl
diff --git a/include/third_party/glm/gtx/matrix_interpolation.hpp b/include/pli_vis/third_party/glm/gtx/matrix_interpolation.hpp
similarity index 100%
rename from include/third_party/glm/gtx/matrix_interpolation.hpp
rename to include/pli_vis/third_party/glm/gtx/matrix_interpolation.hpp
diff --git a/include/third_party/glm/gtx/matrix_interpolation.inl b/include/pli_vis/third_party/glm/gtx/matrix_interpolation.inl
similarity index 100%
rename from include/third_party/glm/gtx/matrix_interpolation.inl
rename to include/pli_vis/third_party/glm/gtx/matrix_interpolation.inl
diff --git a/include/third_party/glm/gtx/matrix_major_storage.hpp b/include/pli_vis/third_party/glm/gtx/matrix_major_storage.hpp
similarity index 100%
rename from include/third_party/glm/gtx/matrix_major_storage.hpp
rename to include/pli_vis/third_party/glm/gtx/matrix_major_storage.hpp
diff --git a/include/third_party/glm/gtx/matrix_major_storage.inl b/include/pli_vis/third_party/glm/gtx/matrix_major_storage.inl
similarity index 100%
rename from include/third_party/glm/gtx/matrix_major_storage.inl
rename to include/pli_vis/third_party/glm/gtx/matrix_major_storage.inl
diff --git a/include/third_party/glm/gtx/matrix_operation.hpp b/include/pli_vis/third_party/glm/gtx/matrix_operation.hpp
similarity index 100%
rename from include/third_party/glm/gtx/matrix_operation.hpp
rename to include/pli_vis/third_party/glm/gtx/matrix_operation.hpp
diff --git a/include/third_party/glm/gtx/matrix_operation.inl b/include/pli_vis/third_party/glm/gtx/matrix_operation.inl
similarity index 100%
rename from include/third_party/glm/gtx/matrix_operation.inl
rename to include/pli_vis/third_party/glm/gtx/matrix_operation.inl
diff --git a/include/third_party/glm/gtx/matrix_query.hpp b/include/pli_vis/third_party/glm/gtx/matrix_query.hpp
similarity index 100%
rename from include/third_party/glm/gtx/matrix_query.hpp
rename to include/pli_vis/third_party/glm/gtx/matrix_query.hpp
diff --git a/include/third_party/glm/gtx/matrix_query.inl b/include/pli_vis/third_party/glm/gtx/matrix_query.inl
similarity index 100%
rename from include/third_party/glm/gtx/matrix_query.inl
rename to include/pli_vis/third_party/glm/gtx/matrix_query.inl
diff --git a/include/third_party/glm/gtx/matrix_transform_2d.hpp b/include/pli_vis/third_party/glm/gtx/matrix_transform_2d.hpp
similarity index 100%
rename from include/third_party/glm/gtx/matrix_transform_2d.hpp
rename to include/pli_vis/third_party/glm/gtx/matrix_transform_2d.hpp
diff --git a/include/third_party/glm/gtx/matrix_transform_2d.inl b/include/pli_vis/third_party/glm/gtx/matrix_transform_2d.inl
similarity index 100%
rename from include/third_party/glm/gtx/matrix_transform_2d.inl
rename to include/pli_vis/third_party/glm/gtx/matrix_transform_2d.inl
diff --git a/include/third_party/glm/gtx/mixed_product.hpp b/include/pli_vis/third_party/glm/gtx/mixed_product.hpp
similarity index 100%
rename from include/third_party/glm/gtx/mixed_product.hpp
rename to include/pli_vis/third_party/glm/gtx/mixed_product.hpp
diff --git a/include/third_party/glm/gtx/mixed_product.inl b/include/pli_vis/third_party/glm/gtx/mixed_product.inl
similarity index 100%
rename from include/third_party/glm/gtx/mixed_product.inl
rename to include/pli_vis/third_party/glm/gtx/mixed_product.inl
diff --git a/include/third_party/glm/gtx/norm.hpp b/include/pli_vis/third_party/glm/gtx/norm.hpp
similarity index 100%
rename from include/third_party/glm/gtx/norm.hpp
rename to include/pli_vis/third_party/glm/gtx/norm.hpp
diff --git a/include/third_party/glm/gtx/norm.inl b/include/pli_vis/third_party/glm/gtx/norm.inl
similarity index 100%
rename from include/third_party/glm/gtx/norm.inl
rename to include/pli_vis/third_party/glm/gtx/norm.inl
diff --git a/include/third_party/glm/gtx/normal.hpp b/include/pli_vis/third_party/glm/gtx/normal.hpp
similarity index 100%
rename from include/third_party/glm/gtx/normal.hpp
rename to include/pli_vis/third_party/glm/gtx/normal.hpp
diff --git a/include/third_party/glm/gtx/normal.inl b/include/pli_vis/third_party/glm/gtx/normal.inl
similarity index 100%
rename from include/third_party/glm/gtx/normal.inl
rename to include/pli_vis/third_party/glm/gtx/normal.inl
diff --git a/include/third_party/glm/gtx/normalize_dot.hpp b/include/pli_vis/third_party/glm/gtx/normalize_dot.hpp
similarity index 100%
rename from include/third_party/glm/gtx/normalize_dot.hpp
rename to include/pli_vis/third_party/glm/gtx/normalize_dot.hpp
diff --git a/include/third_party/glm/gtx/normalize_dot.inl b/include/pli_vis/third_party/glm/gtx/normalize_dot.inl
similarity index 100%
rename from include/third_party/glm/gtx/normalize_dot.inl
rename to include/pli_vis/third_party/glm/gtx/normalize_dot.inl
diff --git a/include/third_party/glm/gtx/number_precision.hpp b/include/pli_vis/third_party/glm/gtx/number_precision.hpp
similarity index 100%
rename from include/third_party/glm/gtx/number_precision.hpp
rename to include/pli_vis/third_party/glm/gtx/number_precision.hpp
diff --git a/include/third_party/glm/gtx/number_precision.inl b/include/pli_vis/third_party/glm/gtx/number_precision.inl
similarity index 100%
rename from include/third_party/glm/gtx/number_precision.inl
rename to include/pli_vis/third_party/glm/gtx/number_precision.inl
diff --git a/include/third_party/glm/gtx/optimum_pow.hpp b/include/pli_vis/third_party/glm/gtx/optimum_pow.hpp
similarity index 100%
rename from include/third_party/glm/gtx/optimum_pow.hpp
rename to include/pli_vis/third_party/glm/gtx/optimum_pow.hpp
diff --git a/include/third_party/glm/gtx/optimum_pow.inl b/include/pli_vis/third_party/glm/gtx/optimum_pow.inl
similarity index 100%
rename from include/third_party/glm/gtx/optimum_pow.inl
rename to include/pli_vis/third_party/glm/gtx/optimum_pow.inl
diff --git a/include/third_party/glm/gtx/orthonormalize.hpp b/include/pli_vis/third_party/glm/gtx/orthonormalize.hpp
similarity index 100%
rename from include/third_party/glm/gtx/orthonormalize.hpp
rename to include/pli_vis/third_party/glm/gtx/orthonormalize.hpp
diff --git a/include/third_party/glm/gtx/orthonormalize.inl b/include/pli_vis/third_party/glm/gtx/orthonormalize.inl
similarity index 100%
rename from include/third_party/glm/gtx/orthonormalize.inl
rename to include/pli_vis/third_party/glm/gtx/orthonormalize.inl
diff --git a/include/third_party/glm/gtx/perpendicular.hpp b/include/pli_vis/third_party/glm/gtx/perpendicular.hpp
similarity index 100%
rename from include/third_party/glm/gtx/perpendicular.hpp
rename to include/pli_vis/third_party/glm/gtx/perpendicular.hpp
diff --git a/include/third_party/glm/gtx/perpendicular.inl b/include/pli_vis/third_party/glm/gtx/perpendicular.inl
similarity index 100%
rename from include/third_party/glm/gtx/perpendicular.inl
rename to include/pli_vis/third_party/glm/gtx/perpendicular.inl
diff --git a/include/third_party/glm/gtx/polar_coordinates.hpp b/include/pli_vis/third_party/glm/gtx/polar_coordinates.hpp
similarity index 100%
rename from include/third_party/glm/gtx/polar_coordinates.hpp
rename to include/pli_vis/third_party/glm/gtx/polar_coordinates.hpp
diff --git a/include/third_party/glm/gtx/polar_coordinates.inl b/include/pli_vis/third_party/glm/gtx/polar_coordinates.inl
similarity index 100%
rename from include/third_party/glm/gtx/polar_coordinates.inl
rename to include/pli_vis/third_party/glm/gtx/polar_coordinates.inl
diff --git a/include/third_party/glm/gtx/projection.hpp b/include/pli_vis/third_party/glm/gtx/projection.hpp
similarity index 100%
rename from include/third_party/glm/gtx/projection.hpp
rename to include/pli_vis/third_party/glm/gtx/projection.hpp
diff --git a/include/third_party/glm/gtx/projection.inl b/include/pli_vis/third_party/glm/gtx/projection.inl
similarity index 100%
rename from include/third_party/glm/gtx/projection.inl
rename to include/pli_vis/third_party/glm/gtx/projection.inl
diff --git a/include/third_party/glm/gtx/quaternion.hpp b/include/pli_vis/third_party/glm/gtx/quaternion.hpp
similarity index 100%
rename from include/third_party/glm/gtx/quaternion.hpp
rename to include/pli_vis/third_party/glm/gtx/quaternion.hpp
diff --git a/include/third_party/glm/gtx/quaternion.inl b/include/pli_vis/third_party/glm/gtx/quaternion.inl
similarity index 100%
rename from include/third_party/glm/gtx/quaternion.inl
rename to include/pli_vis/third_party/glm/gtx/quaternion.inl
diff --git a/include/third_party/glm/gtx/range.hpp b/include/pli_vis/third_party/glm/gtx/range.hpp
similarity index 100%
rename from include/third_party/glm/gtx/range.hpp
rename to include/pli_vis/third_party/glm/gtx/range.hpp
diff --git a/include/third_party/glm/gtx/raw_data.hpp b/include/pli_vis/third_party/glm/gtx/raw_data.hpp
similarity index 100%
rename from include/third_party/glm/gtx/raw_data.hpp
rename to include/pli_vis/third_party/glm/gtx/raw_data.hpp
diff --git a/include/third_party/glm/gtx/raw_data.inl b/include/pli_vis/third_party/glm/gtx/raw_data.inl
similarity index 100%
rename from include/third_party/glm/gtx/raw_data.inl
rename to include/pli_vis/third_party/glm/gtx/raw_data.inl
diff --git a/include/third_party/glm/gtx/rotate_normalized_axis.hpp b/include/pli_vis/third_party/glm/gtx/rotate_normalized_axis.hpp
similarity index 100%
rename from include/third_party/glm/gtx/rotate_normalized_axis.hpp
rename to include/pli_vis/third_party/glm/gtx/rotate_normalized_axis.hpp
diff --git a/include/third_party/glm/gtx/rotate_normalized_axis.inl b/include/pli_vis/third_party/glm/gtx/rotate_normalized_axis.inl
similarity index 100%
rename from include/third_party/glm/gtx/rotate_normalized_axis.inl
rename to include/pli_vis/third_party/glm/gtx/rotate_normalized_axis.inl
diff --git a/include/third_party/glm/gtx/rotate_vector.hpp b/include/pli_vis/third_party/glm/gtx/rotate_vector.hpp
similarity index 100%
rename from include/third_party/glm/gtx/rotate_vector.hpp
rename to include/pli_vis/third_party/glm/gtx/rotate_vector.hpp
diff --git a/include/third_party/glm/gtx/rotate_vector.inl b/include/pli_vis/third_party/glm/gtx/rotate_vector.inl
similarity index 100%
rename from include/third_party/glm/gtx/rotate_vector.inl
rename to include/pli_vis/third_party/glm/gtx/rotate_vector.inl
diff --git a/include/third_party/glm/gtx/scalar_multiplication.hpp b/include/pli_vis/third_party/glm/gtx/scalar_multiplication.hpp
similarity index 100%
rename from include/third_party/glm/gtx/scalar_multiplication.hpp
rename to include/pli_vis/third_party/glm/gtx/scalar_multiplication.hpp
diff --git a/include/third_party/glm/gtx/scalar_relational.hpp b/include/pli_vis/third_party/glm/gtx/scalar_relational.hpp
similarity index 100%
rename from include/third_party/glm/gtx/scalar_relational.hpp
rename to include/pli_vis/third_party/glm/gtx/scalar_relational.hpp
diff --git a/include/third_party/glm/gtx/scalar_relational.inl b/include/pli_vis/third_party/glm/gtx/scalar_relational.inl
similarity index 100%
rename from include/third_party/glm/gtx/scalar_relational.inl
rename to include/pli_vis/third_party/glm/gtx/scalar_relational.inl
diff --git a/include/third_party/glm/gtx/simd_mat4.hpp b/include/pli_vis/third_party/glm/gtx/simd_mat4.hpp
similarity index 100%
rename from include/third_party/glm/gtx/simd_mat4.hpp
rename to include/pli_vis/third_party/glm/gtx/simd_mat4.hpp
diff --git a/include/third_party/glm/gtx/simd_mat4.inl b/include/pli_vis/third_party/glm/gtx/simd_mat4.inl
similarity index 100%
rename from include/third_party/glm/gtx/simd_mat4.inl
rename to include/pli_vis/third_party/glm/gtx/simd_mat4.inl
diff --git a/include/third_party/glm/gtx/simd_quat.hpp b/include/pli_vis/third_party/glm/gtx/simd_quat.hpp
similarity index 100%
rename from include/third_party/glm/gtx/simd_quat.hpp
rename to include/pli_vis/third_party/glm/gtx/simd_quat.hpp
diff --git a/include/third_party/glm/gtx/simd_quat.inl b/include/pli_vis/third_party/glm/gtx/simd_quat.inl
similarity index 100%
rename from include/third_party/glm/gtx/simd_quat.inl
rename to include/pli_vis/third_party/glm/gtx/simd_quat.inl
diff --git a/include/third_party/glm/gtx/simd_vec4.hpp b/include/pli_vis/third_party/glm/gtx/simd_vec4.hpp
similarity index 100%
rename from include/third_party/glm/gtx/simd_vec4.hpp
rename to include/pli_vis/third_party/glm/gtx/simd_vec4.hpp
diff --git a/include/third_party/glm/gtx/simd_vec4.inl b/include/pli_vis/third_party/glm/gtx/simd_vec4.inl
similarity index 100%
rename from include/third_party/glm/gtx/simd_vec4.inl
rename to include/pli_vis/third_party/glm/gtx/simd_vec4.inl
diff --git a/include/third_party/glm/gtx/spline.hpp b/include/pli_vis/third_party/glm/gtx/spline.hpp
similarity index 100%
rename from include/third_party/glm/gtx/spline.hpp
rename to include/pli_vis/third_party/glm/gtx/spline.hpp
diff --git a/include/third_party/glm/gtx/spline.inl b/include/pli_vis/third_party/glm/gtx/spline.inl
similarity index 100%
rename from include/third_party/glm/gtx/spline.inl
rename to include/pli_vis/third_party/glm/gtx/spline.inl
diff --git a/include/third_party/glm/gtx/std_based_type.hpp b/include/pli_vis/third_party/glm/gtx/std_based_type.hpp
similarity index 100%
rename from include/third_party/glm/gtx/std_based_type.hpp
rename to include/pli_vis/third_party/glm/gtx/std_based_type.hpp
diff --git a/include/third_party/glm/gtx/std_based_type.inl b/include/pli_vis/third_party/glm/gtx/std_based_type.inl
similarity index 100%
rename from include/third_party/glm/gtx/std_based_type.inl
rename to include/pli_vis/third_party/glm/gtx/std_based_type.inl
diff --git a/include/third_party/glm/gtx/string_cast.hpp b/include/pli_vis/third_party/glm/gtx/string_cast.hpp
similarity index 100%
rename from include/third_party/glm/gtx/string_cast.hpp
rename to include/pli_vis/third_party/glm/gtx/string_cast.hpp
diff --git a/include/third_party/glm/gtx/string_cast.inl b/include/pli_vis/third_party/glm/gtx/string_cast.inl
similarity index 100%
rename from include/third_party/glm/gtx/string_cast.inl
rename to include/pli_vis/third_party/glm/gtx/string_cast.inl
diff --git a/include/third_party/glm/gtx/transform.hpp b/include/pli_vis/third_party/glm/gtx/transform.hpp
similarity index 100%
rename from include/third_party/glm/gtx/transform.hpp
rename to include/pli_vis/third_party/glm/gtx/transform.hpp
diff --git a/include/third_party/glm/gtx/transform.inl b/include/pli_vis/third_party/glm/gtx/transform.inl
similarity index 100%
rename from include/third_party/glm/gtx/transform.inl
rename to include/pli_vis/third_party/glm/gtx/transform.inl
diff --git a/include/third_party/glm/gtx/transform2.hpp b/include/pli_vis/third_party/glm/gtx/transform2.hpp
similarity index 100%
rename from include/third_party/glm/gtx/transform2.hpp
rename to include/pli_vis/third_party/glm/gtx/transform2.hpp
diff --git a/include/third_party/glm/gtx/transform2.inl b/include/pli_vis/third_party/glm/gtx/transform2.inl
similarity index 100%
rename from include/third_party/glm/gtx/transform2.inl
rename to include/pli_vis/third_party/glm/gtx/transform2.inl
diff --git a/include/third_party/glm/gtx/type_aligned.hpp b/include/pli_vis/third_party/glm/gtx/type_aligned.hpp
similarity index 100%
rename from include/third_party/glm/gtx/type_aligned.hpp
rename to include/pli_vis/third_party/glm/gtx/type_aligned.hpp
diff --git a/include/third_party/glm/gtx/type_aligned.inl b/include/pli_vis/third_party/glm/gtx/type_aligned.inl
similarity index 100%
rename from include/third_party/glm/gtx/type_aligned.inl
rename to include/pli_vis/third_party/glm/gtx/type_aligned.inl
diff --git a/include/third_party/glm/gtx/type_trait.hpp b/include/pli_vis/third_party/glm/gtx/type_trait.hpp
similarity index 100%
rename from include/third_party/glm/gtx/type_trait.hpp
rename to include/pli_vis/third_party/glm/gtx/type_trait.hpp
diff --git a/include/third_party/glm/gtx/type_trait.inl b/include/pli_vis/third_party/glm/gtx/type_trait.inl
similarity index 100%
rename from include/third_party/glm/gtx/type_trait.inl
rename to include/pli_vis/third_party/glm/gtx/type_trait.inl
diff --git a/include/third_party/glm/gtx/vector_angle.hpp b/include/pli_vis/third_party/glm/gtx/vector_angle.hpp
similarity index 100%
rename from include/third_party/glm/gtx/vector_angle.hpp
rename to include/pli_vis/third_party/glm/gtx/vector_angle.hpp
diff --git a/include/third_party/glm/gtx/vector_angle.inl b/include/pli_vis/third_party/glm/gtx/vector_angle.inl
similarity index 100%
rename from include/third_party/glm/gtx/vector_angle.inl
rename to include/pli_vis/third_party/glm/gtx/vector_angle.inl
diff --git a/include/third_party/glm/gtx/vector_query.hpp b/include/pli_vis/third_party/glm/gtx/vector_query.hpp
similarity index 100%
rename from include/third_party/glm/gtx/vector_query.hpp
rename to include/pli_vis/third_party/glm/gtx/vector_query.hpp
diff --git a/include/third_party/glm/gtx/vector_query.inl b/include/pli_vis/third_party/glm/gtx/vector_query.inl
similarity index 100%
rename from include/third_party/glm/gtx/vector_query.inl
rename to include/pli_vis/third_party/glm/gtx/vector_query.inl
diff --git a/include/third_party/glm/gtx/wrap.hpp b/include/pli_vis/third_party/glm/gtx/wrap.hpp
similarity index 100%
rename from include/third_party/glm/gtx/wrap.hpp
rename to include/pli_vis/third_party/glm/gtx/wrap.hpp
diff --git a/include/third_party/glm/gtx/wrap.inl b/include/pli_vis/third_party/glm/gtx/wrap.inl
similarity index 100%
rename from include/third_party/glm/gtx/wrap.inl
rename to include/pli_vis/third_party/glm/gtx/wrap.inl
diff --git a/include/third_party/glm/integer.hpp b/include/pli_vis/third_party/glm/integer.hpp
similarity index 100%
rename from include/third_party/glm/integer.hpp
rename to include/pli_vis/third_party/glm/integer.hpp
diff --git a/include/third_party/glm/mat2x2.hpp b/include/pli_vis/third_party/glm/mat2x2.hpp
similarity index 100%
rename from include/third_party/glm/mat2x2.hpp
rename to include/pli_vis/third_party/glm/mat2x2.hpp
diff --git a/include/third_party/glm/mat2x3.hpp b/include/pli_vis/third_party/glm/mat2x3.hpp
similarity index 100%
rename from include/third_party/glm/mat2x3.hpp
rename to include/pli_vis/third_party/glm/mat2x3.hpp
diff --git a/include/third_party/glm/mat2x4.hpp b/include/pli_vis/third_party/glm/mat2x4.hpp
similarity index 100%
rename from include/third_party/glm/mat2x4.hpp
rename to include/pli_vis/third_party/glm/mat2x4.hpp
diff --git a/include/third_party/glm/mat3x2.hpp b/include/pli_vis/third_party/glm/mat3x2.hpp
similarity index 100%
rename from include/third_party/glm/mat3x2.hpp
rename to include/pli_vis/third_party/glm/mat3x2.hpp
diff --git a/include/third_party/glm/mat3x3.hpp b/include/pli_vis/third_party/glm/mat3x3.hpp
similarity index 100%
rename from include/third_party/glm/mat3x3.hpp
rename to include/pli_vis/third_party/glm/mat3x3.hpp
diff --git a/include/third_party/glm/mat3x4.hpp b/include/pli_vis/third_party/glm/mat3x4.hpp
similarity index 100%
rename from include/third_party/glm/mat3x4.hpp
rename to include/pli_vis/third_party/glm/mat3x4.hpp
diff --git a/include/third_party/glm/mat4x2.hpp b/include/pli_vis/third_party/glm/mat4x2.hpp
similarity index 100%
rename from include/third_party/glm/mat4x2.hpp
rename to include/pli_vis/third_party/glm/mat4x2.hpp
diff --git a/include/third_party/glm/mat4x3.hpp b/include/pli_vis/third_party/glm/mat4x3.hpp
similarity index 100%
rename from include/third_party/glm/mat4x3.hpp
rename to include/pli_vis/third_party/glm/mat4x3.hpp
diff --git a/include/third_party/glm/mat4x4.hpp b/include/pli_vis/third_party/glm/mat4x4.hpp
similarity index 100%
rename from include/third_party/glm/mat4x4.hpp
rename to include/pli_vis/third_party/glm/mat4x4.hpp
diff --git a/include/third_party/glm/matrix.hpp b/include/pli_vis/third_party/glm/matrix.hpp
similarity index 100%
rename from include/third_party/glm/matrix.hpp
rename to include/pli_vis/third_party/glm/matrix.hpp
diff --git a/include/third_party/glm/packing.hpp b/include/pli_vis/third_party/glm/packing.hpp
similarity index 100%
rename from include/third_party/glm/packing.hpp
rename to include/pli_vis/third_party/glm/packing.hpp
diff --git a/include/third_party/glm/simd/common.h b/include/pli_vis/third_party/glm/simd/common.h
similarity index 100%
rename from include/third_party/glm/simd/common.h
rename to include/pli_vis/third_party/glm/simd/common.h
diff --git a/include/third_party/glm/simd/exponential.h b/include/pli_vis/third_party/glm/simd/exponential.h
similarity index 100%
rename from include/third_party/glm/simd/exponential.h
rename to include/pli_vis/third_party/glm/simd/exponential.h
diff --git a/include/third_party/glm/simd/geometric.h b/include/pli_vis/third_party/glm/simd/geometric.h
similarity index 100%
rename from include/third_party/glm/simd/geometric.h
rename to include/pli_vis/third_party/glm/simd/geometric.h
diff --git a/include/third_party/glm/simd/integer.h b/include/pli_vis/third_party/glm/simd/integer.h
similarity index 100%
rename from include/third_party/glm/simd/integer.h
rename to include/pli_vis/third_party/glm/simd/integer.h
diff --git a/include/third_party/glm/simd/matrix.h b/include/pli_vis/third_party/glm/simd/matrix.h
similarity index 100%
rename from include/third_party/glm/simd/matrix.h
rename to include/pli_vis/third_party/glm/simd/matrix.h
diff --git a/include/third_party/glm/simd/packing.h b/include/pli_vis/third_party/glm/simd/packing.h
similarity index 100%
rename from include/third_party/glm/simd/packing.h
rename to include/pli_vis/third_party/glm/simd/packing.h
diff --git a/include/third_party/glm/simd/platform.h b/include/pli_vis/third_party/glm/simd/platform.h
similarity index 100%
rename from include/third_party/glm/simd/platform.h
rename to include/pli_vis/third_party/glm/simd/platform.h
diff --git a/include/third_party/glm/simd/trigonometric.h b/include/pli_vis/third_party/glm/simd/trigonometric.h
similarity index 100%
rename from include/third_party/glm/simd/trigonometric.h
rename to include/pli_vis/third_party/glm/simd/trigonometric.h
diff --git a/include/third_party/glm/simd/vector_relational.h b/include/pli_vis/third_party/glm/simd/vector_relational.h
similarity index 100%
rename from include/third_party/glm/simd/vector_relational.h
rename to include/pli_vis/third_party/glm/simd/vector_relational.h
diff --git a/include/third_party/glm/trigonometric.hpp b/include/pli_vis/third_party/glm/trigonometric.hpp
similarity index 100%
rename from include/third_party/glm/trigonometric.hpp
rename to include/pli_vis/third_party/glm/trigonometric.hpp
diff --git a/include/third_party/glm/vec2.hpp b/include/pli_vis/third_party/glm/vec2.hpp
similarity index 100%
rename from include/third_party/glm/vec2.hpp
rename to include/pli_vis/third_party/glm/vec2.hpp
diff --git a/include/third_party/glm/vec3.hpp b/include/pli_vis/third_party/glm/vec3.hpp
similarity index 100%
rename from include/third_party/glm/vec3.hpp
rename to include/pli_vis/third_party/glm/vec3.hpp
diff --git a/include/third_party/glm/vec4.hpp b/include/pli_vis/third_party/glm/vec4.hpp
similarity index 100%
rename from include/third_party/glm/vec4.hpp
rename to include/pli_vis/third_party/glm/vec4.hpp
diff --git a/include/third_party/glm/vector_relational.hpp b/include/pli_vis/third_party/glm/vector_relational.hpp
similarity index 100%
rename from include/third_party/glm/vector_relational.hpp
rename to include/pli_vis/third_party/glm/vector_relational.hpp
diff --git a/include/third_party/highfive/H5Attribute.hpp b/include/pli_vis/third_party/highfive/H5Attribute.hpp
similarity index 100%
rename from include/third_party/highfive/H5Attribute.hpp
rename to include/pli_vis/third_party/highfive/H5Attribute.hpp
diff --git a/include/third_party/highfive/H5DataSet.hpp b/include/pli_vis/third_party/highfive/H5DataSet.hpp
similarity index 100%
rename from include/third_party/highfive/H5DataSet.hpp
rename to include/pli_vis/third_party/highfive/H5DataSet.hpp
diff --git a/include/third_party/highfive/H5DataSpace.hpp b/include/pli_vis/third_party/highfive/H5DataSpace.hpp
similarity index 100%
rename from include/third_party/highfive/H5DataSpace.hpp
rename to include/pli_vis/third_party/highfive/H5DataSpace.hpp
diff --git a/include/third_party/highfive/H5DataType.hpp b/include/pli_vis/third_party/highfive/H5DataType.hpp
similarity index 100%
rename from include/third_party/highfive/H5DataType.hpp
rename to include/pli_vis/third_party/highfive/H5DataType.hpp
diff --git a/include/third_party/highfive/H5Exception.hpp b/include/pli_vis/third_party/highfive/H5Exception.hpp
similarity index 100%
rename from include/third_party/highfive/H5Exception.hpp
rename to include/pli_vis/third_party/highfive/H5Exception.hpp
diff --git a/include/third_party/highfive/H5File.hpp b/include/pli_vis/third_party/highfive/H5File.hpp
similarity index 100%
rename from include/third_party/highfive/H5File.hpp
rename to include/pli_vis/third_party/highfive/H5File.hpp
diff --git a/include/third_party/highfive/H5Group.hpp b/include/pli_vis/third_party/highfive/H5Group.hpp
similarity index 100%
rename from include/third_party/highfive/H5Group.hpp
rename to include/pli_vis/third_party/highfive/H5Group.hpp
diff --git a/include/third_party/highfive/H5Object.hpp b/include/pli_vis/third_party/highfive/H5Object.hpp
similarity index 100%
rename from include/third_party/highfive/H5Object.hpp
rename to include/pli_vis/third_party/highfive/H5Object.hpp
diff --git a/include/third_party/highfive/H5Selection.hpp b/include/pli_vis/third_party/highfive/H5Selection.hpp
similarity index 100%
rename from include/third_party/highfive/H5Selection.hpp
rename to include/pli_vis/third_party/highfive/H5Selection.hpp
diff --git a/include/third_party/highfive/bits/H5Attribute_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Attribute_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Attribute_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Attribute_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Converter_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Converter_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Converter_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Converter_misc.hpp
diff --git a/include/third_party/highfive/bits/H5DataSet_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5DataSet_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5DataSet_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5DataSet_misc.hpp
diff --git a/include/third_party/highfive/bits/H5DataType_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5DataType_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5DataType_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5DataType_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Dataspace_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Dataspace_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Dataspace_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Dataspace_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Exception_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Exception_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Exception_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Exception_misc.hpp
diff --git a/include/third_party/highfive/bits/H5File_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5File_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5File_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5File_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Group_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Group_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Group_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Group_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Node_traits.hpp b/include/pli_vis/third_party/highfive/bits/H5Node_traits.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Node_traits.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Node_traits.hpp
diff --git a/include/third_party/highfive/bits/H5Node_traits_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Node_traits_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Node_traits_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Node_traits_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Object_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Object_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Object_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Object_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Selection_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Selection_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Selection_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Selection_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Slice_traits.hpp b/include/pli_vis/third_party/highfive/bits/H5Slice_traits.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Slice_traits.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Slice_traits.hpp
diff --git a/include/third_party/highfive/bits/H5Slice_traits_misc.hpp b/include/pli_vis/third_party/highfive/bits/H5Slice_traits_misc.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Slice_traits_misc.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Slice_traits_misc.hpp
diff --git a/include/third_party/highfive/bits/H5Utils.hpp b/include/pli_vis/third_party/highfive/bits/H5Utils.hpp
similarity index 100%
rename from include/third_party/highfive/bits/H5Utils.hpp
rename to include/pli_vis/third_party/highfive/bits/H5Utils.hpp
diff --git a/include/pli_vis/third_party/qwt/qwt.h b/include/pli_vis/third_party/qwt/qwt.h
new file mode 100644
index 0000000000000000000000000000000000000000..e363dd57aacf37546267078013b6740aa2e9879b
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt.h
@@ -0,0 +1,22 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_H
+#define QWT_H
+
+#include "qwt/qwt_global.h"
+
+/*!
+  Some constants for use within Qwt.
+*/
+namespace Qwt
+{
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_abstract_legend.h b/include/pli_vis/third_party/qwt/qwt_abstract_legend.h
new file mode 100644
index 0000000000000000000000000000000000000000..642065845825e48c3fa54c334179e744e4153531
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_abstract_legend.h
@@ -0,0 +1,71 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ABSTRACT_LEGEND_H
+#define QWT_ABSTRACT_LEGEND_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_legend_data.h"
+#include <qframe.h>
+#include <qlist.h>
+
+class QVariant;
+
+/*!
+  \brief Abstract base class for legend widgets
+
+  Legends, that need to be under control of the QwtPlot layout system
+  need to be derived from QwtAbstractLegend. 
+
+  \note Other type of legends can be implemented by connecting to
+        the QwtPlot::legendDataChanged() signal. But as these legends
+        are unknown to the plot layout system the layout code
+        ( on screen and for QwtPlotRenderer ) need to be organized
+        in application code.
+
+  \sa QwtLegend
+ */
+class QWT_EXPORT QwtAbstractLegend : public QFrame
+{
+    Q_OBJECT
+
+public:
+    explicit QwtAbstractLegend( QWidget *parent = NULL );
+    virtual ~QwtAbstractLegend();
+
+    /*!
+      Render the legend into a given rectangle.
+
+      \param painter Painter
+      \param rect Bounding rectangle
+      \param fillBackground When true, fill rect with the widget background 
+
+      \sa renderLegend() is used by QwtPlotRenderer
+    */
+    virtual void renderLegend( QPainter *painter, 
+        const QRectF &rect, bool fillBackground ) const = 0;
+
+    //! \return True, when no plot item is inserted
+    virtual bool isEmpty() const = 0;
+
+    virtual int scrollExtent( Qt::Orientation ) const;
+
+public Q_SLOTS:
+
+    /*!
+      \brief Update the entries for a plot item
+
+      \param itemInfo Info about an item
+      \param data List of legend entry attributes for the  item
+     */
+    virtual void updateLegend( const QVariant &itemInfo, 
+        const QList<QwtLegendData> &data ) = 0;
+};
+
+#endif 
diff --git a/include/pli_vis/third_party/qwt/qwt_abstract_scale.h b/include/pli_vis/third_party/qwt/qwt_abstract_scale.h
new file mode 100644
index 0000000000000000000000000000000000000000..75175cc8d82d1771715538ec67ff35c425fe695e
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_abstract_scale.h
@@ -0,0 +1,105 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ABSTRACT_SCALE_H
+#define QWT_ABSTRACT_SCALE_H
+
+#include "qwt/qwt_global.h"
+#include <qwidget.h>
+
+class QwtScaleEngine;
+class QwtAbstractScaleDraw;
+class QwtScaleDiv;
+class QwtScaleMap;
+class QwtInterval;
+
+/*!
+  \brief An abstract base class for widgets having a scale
+
+  The scale of an QwtAbstractScale is determined by a QwtScaleDiv
+  definition, that contains the boundaries and the ticks of the scale.
+  The scale is painted using a QwtScaleDraw object.
+
+  The scale division might be assigned explicitly - but usually
+  it is calculated from the boundaries using a QwtScaleEngine. 
+
+  The scale engine also decides the type of transformation of the scale 
+  ( linear, logarithmic ... ).
+*/
+
+class QWT_EXPORT QwtAbstractScale: public QWidget
+{
+    Q_OBJECT
+
+    Q_PROPERTY( double lowerBound READ lowerBound WRITE setLowerBound )
+    Q_PROPERTY( double upperBound READ upperBound WRITE setUpperBound )
+
+    Q_PROPERTY( int scaleMaxMajor READ scaleMaxMajor WRITE setScaleMaxMajor )
+    Q_PROPERTY( int scaleMaxMinor READ scaleMaxMinor WRITE setScaleMaxMinor )
+
+    Q_PROPERTY( double scaleStepSize READ scaleStepSize WRITE setScaleStepSize )
+
+public:
+    QwtAbstractScale( QWidget *parent = NULL );
+    virtual ~QwtAbstractScale();
+
+    void setScale( double lowerBound, double upperBound );
+    void setScale( const QwtInterval & );
+    void setScale( const QwtScaleDiv & );
+
+    const QwtScaleDiv& scaleDiv() const;
+
+    void setLowerBound( double value );
+    double lowerBound() const;
+
+    void setUpperBound( double value );
+    double upperBound() const;
+
+    void setScaleStepSize( double stepSize );
+    double scaleStepSize() const;
+
+    void setScaleMaxMajor( int ticks );
+    int scaleMaxMinor() const;
+
+    void setScaleMaxMinor( int ticks );
+    int scaleMaxMajor() const;
+
+    void setScaleEngine( QwtScaleEngine * );
+    const QwtScaleEngine *scaleEngine() const;
+    QwtScaleEngine *scaleEngine();
+
+    int transform( double ) const;
+    double invTransform( int ) const;
+
+    bool isInverted() const;
+
+    double minimum() const;
+    double maximum() const;
+
+    const QwtScaleMap &scaleMap() const;
+
+protected:
+    void rescale( double lowerBound, 
+        double upperBound, double stepSize );
+
+    void setAbstractScaleDraw( QwtAbstractScaleDraw * );
+
+    const QwtAbstractScaleDraw *abstractScaleDraw() const;
+    QwtAbstractScaleDraw *abstractScaleDraw();
+
+    virtual void scaleChange();
+
+private:
+    void updateScaleDraw();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_abstract_scale_draw.h b/include/pli_vis/third_party/qwt/qwt_abstract_scale_draw.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ddbdf33781ae3538f5eaed59414399ec2de7a0a
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_abstract_scale_draw.h
@@ -0,0 +1,141 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ABSTRACT_SCALE_DRAW_H
+#define QWT_ABSTRACT_SCALE_DRAW_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_text.h"
+
+class QPalette;
+class QPainter;
+class QFont;
+class QwtTransform;
+class QwtScaleMap;
+
+/*!
+  \brief A abstract base class for drawing scales
+
+  QwtAbstractScaleDraw can be used to draw linear or logarithmic scales.
+
+  After a scale division has been specified as a QwtScaleDiv object
+  using setScaleDiv(), the scale can be drawn with the draw() member.
+*/
+class QWT_EXPORT QwtAbstractScaleDraw
+{
+public:
+
+    /*!
+       Components of a scale
+       \sa enableComponent(), hasComponent
+    */
+    enum ScaleComponent
+    {
+        //! Backbone = the line where the ticks are located
+        Backbone = 0x01,
+
+        //! Ticks
+        Ticks = 0x02,
+
+        //! Labels
+        Labels = 0x04
+    };
+
+    //! Scale components
+    typedef QFlags<ScaleComponent> ScaleComponents;
+
+    QwtAbstractScaleDraw();
+    virtual ~QwtAbstractScaleDraw();
+
+    void setScaleDiv( const QwtScaleDiv &s );
+    const QwtScaleDiv& scaleDiv() const;
+
+    void setTransformation( QwtTransform * );
+    const QwtScaleMap &scaleMap() const;
+    QwtScaleMap &scaleMap();
+
+    void enableComponent( ScaleComponent, bool enable = true );
+    bool hasComponent( ScaleComponent ) const;
+
+    void setTickLength( QwtScaleDiv::TickType, double length );
+    double tickLength( QwtScaleDiv::TickType ) const;
+    double maxTickLength() const;
+
+    void setSpacing( double margin );
+    double spacing() const;
+
+    void setPenWidth( int width );
+    int penWidth() const;
+
+    virtual void draw( QPainter *, const QPalette & ) const;
+
+    virtual QwtText label( double ) const;
+
+    /*!
+      Calculate the extent
+
+      The extent is the distance from the baseline to the outermost
+      pixel of the scale draw in opposite to its orientation.
+      It is at least minimumExtent() pixels.
+
+      \param font Font used for drawing the tick labels
+      \return Number of pixels
+
+      \sa setMinimumExtent(), minimumExtent()
+    */
+    virtual double extent( const QFont &font ) const = 0;
+
+    void setMinimumExtent( double );
+    double minimumExtent() const;
+
+protected:
+    /*!
+       Draw a tick
+
+       \param painter Painter
+       \param value Value of the tick
+       \param len Length of the tick
+
+       \sa drawBackbone(), drawLabel()
+    */
+    virtual void drawTick( QPainter *painter, double value, double len ) const = 0;
+
+    /*!
+      Draws the baseline of the scale
+      \param painter Painter
+
+      \sa drawTick(), drawLabel()
+    */
+    virtual void drawBackbone( QPainter *painter ) const = 0;
+
+    /*!
+        Draws the label for a major scale tick
+
+        \param painter Painter
+        \param value Value
+
+        \sa drawTick(), drawBackbone()
+    */
+    virtual void drawLabel( QPainter *painter, double value ) const = 0;
+
+    void invalidateCache();
+    const QwtText &tickLabel( const QFont &, double value ) const;
+
+private:
+    QwtAbstractScaleDraw( const QwtAbstractScaleDraw & );
+    QwtAbstractScaleDraw &operator=( const QwtAbstractScaleDraw & );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtAbstractScaleDraw::ScaleComponents )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_abstract_slider.h b/include/pli_vis/third_party/qwt/qwt_abstract_slider.h
new file mode 100644
index 0000000000000000000000000000000000000000..fa97bc54958517b93e2e49110021f40987d157e2
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_abstract_slider.h
@@ -0,0 +1,167 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ABSTRACT_SLIDER_H
+#define QWT_ABSTRACT_SLIDER_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_abstract_scale.h"
+
+/*!
+  \brief An abstract base class for slider widgets with a scale
+
+  A slider widget displays a value according to a scale.
+  The class is designed as a common super class for widgets like 
+  QwtKnob, QwtDial and QwtSlider.
+
+  When the slider is nor readOnly() its value can be modified 
+  by keyboard, mouse and wheel inputs. 
+
+  The range of the slider is divided into a number of steps from
+  which the value increments according to user inputs depend. 
+  Only for linear scales the number of steps correspond with
+  a fixed step size.
+*/
+
+class QWT_EXPORT QwtAbstractSlider: public QwtAbstractScale
+{
+    Q_OBJECT
+
+    Q_PROPERTY( double value READ value WRITE setValue )
+
+    Q_PROPERTY( uint totalSteps READ totalSteps WRITE setTotalSteps )
+    Q_PROPERTY( uint singleSteps READ singleSteps WRITE setSingleSteps )
+    Q_PROPERTY( uint pageSteps READ pageSteps WRITE setPageSteps )
+    Q_PROPERTY( bool stepAlignment READ stepAlignment WRITE setStepAlignment )
+
+    Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
+    Q_PROPERTY( bool tracking READ isTracking WRITE setTracking )
+    Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping )
+
+    Q_PROPERTY( bool invertedControls READ invertedControls WRITE setInvertedControls )
+
+public:
+    explicit QwtAbstractSlider( QWidget *parent = NULL );
+    virtual ~QwtAbstractSlider();
+
+    void setValid( bool );
+    bool isValid() const;
+
+    double value() const;
+
+    void setWrapping( bool );
+    bool wrapping() const;
+
+    void setTotalSteps( uint );
+    uint totalSteps() const;
+
+    void setSingleSteps( uint );
+    uint singleSteps() const;
+
+    void setPageSteps( uint );
+    uint pageSteps() const;
+
+    void setStepAlignment( bool ); 
+    bool stepAlignment() const;
+
+    void setTracking( bool );
+    bool isTracking() const;
+
+    void setReadOnly( bool );
+    bool isReadOnly() const;
+
+    void setInvertedControls( bool );
+    bool invertedControls() const;
+
+public Q_SLOTS:
+    void setValue( double val );
+
+Q_SIGNALS:
+
+    /*!
+      \brief Notify a change of value.
+
+      When tracking is enabled (default setting), 
+      this signal will be emitted every time the value changes. 
+
+      \param value New value
+
+      \sa setTracking(), sliderMoved()
+    */
+    void valueChanged( double value );
+
+    /*!
+      This signal is emitted when the user presses the
+      movable part of the slider.
+    */
+    void sliderPressed();
+
+    /*!
+      This signal is emitted when the user releases the
+      movable part of the slider.
+    */
+    void sliderReleased();
+
+    /*!
+      This signal is emitted when the user moves the
+      slider with the mouse.
+
+      \param value New value
+
+      \sa valueChanged()
+    */
+    void sliderMoved( double value );
+
+protected:
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void mouseReleaseEvent( QMouseEvent * );
+    virtual void mouseMoveEvent( QMouseEvent * );
+    virtual void keyPressEvent( QKeyEvent * );
+    virtual void wheelEvent( QWheelEvent * );
+
+    /*!
+      \brief Determine what to do when the user presses a mouse button.
+
+      \param pos Mouse position
+
+      \retval True, when pos is a valid scroll position
+      \sa scrolledTo()
+    */
+    virtual bool isScrollPosition( const QPoint &pos ) const = 0;
+
+    /*!
+      \brief Determine the value for a new position of the
+             movable part of the slider
+
+      \param pos Mouse position
+
+      \return Value for the mouse position
+      \sa isScrollPosition()
+    */
+    virtual double scrolledTo( const QPoint &pos ) const = 0;
+
+    void incrementValue( int numSteps );
+
+    virtual void scaleChange();
+
+protected:
+    virtual void sliderChange();
+
+    double incrementedValue( 
+        double value, int stepCount ) const;
+
+private:
+    double alignedValue( double ) const;
+    double boundedValue( double ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_analog_clock.h b/include/pli_vis/third_party/qwt/qwt_analog_clock.h
new file mode 100644
index 0000000000000000000000000000000000000000..2808d6981f9bde3f203d22fe0d36d4862899178f
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_analog_clock.h
@@ -0,0 +1,93 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ANALOG_CLOCK_H
+#define QWT_ANALOG_CLOCK_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_dial.h"
+#include "qwt/qwt_dial_needle.h"
+#include <qdatetime.h>
+
+/*!
+  \brief An analog clock
+
+  \image html analogclock.png
+
+  \par Example
+  \code 
+  #include <qwt_analog_clock.h>
+
+  QwtAnalogClock *clock = new QwtAnalogClock(...);
+  clock->scaleDraw()->setPenWidth(3);
+  clock->setLineWidth(6);
+  clock->setFrameShadow(QwtDial::Sunken);
+  clock->setTime();
+
+  // update the clock every second
+  QTimer *timer = new QTimer(clock);
+  timer->connect(timer, SIGNAL(timeout()), clock, SLOT(setCurrentTime()));
+  timer->start(1000);
+
+  \endcode
+
+  \note The examples/dials example shows how to use QwtAnalogClock.
+*/
+
+class QWT_EXPORT QwtAnalogClock: public QwtDial
+{
+    Q_OBJECT
+
+public:
+    /*!
+        Hand type
+        \sa setHand(), hand()
+    */
+    enum Hand
+    {
+        //! Needle displaying the seconds
+        SecondHand,
+
+        //! Needle displaying the minutes
+        MinuteHand,
+
+        //! Needle displaying the hours
+        HourHand,
+
+        //! Number of needles
+        NHands
+    };
+
+    explicit QwtAnalogClock( QWidget* parent = NULL );
+    virtual ~QwtAnalogClock();
+
+    void setHand( Hand, QwtDialNeedle * );
+
+    const QwtDialNeedle *hand( Hand ) const;
+    QwtDialNeedle *hand( Hand );
+
+public Q_SLOTS:
+    void setCurrentTime();
+    void setTime( const QTime & );
+
+protected:
+    virtual void drawNeedle( QPainter *, const QPointF &,
+        double radius, double direction, QPalette::ColorGroup ) const;
+
+    virtual void drawHand( QPainter *, Hand, const QPointF &,
+        double radius, double direction, QPalette::ColorGroup ) const;
+
+private:
+    // use setHand instead
+    void setNeedle( QwtDialNeedle * );
+
+    QwtDialNeedle *d_hand[NHands];
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_arrow_button.h b/include/pli_vis/third_party/qwt/qwt_arrow_button.h
new file mode 100644
index 0000000000000000000000000000000000000000..f380d9df148647676aecf6712cd24549ab4e82dd
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_arrow_button.h
@@ -0,0 +1,52 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ARROW_BUTTON_H
+#define QWT_ARROW_BUTTON_H
+
+#include "qwt/qwt_global.h"
+#include <qpushbutton.h>
+
+/*!
+  \brief Arrow Button
+
+  A push button with one or more filled triangles on its front.
+  An Arrow button can have 1 to 3 arrows in a row, pointing
+  up, down, left or right.
+*/
+class QWT_EXPORT QwtArrowButton : public QPushButton
+{
+public:
+    explicit QwtArrowButton ( int num, Qt::ArrowType, QWidget *parent = NULL );
+    virtual ~QwtArrowButton();
+
+    Qt::ArrowType arrowType() const;
+    int num() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+protected:
+    virtual void paintEvent( QPaintEvent *event );
+
+    virtual void drawButtonLabel( QPainter *p );
+    virtual void drawArrow( QPainter *,
+        const QRect &, Qt::ArrowType ) const;
+    virtual QRect labelRect() const;
+    virtual QSize arrowSize( Qt::ArrowType,
+        const QSize &boundingSize ) const;
+
+    virtual void keyPressEvent( QKeyEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_clipper.h b/include/pli_vis/third_party/qwt/qwt_clipper.h
new file mode 100644
index 0000000000000000000000000000000000000000..2d9b1ebfb1cf52063755ec77c0541d2cae06ed32
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_clipper.h
@@ -0,0 +1,40 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_CLIPPER_H
+#define QWT_CLIPPER_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_interval.h"
+#include <qpolygon.h>
+#include <qvector.h>
+
+class QRect;
+class QRectF;
+
+/*!
+  \brief Some clipping algorithms
+*/
+
+class QWT_EXPORT QwtClipper
+{
+public:
+    static QPolygon clipPolygon( const QRect &, 
+        const QPolygon &, bool closePolygon = false );
+    static QPolygon clipPolygon( const QRectF &, 
+        const QPolygon &, bool closePolygon = false );
+
+    static QPolygonF clipPolygonF( const QRectF &, 
+        const QPolygonF &, bool closePolygon = false );
+
+    static QVector<QwtInterval> clipCircle(
+        const QRectF &, const QPointF &, double radius );
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_color_map.h b/include/pli_vis/third_party/qwt/qwt_color_map.h
new file mode 100644
index 0000000000000000000000000000000000000000..f467ffb55f0f0261412acc54b3d7f7b0c1332d13
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_color_map.h
@@ -0,0 +1,200 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COLOR_MAP_H
+#define QWT_COLOR_MAP_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_interval.h"
+#include <qcolor.h>
+#include <qvector.h>
+
+/*!
+  \brief QwtColorMap is used to map values into colors.
+
+  For displaying 3D data on a 2D plane the 3rd dimension is often
+  displayed using colors, like f.e in a spectrogram.
+
+  Each color map is optimized to return colors for only one of the
+  following image formats:
+
+  - QImage::Format_Indexed8\n
+  - QImage::Format_ARGB32\n
+
+  \sa QwtPlotSpectrogram, QwtScaleWidget
+*/
+
+class QWT_EXPORT QwtColorMap
+{
+public:
+    /*!
+        Format for color mapping
+        \sa rgb(), colorIndex(), colorTable()
+    */
+
+    enum Format
+    {
+        //! The map is intended to map into RGB values.
+        RGB,
+
+        /*!
+          The map is intended to map into 8 bit values, that
+          are indices into the color table.
+         */
+        Indexed
+    };
+
+    QwtColorMap( Format = QwtColorMap::RGB );
+    virtual ~QwtColorMap();
+
+    Format format() const;
+
+    /*!
+       Map a value of a given interval into a RGB value.
+
+       \param interval Range for the values
+       \param value Value
+       \return RGB value, corresponding to value
+    */
+    virtual QRgb rgb( const QwtInterval &interval,
+        double value ) const = 0;
+
+    /*!
+       Map a value of a given interval into a color index
+
+       \param interval Range for the values
+       \param value Value
+       \return color index, corresponding to value
+     */
+    virtual unsigned char colorIndex(
+        const QwtInterval &interval, double value ) const = 0;
+
+    QColor color( const QwtInterval &, double value ) const;
+    virtual QVector<QRgb> colorTable( const QwtInterval & ) const;
+
+private:
+    Format d_format;
+};
+
+/*!
+  \brief QwtLinearColorMap builds a color map from color stops.
+
+  A color stop is a color at a specific position. The valid
+  range for the positions is [0.0, 1.0]. When mapping a value
+  into a color it is translated into this interval according to mode().
+*/
+class QWT_EXPORT QwtLinearColorMap: public QwtColorMap
+{
+public:
+    /*!
+       Mode of color map
+       \sa setMode(), mode()
+    */
+    enum Mode
+    {
+        //! Return the color from the next lower color stop
+        FixedColors,
+
+        //! Interpolating the colors of the adjacent stops.
+        ScaledColors
+    };
+
+    QwtLinearColorMap( QwtColorMap::Format = QwtColorMap::RGB );
+    QwtLinearColorMap( const QColor &from, const QColor &to,
+        QwtColorMap::Format = QwtColorMap::RGB );
+
+    virtual ~QwtLinearColorMap();
+
+    void setMode( Mode );
+    Mode mode() const;
+
+    void setColorInterval( const QColor &color1, const QColor &color2 );
+    void addColorStop( double value, const QColor& );
+    QVector<double> colorStops() const;
+
+    QColor color1() const;
+    QColor color2() const;
+
+    virtual QRgb rgb( const QwtInterval &, double value ) const;
+    virtual unsigned char colorIndex(
+        const QwtInterval &, double value ) const;
+
+    class ColorStops;
+
+private:
+    // Disabled copy constructor and operator=
+    QwtLinearColorMap( const QwtLinearColorMap & );
+    QwtLinearColorMap &operator=( const QwtLinearColorMap & );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+  \brief QwtAlphaColorMap varies the alpha value of a color
+*/
+class QWT_EXPORT QwtAlphaColorMap: public QwtColorMap
+{
+public:
+    QwtAlphaColorMap( const QColor & = QColor( Qt::gray ) );
+    virtual ~QwtAlphaColorMap();
+
+    void setColor( const QColor & );
+    QColor color() const;
+
+    virtual QRgb rgb( const QwtInterval &, double value ) const;
+
+private:
+    QwtAlphaColorMap( const QwtAlphaColorMap & );
+    QwtAlphaColorMap &operator=( const QwtAlphaColorMap & );
+
+    virtual unsigned char colorIndex(
+        const QwtInterval &, double value ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+
+/*!
+   Map a value into a color
+
+   \param interval Valid interval for values
+   \param value Value
+
+   \return Color corresponding to value
+
+   \warning This method is slow for Indexed color maps. If it is
+            necessary to map many values, its better to get the
+            color table once and find the color using colorIndex().
+*/
+inline QColor QwtColorMap::color(
+    const QwtInterval &interval, double value ) const
+{
+    if ( d_format == RGB )
+    {
+        return QColor::fromRgba( rgb( interval, value ) );
+    }
+    else
+    {
+        const unsigned int index = colorIndex( interval, value );
+        return colorTable( interval )[index]; // slow
+    }
+}
+
+/*!
+   \return Intended format of the color map
+   \sa Format
+*/
+inline QwtColorMap::Format QwtColorMap::format() const
+{
+    return d_format;
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_column_symbol.h b/include/pli_vis/third_party/qwt/qwt_column_symbol.h
new file mode 100644
index 0000000000000000000000000000000000000000..7a04b1161229d0ce8ea55462941a16ac3aed1fd1
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_column_symbol.h
@@ -0,0 +1,161 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COLUMN_SYMBOL_H
+#define QWT_COLUMN_SYMBOL_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_interval.h"
+#include <qpen.h>
+#include <qsize.h>
+#include <qrect.h>
+
+class QPainter;
+class QPalette;
+class QRect;
+class QwtText;
+
+/*!
+    \brief Directed rectangle representing bounding rectangle and orientation
+    of a column.
+*/
+class QWT_EXPORT QwtColumnRect
+{
+public:
+    //! Direction of the column
+    enum Direction
+    {
+        //! From left to right
+        LeftToRight,
+
+        //! From right to left
+        RightToLeft,
+
+        //! From bottom to top
+        BottomToTop,
+
+        //! From top to bottom
+        TopToBottom
+    };
+
+    //! Build an rectangle with invalid intervals directed BottomToTop.
+    QwtColumnRect():
+        direction( BottomToTop )
+    {
+    }
+
+    //! \return A normalized QRect built from the intervals
+    QRectF toRect() const
+    {
+        QRectF r( hInterval.minValue(), vInterval.minValue(),
+            hInterval.maxValue() - hInterval.minValue(),
+            vInterval.maxValue() - vInterval.minValue() );
+        r = r.normalized();
+
+        if ( hInterval.borderFlags() & QwtInterval::ExcludeMinimum )
+            r.adjust( 1, 0, 0, 0 );
+        if ( hInterval.borderFlags() & QwtInterval::ExcludeMaximum )
+            r.adjust( 0, 0, -1, 0 );
+        if ( vInterval.borderFlags() & QwtInterval::ExcludeMinimum )
+            r.adjust( 0, 1, 0, 0 );
+        if ( vInterval.borderFlags() & QwtInterval::ExcludeMaximum )
+            r.adjust( 0, 0, 0, -1 );
+
+        return r;
+    }
+
+    //! \return Orientation
+    Qt::Orientation orientation() const
+    {
+        if ( direction == LeftToRight || direction == RightToLeft )
+            return Qt::Horizontal;
+
+        return Qt::Vertical;
+    }
+
+    //! Interval for the horizontal coordinates
+    QwtInterval hInterval;
+
+    //! Interval for the vertical coordinates
+    QwtInterval vInterval;
+
+    //! Direction
+    Direction direction;
+};
+
+//! A drawing primitive for columns
+class QWT_EXPORT QwtColumnSymbol
+{
+public:
+    /*!
+      Style
+      \sa setStyle(), style()
+    */
+    enum Style
+    {
+        //! No Style, the symbol draws nothing
+        NoStyle = -1,
+
+        /*!
+          The column is painted with a frame depending on the frameStyle()
+          and lineWidth() using the palette().
+         */
+        Box,
+
+        /*!
+          Styles >= QwtColumnSymbol::UserStyle are reserved for derived
+          classes of QwtColumnSymbol that overload draw() with
+          additional application specific symbol types.
+         */
+        UserStyle = 1000
+    };
+
+    /*!
+      Frame Style used in Box style().
+      \sa Style, setFrameStyle(), frameStyle(), setStyle(), setPalette()
+     */
+    enum FrameStyle
+    {
+        //! No frame
+        NoFrame,
+
+        //! A plain frame style
+        Plain,
+
+        //! A raised frame style
+        Raised
+    };
+
+public:
+    QwtColumnSymbol( Style = NoStyle );
+    virtual ~QwtColumnSymbol();
+
+    void setFrameStyle( FrameStyle style );
+    FrameStyle frameStyle() const;
+
+    void setLineWidth( int width );
+    int lineWidth() const;
+
+    void setPalette( const QPalette & );
+    const QPalette &palette() const;
+
+    void setStyle( Style );
+    Style style() const;
+
+    virtual void draw( QPainter *, const QwtColumnRect & ) const;
+
+protected:
+    void drawBox( QPainter *, const QwtColumnRect & ) const;
+
+private:
+    class PrivateData;
+    PrivateData* d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_compass.h b/include/pli_vis/third_party/qwt/qwt_compass.h
new file mode 100644
index 0000000000000000000000000000000000000000..3a1c185c11a48b5cb6bf562b4838179fc1e99b29
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_compass.h
@@ -0,0 +1,83 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COMPASS_H
+#define QWT_COMPASS_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_dial.h"
+#include "qwt/qwt_round_scale_draw.h"
+#include <qstring.h>
+#include <qmap.h>
+
+class QwtCompassRose;
+
+/*!
+  \brief A special scale draw made for QwtCompass
+
+  QwtCompassScaleDraw maps values to strings using
+  a special map, that can be modified by the application
+
+  The default map consists of the labels N, NE, E, SE, S, SW, W, NW.
+
+  \sa QwtCompass
+*/
+class QWT_EXPORT QwtCompassScaleDraw: public QwtRoundScaleDraw
+{
+public:
+    explicit QwtCompassScaleDraw();
+    explicit QwtCompassScaleDraw( const QMap<double, QString> &map );
+
+    void setLabelMap( const QMap<double, QString> &map );
+    QMap<double, QString> labelMap() const;
+
+    virtual QwtText label( double value ) const;
+
+private:
+    QMap<double, QString> d_labelMap;
+};
+
+/*!
+  \brief A Compass Widget
+
+  QwtCompass is a widget to display and enter directions. It consists
+  of a scale, an optional needle and rose.
+
+  \image html dials1.png
+
+  \note The examples/dials example shows how to use QwtCompass.
+*/
+
+class QWT_EXPORT QwtCompass: public QwtDial
+{
+    Q_OBJECT
+
+public:
+    explicit QwtCompass( QWidget* parent = NULL );
+    virtual ~QwtCompass();
+
+    void setRose( QwtCompassRose *rose );
+    const QwtCompassRose *rose() const;
+    QwtCompassRose *rose();
+
+protected:
+    virtual void drawRose( QPainter *, const QPointF &center,
+        double radius, double north, QPalette::ColorGroup ) const;
+
+    virtual void drawScaleContents( QPainter *,
+        const QPointF &center, double radius ) const;
+
+    virtual void keyPressEvent( QKeyEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_compass_rose.h b/include/pli_vis/third_party/qwt/qwt_compass_rose.h
new file mode 100644
index 0000000000000000000000000000000000000000..b023d8478a43e2b54d00e8cb8eb6ba19b2e242ef
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_compass_rose.h
@@ -0,0 +1,89 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COMPASS_ROSE_H
+#define QWT_COMPASS_ROSE_H 1
+
+#include "qwt/qwt_global.h"
+#include <qpalette.h>
+
+class QPainter;
+
+/*!
+  \brief Abstract base class for a compass rose
+*/
+class QWT_EXPORT QwtCompassRose
+{
+public:
+    //! Destructor
+    virtual ~QwtCompassRose() {};
+
+    //! Assign a palette
+    virtual void setPalette( const QPalette &p )
+    {
+        d_palette = p;
+    }
+
+    //! \return Current palette
+    const QPalette &palette() const
+    {
+        return d_palette;
+    }
+
+    /*!
+        Draw the rose
+
+        \param painter Painter
+        \param center Center point
+        \param radius Radius of the rose
+        \param north Position
+        \param colorGroup Color group
+     */
+    virtual void draw( QPainter *painter, 
+        const QPointF &center, double radius, double north,
+        QPalette::ColorGroup colorGroup = QPalette::Active ) const = 0;
+
+private:
+    QPalette d_palette;
+};
+
+/*!
+  \brief A simple rose for QwtCompass
+*/
+class QWT_EXPORT QwtSimpleCompassRose: public QwtCompassRose
+{
+public:
+    QwtSimpleCompassRose( int numThorns = 8, int numThornLevels = -1 );
+    virtual ~QwtSimpleCompassRose();
+
+    void setWidth( double w );
+    double width() const;
+
+    void setNumThorns( int count );
+    int numThorns() const;
+
+    void setNumThornLevels( int count );
+    int numThornLevels() const;
+
+    void setShrinkFactor( double factor );
+    double shrinkFactor() const;
+
+    virtual void draw( QPainter *, const QPointF &center, double radius,
+        double north, QPalette::ColorGroup = QPalette::Active ) const;
+
+    static void drawRose( QPainter *, const QPalette &,
+        const QPointF &center, double radius, double origin, double width,
+        int numThorns, int numThornLevels, double shrinkFactor );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif 
diff --git a/include/pli_vis/third_party/qwt/qwt_compat.h b/include/pli_vis/third_party/qwt/qwt_compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3568436652f25e67ba40cb81dfb741fa251e5d5
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_compat.h
@@ -0,0 +1,42 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef _QWT_COMPAT_H_
+#define _QWT_COMPAT_H_
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_interval.h"
+#include "qwt/qwt_point_3d.h"
+#include <qlist.h>
+#include <qvector.h>
+#include <qpoint.h>
+#include <qsize.h>
+#include <qrect.h>
+#include <qpolygon.h>
+
+// A couple of definition for Qwt5 compatibility
+
+#define qwtMax qMax
+#define qwtMin qMin
+#define qwtAbs qAbs
+#define qwtRound qRound
+
+#define QwtArray QVector
+
+typedef QList<double> QwtValueList;
+typedef QPointF QwtDoublePoint;
+typedef QSizeF QwtDoubleSize;
+typedef QRectF QwtDoubleRect;
+
+typedef QPolygon QwtPolygon;
+typedef QPolygonF QwtPolygonF;
+typedef QwtInterval QwtDoubleInterval;
+typedef QwtPoint3D QwtDoublePoint3D;
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_counter.h b/include/pli_vis/third_party/qwt/qwt_counter.h
new file mode 100644
index 0000000000000000000000000000000000000000..7b2530694171344875dd0702f4843f3ee07518f5
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_counter.h
@@ -0,0 +1,161 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_COUNTER_H
+#define QWT_COUNTER_H
+
+#include "qwt/qwt_global.h"
+#include <qwidget.h>
+
+/*!
+  \brief The Counter Widget
+
+  A Counter consists of a label displaying a number and
+  one ore more (up to three) push buttons on each side
+  of the label which can be used to increment or decrement
+  the counter's value.
+
+  A counter has a range from a minimum value to a maximum value
+  and a step size. When the wrapping property is set
+  the counter is circular.
+ 
+  The number of steps by which a button increments or decrements the value 
+  can be specified using setIncSteps(). The number of buttons can be 
+  changed with setNumButtons().
+
+  Example:
+\code
+#include <qwt_counter.h>
+
+QwtCounter *counter = new QwtCounter(parent);
+
+counter->setRange(0.0, 100.0);                  // From 0.0 to 100
+counter->setSingleStep( 1.0 );                  // Step size 1.0
+counter->setNumButtons(2);                      // Two buttons each side
+counter->setIncSteps(QwtCounter::Button1, 1);   // Button 1 increments 1 step
+counter->setIncSteps(QwtCounter::Button2, 20);  // Button 2 increments 20 steps
+
+connect(counter, SIGNAL(valueChanged(double)), myClass, SLOT(newValue(double)));
+\endcode
+ */
+
+class QWT_EXPORT QwtCounter : public QWidget
+{
+    Q_OBJECT
+
+    Q_PROPERTY( double value READ value WRITE setValue )
+    Q_PROPERTY( double minimum READ minimum WRITE setMinimum )
+    Q_PROPERTY( double maximum READ maximum WRITE setMaximum )
+    Q_PROPERTY( double singleStep READ singleStep WRITE setSingleStep )
+
+    Q_PROPERTY( int numButtons READ numButtons WRITE setNumButtons )
+    Q_PROPERTY( int stepButton1 READ stepButton1 WRITE setStepButton1 )
+    Q_PROPERTY( int stepButton2 READ stepButton2 WRITE setStepButton2 )
+    Q_PROPERTY( int stepButton3 READ stepButton3 WRITE setStepButton3 )
+
+    Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
+    Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping )
+
+public:
+    //! Button index
+    enum Button
+    {
+        //! Button intended for minor steps
+        Button1,
+
+        //! Button intended for medium steps
+        Button2,
+
+        //! Button intended for large steps
+        Button3,
+
+        //! Number of buttons
+        ButtonCnt
+    };
+
+    explicit QwtCounter( QWidget *parent = NULL );
+    virtual ~QwtCounter();
+
+    void setValid( bool );
+    bool isValid() const;
+
+    void setWrapping( bool );
+    bool wrapping() const;
+
+    bool isReadOnly() const;
+    void setReadOnly( bool );
+
+    void setNumButtons( int n );
+    int numButtons() const;
+
+    void setIncSteps( QwtCounter::Button btn, int nSteps );
+    int incSteps( QwtCounter::Button btn ) const;
+
+    virtual QSize sizeHint() const;
+
+    double singleStep() const;
+    void setSingleStep( double s );
+
+    void setRange( double min, double max );
+    
+    double minimum() const;
+    void setMinimum( double min );
+
+    double maximum() const;
+    void setMaximum( double max );
+
+    void setStepButton1( int nSteps );
+    int stepButton1() const;
+
+    void setStepButton2( int nSteps );
+    int stepButton2() const;
+
+    void setStepButton3( int nSteps );
+    int stepButton3() const;
+
+    double value() const;
+
+public Q_SLOTS:
+    void setValue( double );
+
+
+Q_SIGNALS:
+    /*!
+        This signal is emitted when a button has been released
+        \param value The new value
+    */
+    void buttonReleased ( double value );
+
+    /*!
+        This signal is emitted when the counter's value has changed
+        \param value The new value
+    */
+    void valueChanged ( double value );
+
+protected:
+    virtual bool event( QEvent * );
+    virtual void wheelEvent( QWheelEvent * );
+    virtual void keyPressEvent( QKeyEvent * );
+
+private Q_SLOTS:
+    void btnReleased();
+    void btnClicked();
+    void textChanged();
+
+private:
+    void incrementValue( int numSteps );
+    void initCounter();
+    void updateButtons();
+    void showNumber( double );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_curve_fitter.h b/include/pli_vis/third_party/qwt/qwt_curve_fitter.h
new file mode 100644
index 0000000000000000000000000000000000000000..9ff47838b4e43fedbec6dd61028c1a7cfe6fb561
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_curve_fitter.h
@@ -0,0 +1,139 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_CURVE_FITTER_H
+#define QWT_CURVE_FITTER_H
+
+#include "qwt/qwt_global.h"
+#include <qpolygon.h>
+#include <qrect.h>
+
+class QwtSpline;
+
+/*!
+  \brief Abstract base class for a curve fitter
+*/
+class QWT_EXPORT QwtCurveFitter
+{
+public:
+    virtual ~QwtCurveFitter();
+
+    /*!
+        Find a curve which has the best fit to a series of data points
+
+        \param polygon Series of data points
+        \return Curve points
+     */
+    virtual QPolygonF fitCurve( const QPolygonF &polygon ) const = 0;
+
+protected:
+    QwtCurveFitter();
+
+private:
+    QwtCurveFitter( const QwtCurveFitter & );
+    QwtCurveFitter &operator=( const QwtCurveFitter & );
+};
+
+/*!
+  \brief A curve fitter using cubic splines
+*/
+class QWT_EXPORT QwtSplineCurveFitter: public QwtCurveFitter
+{
+public:
+    /*!
+      Spline type
+      The default setting is Auto
+      \sa setFitMode(), FitMode()
+     */
+    enum FitMode
+    {
+        /*!
+          Use the default spline algorithm for polygons with
+          increasing x values ( p[i-1] < p[i] ), otherwise use
+          a parametric spline algorithm.
+         */
+        Auto,
+
+        //! Use a default spline algorithm
+        Spline,
+
+        //! Use a parametric spline algorithm
+        ParametricSpline
+    };
+
+    QwtSplineCurveFitter();
+    virtual ~QwtSplineCurveFitter();
+
+    void setFitMode( FitMode );
+    FitMode fitMode() const;
+
+    void setSpline( const QwtSpline& );
+    const QwtSpline &spline() const;
+    QwtSpline &spline();
+
+    void setSplineSize( int size );
+    int splineSize() const;
+
+    virtual QPolygonF fitCurve( const QPolygonF & ) const;
+
+private:
+    QPolygonF fitSpline( const QPolygonF & ) const;
+    QPolygonF fitParametric( const QPolygonF & ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+  \brief A curve fitter implementing Douglas and Peucker algorithm
+
+  The purpose of the Douglas and Peucker algorithm is that given a 'curve'
+  composed of line segments to find a curve not too dissimilar but that
+  has fewer points. The algorithm defines 'too dissimilar' based on the
+  maximum distance (tolerance) between the original curve and the
+  smoothed curve.
+
+  The runtime of the algorithm increases non linear ( worst case O( n*n ) )
+  and might be very slow for huge polygons. To avoid performance issues
+  it might be useful to split the polygon ( setChunkSize() ) and to run the algorithm
+  for these smaller parts. The disadvantage of having no interpolation
+  at the borders is for most use cases irrelevant.
+
+  The smoothed curve consists of a subset of the points that defined the
+  original curve.
+
+  In opposite to QwtSplineCurveFitter the Douglas and Peucker algorithm reduces
+  the number of points. By adjusting the tolerance parameter according to the
+  axis scales QwtSplineCurveFitter can be used to implement different
+  level of details to speed up painting of curves of many points.
+*/
+class QWT_EXPORT QwtWeedingCurveFitter: public QwtCurveFitter
+{
+public:
+    QwtWeedingCurveFitter( double tolerance = 1.0 );
+    virtual ~QwtWeedingCurveFitter();
+
+    void setTolerance( double );
+    double tolerance() const;
+
+    void setChunkSize( uint );
+    uint chunkSize() const;
+
+    virtual QPolygonF fitCurve( const QPolygonF & ) const;
+
+private:
+    virtual QPolygonF simplify( const QPolygonF & ) const;
+
+    class Line;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_date.h b/include/pli_vis/third_party/qwt/qwt_date.h
new file mode 100644
index 0000000000000000000000000000000000000000..515af0e75289e941151ea623210ed6f9bd294a49
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_date.h
@@ -0,0 +1,128 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef _QWT_DATE_H_
+#define _QWT_DATE_H_
+
+#include "qwt/qwt_global.h"
+#include <qdatetime.h>
+
+/*!
+  \brief A collection of methods around date/time values
+
+  Qt offers convenient classes for dealing with date/time values,
+  but Qwt uses coordinate systems that are based on doubles.
+  QwtDate offers methods to translate from QDateTime to double and v.v.
+
+  A double is interpreted as the number of milliseconds since
+  1970-01-01T00:00:00 Universal Coordinated Time - also known
+  as "The Epoch". 
+
+  While the range of the Julian day in Qt4 is limited to [0, MAX_INT], 
+  Qt5 stores it as qint64 offering a huge range of valid dates. 
+  As the significance of a double is below this ( assuming a 
+  fraction of 52 bits ) the translation is not 
+  bijective with rounding errors for dates very far from Epoch. 
+  For a resolution of 1 ms those start to happen for dates above the 
+  year 144683. 
+
+  An axis for a date/time interval is expected to be aligned
+  and divided in time/date units like seconds, minutes, ...
+  QwtDate offers several algorithms that are needed to
+  calculate these axes.
+
+  \sa QwtDateScaleEngine, QwtDateScaleDraw, QDate, QTime
+*/
+class QWT_EXPORT QwtDate
+{
+public:
+    /*! 
+       How to identify the first week of year differs between
+       countries. 
+     */
+    enum Week0Type
+    {
+        /*!
+           According to ISO 8601 the first week of a year is defined
+           as "the week with the year's first Thursday in it".
+
+           FirstThursday corresponds to the numbering that is
+           implemented in QDate::weekNumber().
+        */
+        FirstThursday,
+
+        /*!
+            "The week with January 1.1 in it."
+           
+            In the U.S. this definition is more common than
+            FirstThursday.
+        */
+        FirstDay
+    };
+
+    /*! 
+      Classification of an time interval
+
+      Time intervals needs to be classified to decide how to
+      align and divide it.
+     */
+    enum IntervalType
+    {
+        //! The interval is related to milliseconds
+        Millisecond,
+
+        //! The interval is related to seconds
+        Second,
+
+        //! The interval is related to minutes
+        Minute,
+
+        //! The interval is related to hours
+        Hour,
+
+        //! The interval is related to days
+        Day,
+
+        //! The interval is related to weeks
+        Week,
+
+        //! The interval is related to months
+        Month,
+
+        //! The interval is related to years
+        Year
+    };
+
+    enum
+    {
+        //! The Julian day of "The Epoch"
+        JulianDayForEpoch = 2440588
+    };
+
+    static QDate minDate();
+    static QDate maxDate();
+
+    static QDateTime toDateTime( double value, 
+        Qt::TimeSpec = Qt::UTC );
+
+    static double toDouble( const QDateTime & );
+
+    static QDateTime ceil( const QDateTime &, IntervalType );
+    static QDateTime floor( const QDateTime &, IntervalType );
+
+    static QDate dateOfWeek0( int year, Week0Type );
+    static int weekNumber( const QDate &, Week0Type );
+
+    static int utcOffset( const QDateTime & );
+
+    static QString toString( const QDateTime &, 
+        const QString & format, Week0Type );
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_date_scale_draw.h b/include/pli_vis/third_party/qwt/qwt_date_scale_draw.h
new file mode 100644
index 0000000000000000000000000000000000000000..85808b186c457ec1b8bd747bd942591cb7080aad
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_date_scale_draw.h
@@ -0,0 +1,86 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef _QWT_DATE_SCALE_DRAW_H_
+#define _QWT_DATE_SCALE_DRAW_H_ 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_scale_draw.h"
+#include "qwt/qwt_date.h"
+
+/*!
+  \brief A class for drawing datetime scales
+
+  QwtDateScaleDraw displays values as datetime labels.
+  The format of the labels depends on the alignment of
+  the major tick labels.
+
+  The default format strings are:
+
+  - Millisecond\n
+    "hh:mm:ss:zzz\nddd dd MMM yyyy"
+  - Second\n
+    "hh:mm:ss\nddd dd MMM yyyy"
+  - Minute\n
+    "hh:mm\nddd dd MMM yyyy"
+  - Hour\n
+    "hh:mm\nddd dd MMM yyyy"
+  - Day\n
+    "ddd dd MMM yyyy"
+  - Week\n
+    "Www yyyy"
+  - Month\n
+    "MMM yyyy"
+  - Year\n
+    "yyyy"
+
+  The format strings can be modified using setDateFormat()
+  or individually for each tick label by overloading dateFormatOfDate(),
+
+  Usually QwtDateScaleDraw is used in combination with
+  QwtDateScaleEngine, that calculates scales for datetime
+  intervals.
+
+  \sa QwtDateScaleEngine, QwtPlot::setAxisScaleDraw()
+*/
+class QWT_EXPORT QwtDateScaleDraw: public QwtScaleDraw
+{
+public:
+    QwtDateScaleDraw( Qt::TimeSpec = Qt::LocalTime );
+    virtual ~QwtDateScaleDraw();
+
+    void setDateFormat( QwtDate::IntervalType, const QString & );
+    QString dateFormat( QwtDate::IntervalType ) const;
+
+    void setTimeSpec( Qt::TimeSpec );
+    Qt::TimeSpec timeSpec() const;
+
+    void setUtcOffset( int seconds );
+    int utcOffset() const;
+
+    void setWeek0Type( QwtDate::Week0Type );
+    QwtDate::Week0Type week0Type() const;
+
+    virtual QwtText label( double ) const;
+
+    QDateTime toDateTime( double ) const;
+
+protected:
+    virtual QwtDate::IntervalType 
+        intervalType( const QwtScaleDiv & ) const;
+
+    virtual QString dateFormatOfDate( const QDateTime &,
+        QwtDate::IntervalType ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_date_scale_engine.h b/include/pli_vis/third_party/qwt/qwt_date_scale_engine.h
new file mode 100644
index 0000000000000000000000000000000000000000..28fd157856a4c48dbef3a412e4351857ee0f4991
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_date_scale_engine.h
@@ -0,0 +1,86 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef _QWT_DATE_SCALE_ENGINE_H_
+#define _QWT_DATE_SCALE_ENGINE_H_ 1
+
+#include "qwt/qwt_date.h"
+#include "qwt/qwt_scale_engine.h"
+
+/*!
+  \brief A scale engine for date/time values
+
+  QwtDateScaleEngine builds scales from a time intervals.
+  Together with QwtDateScaleDraw it can be used for
+  axes according to date/time values.
+
+  Years, months, weeks, days, hours and minutes are organized
+  in steps with non constant intervals. QwtDateScaleEngine
+  classifies intervals and aligns the boundaries and tick positions
+  according to this classification.
+
+  QwtDateScaleEngine supports representations depending
+  on Qt::TimeSpec specifications. The valid range for scales
+  is limited by the range of QDateTime, that differs 
+  between Qt4 and Qt5.
+  
+  Datetime values are expected as the number of milliseconds since
+  1970-01-01T00:00:00 Universal Coordinated Time - also known
+  as "The Epoch", that can be converted to QDateTime using 
+  QwtDate::toDateTime().
+
+  \sa QwtDate, QwtPlot::setAxisScaleEngine(),
+      QwtAbstractScale::setScaleEngine()
+*/
+class QWT_EXPORT QwtDateScaleEngine: public QwtLinearScaleEngine
+{
+public:
+    QwtDateScaleEngine( Qt::TimeSpec = Qt::LocalTime );
+    virtual ~QwtDateScaleEngine();
+
+    void setTimeSpec( Qt::TimeSpec );
+    Qt::TimeSpec timeSpec() const;
+
+    void setUtcOffset( int seconds );
+    int utcOffset() const;
+
+    void setWeek0Type( QwtDate::Week0Type );
+    QwtDate::Week0Type week0Type() const;
+    
+    void setMaxWeeks( int );
+    int maxWeeks() const;
+
+    virtual void autoScale( int maxNumSteps,
+        double &x1, double &x2, double &stepSize ) const;
+
+    virtual QwtScaleDiv divideScale( 
+        double x1, double x2,
+        int maxMajorSteps, int maxMinorSteps,
+        double stepSize = 0.0 ) const;
+
+    virtual QwtDate::IntervalType intervalType( 
+        const QDateTime &, const QDateTime &, int maxSteps ) const;
+
+    QDateTime toDateTime( double ) const;
+
+protected:
+    virtual QDateTime alignDate( const QDateTime &, double stepSize,
+        QwtDate::IntervalType, bool up ) const;
+
+private:
+    QwtScaleDiv buildScaleDiv( const QDateTime &, const QDateTime &,
+        int maxMajorSteps, int maxMinorSteps, 
+        QwtDate::IntervalType ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_dial.h b/include/pli_vis/third_party/qwt/qwt_dial.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5d2c5b1deabda4a4161cdd101731ae10fea4a4b
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_dial.h
@@ -0,0 +1,168 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_DIAL_H
+#define QWT_DIAL_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_abstract_slider.h"
+#include "qwt/qwt_abstract_scale_draw.h"
+#include <qframe.h>
+#include <qpalette.h>
+
+class QwtDialNeedle;
+class QwtRoundScaleDraw;
+
+/*!
+  \brief QwtDial class provides a rounded range control.
+
+  QwtDial is intended as base class for dial widgets like
+  speedometers, compass widgets, clocks ...
+
+  \image html dials2.png
+
+  A dial contains a scale and a needle indicating the current value
+  of the dial. Depending on Mode one of them is fixed and the
+  other is rotating. If not isReadOnly() the
+  dial can be rotated by dragging the mouse or using keyboard inputs
+  (see QwtAbstractSlider::keyPressEvent()). A dial might be wrapping, what means
+  a rotation below/above one limit continues on the other limit (f.e compass).
+  The scale might cover any arc of the dial, its values are related to
+  the origin() of the dial.
+
+  Often dials have to be updated very often according to values from external
+  devices. For these high refresh rates QwtDial caches as much as possible.
+  For derived classes it might be necessary to clear these caches manually
+  according to attribute changes using invalidateCache().
+
+  \sa QwtCompass, QwtAnalogClock, QwtDialNeedle
+  \note The controls and dials examples shows different types of dials.
+  \note QDial is more similar to QwtKnob than to QwtDial
+*/
+
+class QWT_EXPORT QwtDial: public QwtAbstractSlider
+{
+    Q_OBJECT
+
+    Q_ENUMS( Shadow Mode Direction )
+
+    Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth )
+    Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow )
+    Q_PROPERTY( Mode mode READ mode WRITE setMode )
+    Q_PROPERTY( double origin READ origin WRITE setOrigin )
+    Q_PROPERTY( double minScaleArc READ minScaleArc WRITE setMinScaleArc )
+    Q_PROPERTY( double maxScaleArc READ maxScaleArc WRITE setMaxScaleArc )
+
+public:
+
+    /*!
+        \brief Frame shadow
+
+         Unfortunately it is not possible to use QFrame::Shadow
+         as a property of a widget that is not derived from QFrame.
+         The following enum is made for the designer only. It is safe
+         to use QFrame::Shadow instead.
+     */
+    enum Shadow
+    {
+        //! QFrame::Plain
+        Plain = QFrame::Plain,
+
+        //! QFrame::Raised
+        Raised = QFrame::Raised,
+
+        //! QFrame::Sunken
+        Sunken = QFrame::Sunken
+    };
+
+    //! Mode controlling whether the needle or the scale is rotating
+    enum Mode
+    {
+        //! The needle is rotating
+        RotateNeedle,
+
+        //! The needle is fixed, the scales are rotating
+        RotateScale
+    };
+
+    explicit QwtDial( QWidget *parent = NULL );
+    virtual ~QwtDial();
+
+    void setFrameShadow( Shadow );
+    Shadow frameShadow() const;
+
+    void setLineWidth( int );
+    int lineWidth() const;
+
+    void setMode( Mode );
+    Mode mode() const;
+
+    void setScaleArc( double min, double max );
+
+    void setMinScaleArc( double min );
+    double minScaleArc() const;
+
+    void setMaxScaleArc( double min );
+    double maxScaleArc() const;
+
+    virtual void setOrigin( double );
+    double origin() const;
+
+    void setNeedle( QwtDialNeedle * );
+    const QwtDialNeedle *needle() const;
+    QwtDialNeedle *needle();
+
+    QRect boundingRect() const;
+    QRect innerRect() const;
+
+    virtual QRect scaleInnerRect() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    void setScaleDraw( QwtRoundScaleDraw * );
+
+    QwtRoundScaleDraw *scaleDraw();
+    const QwtRoundScaleDraw *scaleDraw() const;
+
+protected:
+    virtual void wheelEvent( QWheelEvent * );
+    virtual void paintEvent( QPaintEvent * );
+    virtual void changeEvent( QEvent * );
+
+    virtual void drawFrame( QPainter *p );
+    virtual void drawContents( QPainter * ) const;
+    virtual void drawFocusIndicator( QPainter * ) const;
+
+    void invalidateCache();
+
+    virtual void drawScale( QPainter *, 
+        const QPointF &center, double radius ) const;
+
+    virtual void drawScaleContents( QPainter *painter, 
+        const QPointF &center, double radius ) const;
+
+    virtual void drawNeedle( QPainter *, const QPointF &,
+        double radius, double direction, QPalette::ColorGroup ) const;
+
+    virtual double scrolledTo( const QPoint & ) const;
+    virtual bool isScrollPosition( const QPoint & ) const;
+
+    virtual void sliderChange();
+    virtual void scaleChange();
+
+private:
+    void setAngleRange( double angle, double span );
+    void drawNeedle( QPainter * ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_dial_needle.h b/include/pli_vis/third_party/qwt/qwt_dial_needle.h
new file mode 100644
index 0000000000000000000000000000000000000000..ff1be44338973b9cdaa67d9af61da13ecd734d7b
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_dial_needle.h
@@ -0,0 +1,187 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_DIAL_NEEDLE_H
+#define QWT_DIAL_NEEDLE_H 1
+
+#include "qwt/qwt_global.h"
+#include <qpalette.h>
+
+class QPainter;
+class QPoint;
+
+/*!
+  \brief Base class for needles that can be used in a QwtDial.
+
+  QwtDialNeedle is a pointer that indicates a value by pointing
+  to a specific direction.
+
+  \sa QwtDial, QwtCompass
+*/
+
+class QWT_EXPORT QwtDialNeedle
+{
+public:
+    QwtDialNeedle();
+    virtual ~QwtDialNeedle();
+
+    virtual void setPalette( const QPalette & );
+    const QPalette &palette() const;
+
+    virtual void draw( QPainter *painter, const QPointF &center,
+        double length, double direction, 
+        QPalette::ColorGroup = QPalette::Active ) const;
+
+protected:
+    /*!
+      \brief Draw the needle
+
+      The origin of the needle is at position (0.0, 0.0 )
+      pointing in direction 0.0 ( = east ). 
+
+      The painter is already initialized with translation and 
+      rotation.
+
+      \param painter Painter
+      \param length Length of the needle
+      \param colorGroup Color group, used for painting
+
+      \sa setPalette(), palette()
+    */
+    virtual void drawNeedle( QPainter *painter, 
+        double length, QPalette::ColorGroup colorGroup ) const = 0;
+
+    virtual void drawKnob( QPainter *, double width, 
+        const QBrush &, bool sunken ) const;
+
+private:
+    QPalette d_palette;
+};
+
+/*!
+  \brief A needle for dial widgets
+
+  The following colors are used:
+
+  - QPalette::Mid\n
+    Pointer
+  - QPalette::Base\n
+    Knob
+
+  \sa QwtDial, QwtCompass
+*/
+
+class QWT_EXPORT QwtDialSimpleNeedle: public QwtDialNeedle
+{
+public:
+    //! Style of the needle
+    enum Style
+    {
+        //! Arrow
+        Arrow,
+
+        //! A straight line from the center
+        Ray
+    };
+
+    QwtDialSimpleNeedle( Style, bool hasKnob = true,
+        const QColor &mid = Qt::gray, const QColor &base = Qt::darkGray );
+
+    void setWidth( double width );
+    double width() const;
+
+protected:
+    virtual void drawNeedle( QPainter *, double length,
+        QPalette::ColorGroup ) const;
+
+private:
+    Style d_style;
+    bool d_hasKnob;
+    double d_width;
+};
+
+/*!
+  \brief A magnet needle for compass widgets
+
+  A magnet needle points to two opposite directions indicating
+  north and south.
+
+  The following colors are used:
+  - QPalette::Light\n
+    Used for pointing south
+  - QPalette::Dark\n
+    Used for pointing north
+  - QPalette::Base\n
+    Knob (ThinStyle only)
+
+  \sa QwtDial, QwtCompass
+*/
+
+class QWT_EXPORT QwtCompassMagnetNeedle: public QwtDialNeedle
+{
+public:
+    //! Style of the needle
+    enum Style
+    {
+        //! A needle with a triangular shape
+        TriangleStyle,
+
+        //! A thin needle 
+        ThinStyle
+    };
+
+    QwtCompassMagnetNeedle( Style = TriangleStyle,
+        const QColor &light = Qt::white, const QColor &dark = Qt::red );
+
+protected:
+    virtual void drawNeedle( QPainter *, 
+        double length, QPalette::ColorGroup ) const;
+
+private:
+    Style d_style;
+};
+
+/*!
+  \brief An indicator for the wind direction
+
+  QwtCompassWindArrow shows the direction where the wind comes from.
+
+  - QPalette::Light\n
+    Used for Style1, or the light half of Style2
+  - QPalette::Dark\n
+    Used for the dark half of Style2
+
+  \sa QwtDial, QwtCompass
+*/
+
+class QWT_EXPORT QwtCompassWindArrow: public QwtDialNeedle
+{
+public:
+    //! Style of the arrow
+    enum Style
+    {
+        //! A needle pointing to the center
+        Style1,
+
+        //! A needle pointing to the center
+        Style2
+    };
+
+    QwtCompassWindArrow( Style, const QColor &light = Qt::white,
+        const QColor &dark = Qt::gray );
+
+protected:
+    virtual void drawNeedle( QPainter *, 
+        double length, QPalette::ColorGroup ) const;
+
+private:
+    Style d_style;
+};
+
+#endif 
diff --git a/include/pli_vis/third_party/qwt/qwt_dyngrid_layout.h b/include/pli_vis/third_party/qwt/qwt_dyngrid_layout.h
new file mode 100644
index 0000000000000000000000000000000000000000..c58cff25547ae389ecebf7008990183df3398cb8
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_dyngrid_layout.h
@@ -0,0 +1,83 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_DYNGRID_LAYOUT_H
+#define QWT_DYNGRID_LAYOUT_H
+
+#include "qwt/qwt_global.h"
+#include <qlayout.h>
+#include <qsize.h>
+#include <qlist.h>
+
+/*!
+  \brief The QwtDynGridLayout class lays out widgets in a grid,
+         adjusting the number of columns and rows to the current size.
+
+  QwtDynGridLayout takes the space it gets, divides it up into rows and
+  columns, and puts each of the widgets it manages into the correct cell(s).
+  It lays out as many number of columns as possible (limited by maxColumns()).
+*/
+
+class QWT_EXPORT QwtDynGridLayout : public QLayout
+{
+    Q_OBJECT
+public:
+    explicit QwtDynGridLayout( QWidget *, int margin = 0, int space = -1 );
+    explicit QwtDynGridLayout( int space = -1 );
+
+    virtual ~QwtDynGridLayout();
+
+    virtual void invalidate();
+
+    void setMaxColumns( uint maxCols );
+    uint maxColumns() const;
+
+    uint numRows () const;
+    uint numColumns () const;
+
+    virtual void addItem( QLayoutItem * );
+
+    virtual QLayoutItem *itemAt( int index ) const;
+    virtual QLayoutItem *takeAt( int index );
+    virtual int count() const;
+
+    void setExpandingDirections( Qt::Orientations );
+    virtual Qt::Orientations expandingDirections() const;
+    QList<QRect> layoutItems( const QRect &, uint numCols ) const;
+
+    virtual int maxItemWidth() const;
+
+    virtual void setGeometry( const QRect &rect );
+
+    virtual bool hasHeightForWidth() const;
+    virtual int heightForWidth( int ) const;
+
+    virtual QSize sizeHint() const;
+
+    virtual bool isEmpty() const;
+    uint itemCount() const;
+
+    virtual uint columnsForWidth( int width ) const;
+
+protected:
+
+    void layoutGrid( uint numCols,
+        QVector<int>& rowHeight, QVector<int>& colWidth ) const;
+    void stretchGrid( const QRect &rect, uint numCols,
+        QVector<int>& rowHeight, QVector<int>& colWidth ) const;
+
+private:
+    void init();
+    int maxRowWidth( int numCols ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_event_pattern.h b/include/pli_vis/third_party/qwt/qwt_event_pattern.h
new file mode 100644
index 0000000000000000000000000000000000000000..ab55fc5170161d205d697ae69063f015ea052221
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_event_pattern.h
@@ -0,0 +1,240 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_EVENT_PATTERN
+#define QWT_EVENT_PATTERN 1
+
+#include "qwt/qwt_global.h"
+#include <qnamespace.h>
+#include <qvector.h>
+
+class QMouseEvent;
+class QKeyEvent;
+
+/*!
+  \brief A collection of event patterns
+
+  QwtEventPattern introduces an level of indirection for mouse and
+  keyboard inputs. Those are represented by symbolic names, so
+  the application code can be configured by individual mappings.
+
+  \sa QwtPicker, QwtPickerMachine, QwtPlotZoomer
+*/
+class QWT_EXPORT QwtEventPattern
+{
+public:
+    /*!
+      \brief Symbolic mouse input codes
+
+      QwtEventPattern implements 3 different settings for
+      mice with 1, 2, or 3 buttons that can be activated
+      using initMousePattern(). The default setting is for
+      3 button mice.
+
+      Individual settings can be configured using setMousePattern().
+
+      \sa initMousePattern(), setMousePattern(), setKeyPattern()
+    */
+    enum MousePatternCode
+    {
+        /*! 
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton 
+          - Qt::LeftButton 
+          - Qt::LeftButton 
+         */
+        MouseSelect1,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::ControlModifier
+          - Qt::RightButton
+          - Qt::RightButton
+         */
+        MouseSelect2,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::AltModifier
+          - Qt::LeftButton + Qt::AltModifier
+          - Qt::MidButton
+         */
+        MouseSelect3,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::ShiftModifier
+          - Qt::LeftButton + Qt::ShiftModifier
+          - Qt::LeftButton + Qt::ShiftModifier
+         */
+        MouseSelect4,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::ControlButton | Qt::ShiftModifier
+          - Qt::RightButton + Qt::ShiftModifier
+          - Qt::RightButton + Qt::ShiftModifier
+         */
+        MouseSelect5,
+
+        /*!
+          The default setting for 1, 2 and 3 button mice is:
+
+          - Qt::LeftButton + Qt::AltModifier + Qt::ShiftModifier
+          - Qt::LeftButton + Qt::AltModifier | Qt::ShiftModifier
+          - Qt::MidButton + Qt::ShiftModifier
+         */
+        MouseSelect6,
+
+        //! Number of mouse patterns
+        MousePatternCount
+    };
+
+    /*!
+      \brief Symbolic keyboard input codes
+
+      Individual settings can be configured using setKeyPattern()
+
+      \sa setKeyPattern(), setMousePattern()
+    */
+    enum KeyPatternCode
+    {
+        //! Qt::Key_Return
+        KeySelect1,
+
+        //! Qt::Key_Space
+        KeySelect2,
+
+        //! Qt::Key_Escape
+        KeyAbort,
+
+        //! Qt::Key_Left
+        KeyLeft,
+
+        //! Qt::Key_Right
+        KeyRight,
+
+        //! Qt::Key_Up
+        KeyUp,
+
+        //! Qt::Key_Down
+        KeyDown,
+
+        //! Qt::Key_Plus
+        KeyRedo,
+
+        //! Qt::Key_Minus
+        KeyUndo,
+
+        //! Qt::Key_Escape
+        KeyHome,
+
+        //! Number of key patterns
+        KeyPatternCount
+    };
+
+    //! A pattern for mouse events
+    class MousePattern
+    {
+    public:
+        //! Constructor
+        MousePattern( Qt::MouseButton btn = Qt::NoButton, 
+                Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ):
+            button( btn ),
+            modifiers( modifierCodes )
+        {
+        }
+
+        //! Button 
+        Qt::MouseButton button;
+
+        //! Keyboard modifier
+        Qt::KeyboardModifiers modifiers;
+    };
+
+    //! A pattern for key events
+    class KeyPattern
+    {
+    public:
+        //! Constructor
+        KeyPattern( int keyCode = Qt::Key_unknown, 
+                Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ):
+            key( keyCode ),
+            modifiers( modifierCodes )
+        {
+        }
+
+        //! Key code
+        int key;
+
+        //! Modifiers
+        Qt::KeyboardModifiers modifiers;
+    };
+
+    QwtEventPattern();
+    virtual ~QwtEventPattern();
+
+    void initMousePattern( int numButtons );
+    void initKeyPattern();
+
+    void setMousePattern( MousePatternCode, Qt::MouseButton button, 
+        Qt::KeyboardModifiers = Qt::NoModifier );
+
+    void setKeyPattern( KeyPatternCode, int keyCode, 
+        Qt::KeyboardModifiers modifierCodes = Qt::NoModifier );
+
+    void setMousePattern( const QVector<MousePattern> & );
+    void setKeyPattern( const QVector<KeyPattern> & );
+
+    const QVector<MousePattern> &mousePattern() const;
+    const QVector<KeyPattern> &keyPattern() const;
+
+    QVector<MousePattern> &mousePattern();
+    QVector<KeyPattern> &keyPattern();
+
+    bool mouseMatch( MousePatternCode, const QMouseEvent * ) const;
+    bool keyMatch( KeyPatternCode, const QKeyEvent * ) const;
+
+protected:
+    virtual bool mouseMatch( const MousePattern &, const QMouseEvent * ) const;
+    virtual bool keyMatch( const KeyPattern &, const QKeyEvent * ) const;
+
+private:
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable: 4251)
+#endif
+    QVector<MousePattern> d_mousePattern;
+    QVector<KeyPattern> d_keyPattern;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+};
+
+//! Compare operator
+inline bool operator==( QwtEventPattern::MousePattern b1,
+    QwtEventPattern::MousePattern  b2 )
+{
+    return b1.button == b2.button && b1.modifiers == b2.modifiers;
+}
+
+//! Compare operator
+inline bool operator==( QwtEventPattern::KeyPattern b1,
+   QwtEventPattern::KeyPattern  b2 )
+{
+    return b1.key == b2.key && b1.modifiers == b2.modifiers;
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_global.h b/include/pli_vis/third_party/qwt/qwt_global.h
new file mode 100644
index 0000000000000000000000000000000000000000..736ccf192341e39205646291a88efeec37a62467
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_global.h
@@ -0,0 +1,41 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_GLOBAL_H
+#define QWT_GLOBAL_H
+
+#include <qglobal.h>
+
+// QWT_VERSION is (major << 16) + (minor << 8) + patch.
+
+#define QWT_VERSION       0x060103
+#define QWT_VERSION_STR   "6.1.3"
+
+#if defined(_MSC_VER) /* MSVC Compiler */
+/* template-class specialization 'identifier' is already instantiated */
+#pragma warning(disable: 4660)
+/* inherits via dominance */
+#pragma warning(disable: 4250)
+#endif // _MSC_VER
+
+#ifdef QWT_DLL
+
+#if defined(QWT_MAKEDLL)     // create a Qwt DLL library 
+#define QWT_EXPORT Q_DECL_EXPORT
+#else                        // use a Qwt DLL library
+#define QWT_EXPORT Q_DECL_IMPORT 
+#endif
+
+#endif // QWT_DLL
+
+#ifndef QWT_EXPORT
+#define QWT_EXPORT
+#endif
+
+#endif 
diff --git a/include/pli_vis/third_party/qwt/qwt_graphic.h b/include/pli_vis/third_party/qwt/qwt_graphic.h
new file mode 100644
index 0000000000000000000000000000000000000000..b6da4bdc9d0b83aa1af3ec006be1ac45eb2e57ad
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_graphic.h
@@ -0,0 +1,176 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_GRAPHIC_H
+#define QWT_GRAPHIC_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_null_paintdevice.h"
+#include <qmetatype.h>
+#include <qimage.h>
+#include <qpixmap.h>
+
+class QwtPainterCommand;
+
+/*!
+    \brief A paint device for scalable graphics
+
+    QwtGraphic is the representation of a graphic that is tailored for
+    scalability. Like QPicture it will be initialized by QPainter
+    operations and can be replayed later to any target paint device.
+
+    While the usual image representations QImage and QPixmap are not
+    scalable Qt offers two paint devices, that might be candidates
+    for representing a vector graphic:
+
+    - QPicture\n
+      Unfortunately QPicture had been forgotten, when Qt4
+      introduced floating point based render engines. Its API
+      is still on integers, what make it unusable for proper scaling.
+
+    - QSvgRenderer/QSvgGenerator\n
+      Unfortunately QSvgRenderer hides to much information about
+      its nodes in internal APIs, that are necessary for proper 
+      layout calculations. Also it is derived from QObject and 
+      can't be copied like QImage/QPixmap.
+
+    QwtGraphic maps all scalable drawing primitives to a QPainterPath
+    and stores them together with the painter state changes 
+    ( pen, brush, transformation ... ) in a list of QwtPaintCommands. 
+    For being a complete QPaintDevice it also stores pixmaps or images, 
+    what is somehow against the idea of the class, because these objects 
+    can't be scaled without a loss in quality.
+
+    The main issue about scaling a QwtGraphic object are the pens used for
+    drawing the outlines of the painter paths. While non cosmetic pens 
+    ( QPen::isCosmetic() ) are scaled with the same ratio as the path, 
+    cosmetic pens have a fixed width. A graphic might have paths with 
+    different pens - cosmetic and non-cosmetic.
+
+    QwtGraphic caches 2 different rectangles:
+
+    - control point rectangle\n
+      The control point rectangle is the bounding rectangle of all
+      control point rectangles of the painter paths, or the target 
+      rectangle of the pixmaps/images.
+
+    - bounding rectangle\n
+      The bounding rectangle extends the control point rectangle by
+      what is needed for rendering the outline with an unscaled pen.
+
+    Because the offset for drawing the outline depends on the shape 
+    of the painter path ( the peak of a triangle is different than the flat side ) 
+    scaling with a fixed aspect ratio always needs to be calculated from the 
+    control point rectangle.
+
+    \sa QwtPainterCommand
+ */
+class QWT_EXPORT QwtGraphic: public QwtNullPaintDevice
+{
+public:
+    /*! 
+        Hint how to render a graphic
+        \sa setRenderHint(), testRenderHint()
+     */
+    enum RenderHint
+    {
+        /*!
+           When rendering a QwtGraphic a specific scaling between 
+           the controlPointRect() and the coordinates of the target rectangle
+           is set up internally in render().
+
+           When RenderPensUnscaled is set this specific scaling is applied
+           for the control points only, but not for the pens.
+           All other painter transformations ( set up by application code )
+           are supposed to work like usual.
+
+           \sa render();
+         */
+        RenderPensUnscaled = 0x1
+    };
+
+    /*! 
+        \brief Render hints
+
+        The default setting is to disable all hints
+     */
+    typedef QFlags<RenderHint> RenderHints;
+
+    QwtGraphic();
+    QwtGraphic( const QwtGraphic & );
+
+    virtual ~QwtGraphic();
+
+    QwtGraphic& operator=( const QwtGraphic & );
+
+    void reset();
+
+    bool isNull() const;
+    bool isEmpty() const;
+
+    void render( QPainter * ) const;
+
+    void render( QPainter *, const QSizeF &, 
+            Qt::AspectRatioMode = Qt::IgnoreAspectRatio  ) const;
+
+    void render( QPainter *, const QRectF &, 
+            Qt::AspectRatioMode = Qt::IgnoreAspectRatio  ) const;
+
+    void render( QPainter *, const QPointF &,
+        Qt::Alignment = Qt::AlignTop | Qt::AlignLeft ) const;
+
+    QPixmap toPixmap() const; 
+    QPixmap toPixmap( const QSize &, 
+        Qt::AspectRatioMode = Qt::IgnoreAspectRatio  ) const;
+
+    QImage toImage() const; 
+    QImage toImage( const QSize &, 
+        Qt::AspectRatioMode = Qt::IgnoreAspectRatio  ) const;
+
+    QRectF scaledBoundingRect( double sx, double sy ) const;
+
+    QRectF boundingRect() const;
+    QRectF controlPointRect() const;
+
+    const QVector< QwtPainterCommand > &commands() const;
+    void setCommands( QVector< QwtPainterCommand > & );
+
+    void setDefaultSize( const QSizeF & );
+    QSizeF defaultSize() const;
+    
+    void setRenderHint( RenderHint, bool on = true );
+    bool testRenderHint( RenderHint ) const;
+
+protected:
+    virtual QSize sizeMetrics() const;
+
+    virtual void drawPath( const QPainterPath & );
+
+    virtual void drawPixmap( const QRectF &,
+        const QPixmap &, const QRectF & );
+
+    virtual void drawImage( const QRectF &,
+        const QImage &, const QRectF &, Qt::ImageConversionFlags );
+
+    virtual void updateState( const QPaintEngineState &state );
+
+private:
+    void updateBoundingRect( const QRectF & );
+    void updateControlPointRect( const QRectF & );
+
+    class PathInfo;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtGraphic::RenderHints )
+Q_DECLARE_METATYPE( QwtGraphic )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_interval.h b/include/pli_vis/third_party/qwt/qwt_interval.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e8e39d43cea4ea8c306261c6815d6db1a0fd70b
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_interval.h
@@ -0,0 +1,320 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_INTERVAL_H
+#define QWT_INTERVAL_H
+
+#include "qwt/qwt_global.h"
+#include <qmetatype.h>
+
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+/*!
+  \brief A class representing an interval
+
+  The interval is represented by 2 doubles, the lower and the upper limit.
+*/
+
+class QWT_EXPORT QwtInterval
+{
+public:
+    /*!
+      Flag indicating if a border is included or excluded 
+      \sa setBorderFlags(), borderFlags()
+    */
+    enum BorderFlag
+    {
+        //! Min/Max values are inside the interval
+        IncludeBorders = 0x00,
+
+        //! Min value is not included in the interval
+        ExcludeMinimum = 0x01,
+
+        //! Max value is not included in the interval
+        ExcludeMaximum = 0x02,
+
+        //! Min/Max values are not included in the interval
+        ExcludeBorders = ExcludeMinimum | ExcludeMaximum
+    };
+
+    //! Border flags
+    typedef QFlags<BorderFlag> BorderFlags;
+
+    QwtInterval();
+    QwtInterval( double minValue, double maxValue,
+        BorderFlags = IncludeBorders );
+
+    void setInterval( double minValue, double maxValue,
+        BorderFlags = IncludeBorders );
+
+    QwtInterval normalized() const;
+    QwtInterval inverted() const;
+    QwtInterval limited( double minValue, double maxValue ) const;
+
+    bool operator==( const QwtInterval & ) const;
+    bool operator!=( const QwtInterval & ) const;
+
+    void setBorderFlags( BorderFlags );
+    BorderFlags borderFlags() const;
+
+    double minValue() const;
+    double maxValue() const;
+
+    double width() const;
+
+    void setMinValue( double );
+    void setMaxValue( double );
+
+    bool contains( double value ) const;
+
+    bool intersects( const QwtInterval & ) const;
+    QwtInterval intersect( const QwtInterval & ) const;
+    QwtInterval unite( const QwtInterval & ) const;
+
+    QwtInterval operator|( const QwtInterval & ) const;
+    QwtInterval operator&( const QwtInterval & ) const;
+
+    QwtInterval &operator|=( const QwtInterval & );
+    QwtInterval &operator&=( const QwtInterval & );
+
+    QwtInterval extend( double value ) const;
+    QwtInterval operator|( double ) const;
+    QwtInterval &operator|=( double );
+
+    bool isValid() const;
+    bool isNull() const;
+    void invalidate();
+
+    QwtInterval symmetrize( double value ) const;
+
+private:
+    double d_minValue;
+    double d_maxValue;
+    BorderFlags d_borderFlags;
+};
+
+Q_DECLARE_TYPEINFO(QwtInterval, Q_MOVABLE_TYPE);
+
+/*!
+  \brief Default Constructor
+
+  Creates an invalid interval [0.0, -1.0]
+  \sa setInterval(), isValid()
+*/
+inline QwtInterval::QwtInterval():
+    d_minValue( 0.0 ),
+    d_maxValue( -1.0 ),
+    d_borderFlags( IncludeBorders )
+{
+}
+
+/*!
+   Constructor
+
+   Build an interval with from min/max values
+
+   \param minValue Minimum value
+   \param maxValue Maximum value
+   \param borderFlags Include/Exclude borders
+*/
+inline QwtInterval::QwtInterval(
+        double minValue, double maxValue, BorderFlags borderFlags ):
+    d_minValue( minValue ),
+    d_maxValue( maxValue ),
+    d_borderFlags( borderFlags )
+{
+}
+
+/*!
+   Assign the limits of the interval
+
+   \param minValue Minimum value
+   \param maxValue Maximum value
+   \param borderFlags Include/Exclude borders
+*/
+inline void QwtInterval::setInterval(
+    double minValue, double maxValue, BorderFlags borderFlags )
+{
+    d_minValue = minValue;
+    d_maxValue = maxValue;
+    d_borderFlags = borderFlags;
+}
+
+/*!
+   Change the border flags
+
+   \param borderFlags Or'd BorderMode flags
+   \sa borderFlags()
+*/
+inline void QwtInterval::setBorderFlags( BorderFlags borderFlags )
+{
+    d_borderFlags = borderFlags;
+}
+
+/*!
+   \return Border flags
+   \sa setBorderFlags()
+*/
+inline QwtInterval::BorderFlags QwtInterval::borderFlags() const
+{
+    return d_borderFlags;
+}
+
+/*!
+   Assign the lower limit of the interval
+
+   \param minValue Minimum value
+*/
+inline void QwtInterval::setMinValue( double minValue )
+{
+    d_minValue = minValue;
+}
+
+/*!
+   Assign the upper limit of the interval
+
+   \param maxValue Maximum value
+*/
+inline void QwtInterval::setMaxValue( double maxValue )
+{
+    d_maxValue = maxValue;
+}
+
+//! \return Lower limit of the interval
+inline double QwtInterval::minValue() const
+{
+    return d_minValue;
+}
+
+//! \return Upper limit of the interval
+inline double QwtInterval::maxValue() const
+{
+    return d_maxValue;
+}
+
+/*!
+   A interval is valid when minValue() <= maxValue().
+   In case of QwtInterval::ExcludeBorders it is true
+   when minValue() < maxValue()
+
+   \return True, when the interval is valid
+*/
+inline bool QwtInterval::isValid() const
+{
+    if ( ( d_borderFlags & ExcludeBorders ) == 0 )
+        return d_minValue <= d_maxValue;
+    else
+        return d_minValue < d_maxValue;
+}
+
+/*!
+   \brief Return the width of an interval
+
+   The width of invalid intervals is 0.0, otherwise the result is
+   maxValue() - minValue().
+
+   \return Interval width
+   \sa isValid()
+*/
+inline double QwtInterval::width() const
+{
+    return isValid() ? ( d_maxValue - d_minValue ) : 0.0;
+}
+
+/*!
+   \brief Intersection of two intervals
+ 
+   \param other Interval to intersect with
+   \return Intersection of this and other
+
+   \sa intersect()
+*/
+inline QwtInterval QwtInterval::operator&(
+    const QwtInterval &other ) const
+{
+    return intersect( other );
+}
+
+/*!
+   Union of two intervals
+
+   \param other Interval to unite with
+   \return Union of this and other
+
+   \sa unite()
+*/
+inline QwtInterval QwtInterval::operator|(
+    const QwtInterval &other ) const
+{
+    return unite( other );
+}
+
+/*! 
+   \brief Compare two intervals
+
+   \param other Interval to compare with
+   \return True, when this and other are equal
+*/
+inline bool QwtInterval::operator==( const QwtInterval &other ) const
+{
+    return ( d_minValue == other.d_minValue ) &&
+           ( d_maxValue == other.d_maxValue ) &&
+           ( d_borderFlags == other.d_borderFlags );
+}
+/*! 
+   \brief Compare two intervals
+
+   \param other Interval to compare with
+   \return True, when this and other are not equal
+*/
+inline bool QwtInterval::operator!=( const QwtInterval &other ) const
+{
+    return ( !( *this == other ) );
+}
+
+/*!
+   Extend an interval
+
+   \param value Value
+   \return Extended interval
+   \sa extend()
+*/
+inline QwtInterval QwtInterval::operator|( double value ) const
+{
+    return extend( value );
+}
+
+//! \return true, if isValid() && (minValue() >= maxValue())
+inline bool QwtInterval::isNull() const
+{
+    return isValid() && d_minValue >= d_maxValue;
+}
+
+/*!
+  Invalidate the interval
+
+  The limits are set to interval [0.0, -1.0]
+  \sa isValid()
+*/
+inline void QwtInterval::invalidate()
+{
+    d_minValue = 0.0;
+    d_maxValue = -1.0;
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtInterval::BorderFlags )
+Q_DECLARE_METATYPE( QwtInterval )
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtInterval & );
+#endif
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_interval_symbol.h b/include/pli_vis/third_party/qwt/qwt_interval_symbol.h
new file mode 100644
index 0000000000000000000000000000000000000000..d91aa55cfc7aadb7621acbf2d9109284144850f1
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_interval_symbol.h
@@ -0,0 +1,87 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_INTERVAL_SYMBOL_H
+#define QWT_INTERVAL_SYMBOL_H
+
+#include "qwt/qwt_global.h"
+#include <qpen.h>
+#include <qsize.h>
+
+class QPainter;
+class QRect;
+class QPointF;
+
+/*!
+  \brief A drawing primitive for displaying an interval like an error bar
+
+  \sa QwtPlotIntervalCurve
+*/
+class QWT_EXPORT QwtIntervalSymbol
+{
+public:
+    //! Symbol style
+    enum Style
+    {
+        //! No Style. The symbol cannot be drawn.
+        NoSymbol = -1,
+
+        /*!
+          The symbol displays a line with caps at the beginning/end.
+          The size of the caps depends on the symbol width().
+         */
+        Bar,
+
+        /*!
+          The symbol displays a plain rectangle using pen() and brush().
+          The size of the rectangle depends on the translated interval and
+          the width(),
+         */
+        Box,
+
+        /*!
+          Styles >= UserSymbol are reserved for derived
+          classes of QwtIntervalSymbol that overload draw() with
+          additional application specific symbol types.
+         */
+        UserSymbol = 1000
+    };
+
+public:
+    QwtIntervalSymbol( Style = NoSymbol );
+    QwtIntervalSymbol( const QwtIntervalSymbol & );
+    virtual ~QwtIntervalSymbol();
+
+    QwtIntervalSymbol &operator=( const QwtIntervalSymbol & );
+    bool operator==( const QwtIntervalSymbol & ) const;
+    bool operator!=( const QwtIntervalSymbol & ) const;
+
+    void setWidth( int );
+    int width() const;
+
+    void setBrush( const QBrush& b );
+    const QBrush& brush() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen& pen() const;
+
+    void setStyle( Style );
+    Style style() const;
+
+    virtual void draw( QPainter *, Qt::Orientation,
+        const QPointF& from, const QPointF& to ) const;
+
+private:
+
+    class PrivateData;
+    PrivateData* d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_knob.h b/include/pli_vis/third_party/qwt/qwt_knob.h
new file mode 100644
index 0000000000000000000000000000000000000000..575428d7786ae431d3b5fbe019e12eaa4d7cedd9
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_knob.h
@@ -0,0 +1,178 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_KNOB_H
+#define QWT_KNOB_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_abstract_slider.h"
+
+class QwtRoundScaleDraw;
+
+/*!
+  \brief The Knob Widget
+
+  The QwtKnob widget imitates look and behavior of a volume knob on a radio.
+  It looks similar to QDial - not to QwtDial.
+
+  The value range of a knob might be divided into several turns.
+
+  The layout of the knob depends on the knobWidth().
+
+  - width > 0 
+    The diameter of the knob is fixed and the knob is aligned
+    according to the alignment() flags inside of the contentsRect(). 
+
+  - width <= 0
+    The knob is extended to the minimum of width/height of the contentsRect()
+    and aligned in the other direction according to alignment().
+
+  Setting a fixed knobWidth() is helpful to align several knobs with different
+  scale labels.
+  
+  \image html knob.png
+*/
+
+class QWT_EXPORT QwtKnob: public QwtAbstractSlider
+{
+    Q_OBJECT
+
+    Q_ENUMS ( KnobStyle MarkerStyle )
+
+    Q_PROPERTY( KnobStyle knobStyle READ knobStyle WRITE setKnobStyle )
+    Q_PROPERTY( int knobWidth READ knobWidth WRITE setKnobWidth )
+    Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment )
+    Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle )
+    Q_PROPERTY( int numTurns READ numTurns WRITE setNumTurns )
+    Q_PROPERTY( MarkerStyle markerStyle READ markerStyle WRITE setMarkerStyle )
+    Q_PROPERTY( int markerSize READ markerSize WRITE setMarkerSize )
+    Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth )
+
+public:
+    /*! 
+       \brief Style of the knob surface
+
+       Depending on the KnobStyle the surface of the knob is
+       filled from the brushes of the widget palette().
+
+       \sa setKnobStyle(), knobStyle()
+     */
+    enum KnobStyle
+    {
+        //! Fill the knob with a brush from QPalette::Button.
+        Flat,
+
+        //! Build a gradient from QPalette::Midlight and QPalette::Button
+        Raised,
+
+        /*! 
+          Build a gradient from QPalette::Midlight, QPalette::Button
+          and QPalette::Midlight
+         */
+        Sunken,
+
+        /*! 
+          Build a radial gradient from QPalette::Button
+          like it is used for QDial in various Qt styles.
+         */
+        Styled
+    };
+
+    /*!
+        \brief Marker type
+ 
+        The marker indicates the current value on the knob
+        The default setting is a Notch marker.
+
+        \sa setMarkerStyle(), setMarkerSize()
+    */
+    enum MarkerStyle 
+    { 
+        //! Don't paint any marker
+        NoMarker = -1,
+
+        //! Paint a single tick in QPalette::ButtonText color
+        Tick, 
+
+        //! Paint a triangle in QPalette::ButtonText color
+        Triangle, 
+
+        //! Paint a circle in QPalette::ButtonText color
+        Dot, 
+
+        /*! 
+          Draw a raised ellipse with a gradient build from
+          QPalette::Light and QPalette::Mid
+         */ 
+        Nub, 
+
+        /*! 
+          Draw a sunken ellipse with a gradient build from
+          QPalette::Light and QPalette::Mid
+         */ 
+        Notch 
+    };
+
+    explicit QwtKnob( QWidget* parent = NULL );
+    virtual ~QwtKnob();
+
+    void setAlignment( Qt::Alignment );
+    Qt::Alignment alignment() const;
+
+    void setKnobWidth( int );
+    int knobWidth() const;
+
+    void setNumTurns( int );
+    int numTurns() const;
+
+    void setTotalAngle ( double angle );
+    double totalAngle() const;
+
+    void setKnobStyle( KnobStyle );
+    KnobStyle knobStyle() const;
+
+    void setBorderWidth( int bw );
+    int borderWidth() const;
+
+    void setMarkerStyle( MarkerStyle );
+    MarkerStyle markerStyle() const;
+
+    void setMarkerSize( int );
+    int markerSize() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    void setScaleDraw( QwtRoundScaleDraw * );
+
+    const QwtRoundScaleDraw *scaleDraw() const;
+    QwtRoundScaleDraw *scaleDraw();
+
+    QRect knobRect() const;
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+    virtual void changeEvent( QEvent * );
+
+    virtual void drawKnob( QPainter *, const QRectF & ) const;
+
+    virtual void drawFocusIndicator( QPainter * ) const;
+
+    virtual void drawMarker( QPainter *, 
+        const QRectF &, double arc ) const;
+
+    virtual double scrolledTo( const QPoint & ) const;
+    virtual bool isScrollPosition( const QPoint & ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_legend.h b/include/pli_vis/third_party/qwt/qwt_legend.h
new file mode 100644
index 0000000000000000000000000000000000000000..746bf3b463fe5038afec541815380080692b934f
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_legend.h
@@ -0,0 +1,117 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_LEGEND_H
+#define QWT_LEGEND_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_abstract_legend.h"
+#include <qvariant.h>
+
+class QScrollBar;
+
+/*!
+  \brief The legend widget
+
+  The QwtLegend widget is a tabular arrangement of legend items. Legend
+  items might be any type of widget, but in general they will be
+  a QwtLegendLabel.
+
+  \sa QwtLegendLabel, QwtPlotItem, QwtPlot
+*/
+
+class QWT_EXPORT QwtLegend : public QwtAbstractLegend
+{
+    Q_OBJECT
+
+public:
+    explicit QwtLegend( QWidget *parent = NULL );
+    virtual ~QwtLegend();
+
+    void setMaxColumns( uint numColums );
+    uint maxColumns() const;
+
+    void setDefaultItemMode( QwtLegendData::Mode );
+    QwtLegendData::Mode defaultItemMode() const;
+
+    QWidget *contentsWidget();
+    const QWidget *contentsWidget() const;
+
+    QWidget *legendWidget( const QVariant &  ) const;
+    QList<QWidget *> legendWidgets( const QVariant & ) const;
+
+    QVariant itemInfo( const QWidget * ) const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+    virtual QSize sizeHint() const;
+    virtual int heightForWidth( int w ) const;
+
+    QScrollBar *horizontalScrollBar() const;
+    QScrollBar *verticalScrollBar() const;
+
+    virtual void renderLegend( QPainter *, 
+        const QRectF &, bool fillBackground ) const;
+
+    virtual void renderItem( QPainter *, 
+        const QWidget *, const QRectF &, bool fillBackground ) const;
+
+    virtual bool isEmpty() const;
+    virtual int scrollExtent( Qt::Orientation ) const;
+
+Q_SIGNALS:
+    /*!
+      A signal which is emitted when the user has clicked on
+      a legend label, which is in QwtLegendData::Clickable mode.
+
+      \param itemInfo Info for the item item of the
+                      selected legend item
+      \param index Index of the legend label in the list of widgets
+                   that are associated with the plot item
+
+      \note clicks are disabled as default
+      \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo()
+     */
+    void clicked( const QVariant &itemInfo, int index );
+
+    /*!
+      A signal which is emitted when the user has clicked on
+      a legend label, which is in QwtLegendData::Checkable mode
+
+      \param itemInfo Info for the item of the
+                      selected legend label
+      \param index Index of the legend label in the list of widgets
+                   that are associated with the plot item
+      \param on True when the legend label is checked
+
+      \note clicks are disabled as default
+      \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo()
+     */
+    void checked( const QVariant &itemInfo, bool on, int index );
+
+public Q_SLOTS:
+    virtual void updateLegend( const QVariant &,
+        const QList<QwtLegendData> & );
+
+protected Q_SLOTS:
+    void itemClicked();
+    void itemChecked( bool );
+
+protected:
+    virtual QWidget *createWidget( const QwtLegendData & ) const;
+    virtual void updateWidget( QWidget *widget, const QwtLegendData &data );
+
+private:
+    void updateTabOrder();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif 
diff --git a/include/pli_vis/third_party/qwt/qwt_legend_data.h b/include/pli_vis/third_party/qwt/qwt_legend_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..192b5536c8754e0b935783aacfc39197297a84a7
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_legend_data.h
@@ -0,0 +1,87 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_LEGEND_DATA_H
+#define QWT_LEGEND_DATA_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_graphic.h"
+#include <qvariant.h>
+#include <qpixmap.h>
+#include <qmap.h>
+
+/*!
+  \brief Attributes of an entry on a legend
+
+  QwtLegendData is an abstract container ( like QAbstractModel )
+  to exchange attributes, that are only known between to 
+  the plot item and the legend. 
+  
+  By overloading QwtPlotItem::legendData() any other set of attributes
+  could be used, that can be handled by a modified ( or completely 
+  different ) implementation of a legend.
+
+  \sa QwtLegend, QwtPlotLegendItem
+  \note The stockchart example implements a legend as a tree
+        with checkable items
+ */
+class QWT_EXPORT QwtLegendData
+{
+public:
+    //! Mode defining how a legend entry interacts
+    enum Mode
+    {
+        //! The legend item is not interactive, like a label
+        ReadOnly,
+
+        //! The legend item is clickable, like a push button
+        Clickable,
+
+        //! The legend item is checkable, like a checkable button
+        Checkable
+    };
+
+    //! Identifier how to interprete a QVariant
+    enum Role
+    {
+        // The value is a Mode
+        ModeRole, 
+
+        // The value is a title
+        TitleRole, 
+
+        // The value is an icon
+        IconRole, 
+
+        // Values < UserRole are reserved for internal use
+        UserRole  = 32
+    };
+
+    QwtLegendData();
+    ~QwtLegendData();
+
+    void setValues( const QMap<int, QVariant> & );
+    const QMap<int, QVariant> &values() const;
+
+    void setValue( int role, const QVariant & );
+    QVariant value( int role ) const;
+
+    bool hasRole( int role ) const;
+    bool isValid() const;
+
+    QwtGraphic icon() const;
+    QwtText title() const;
+    Mode mode() const;
+
+private:
+    QMap<int, QVariant> d_map;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_legend_label.h b/include/pli_vis/third_party/qwt/qwt_legend_label.h
new file mode 100644
index 0000000000000000000000000000000000000000..d78ed60bee974bc6c8e3e5f3dff3774fdb1b3c31
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_legend_label.h
@@ -0,0 +1,80 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_LEGEND_LABEL_H
+#define QWT_LEGEND_LABEL_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_legend_data.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_text_label.h"
+#include <qpixmap.h>
+
+class QwtLegendData;
+
+/*!
+  \brief A widget representing something on a QwtLegend.
+*/
+class QWT_EXPORT QwtLegendLabel: public QwtTextLabel
+{
+    Q_OBJECT
+public:
+    explicit QwtLegendLabel( QWidget *parent = 0 );
+    virtual ~QwtLegendLabel();
+
+    void setData( const QwtLegendData & );
+    const QwtLegendData &data() const;
+
+    void setItemMode( QwtLegendData::Mode );
+    QwtLegendData::Mode itemMode() const;
+
+    void setSpacing( int spacing );
+    int spacing() const;
+
+    virtual void setText( const QwtText & );
+
+    void setIcon( const QPixmap & );
+    QPixmap icon() const;
+
+    virtual QSize sizeHint() const;
+
+    bool isChecked() const;
+
+public Q_SLOTS:
+    void setChecked( bool on );
+
+Q_SIGNALS:
+    //! Signal, when the legend item has been clicked
+    void clicked();
+
+    //! Signal, when the legend item has been pressed
+    void pressed();
+
+    //! Signal, when the legend item has been released
+    void released();
+
+    //! Signal, when the legend item has been toggled
+    void checked( bool );
+
+protected:
+    void setDown( bool );
+    bool isDown() const;
+
+    virtual void paintEvent( QPaintEvent * );
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void mouseReleaseEvent( QMouseEvent * );
+    virtual void keyPressEvent( QKeyEvent * );
+    virtual void keyReleaseEvent( QKeyEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif 
diff --git a/include/pli_vis/third_party/qwt/qwt_magnifier.h b/include/pli_vis/third_party/qwt/qwt_magnifier.h
new file mode 100644
index 0000000000000000000000000000000000000000..8dd30c0a09bad3e056d2d81dbaf12729525abbcf
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_magnifier.h
@@ -0,0 +1,86 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_MAGNIFIER_H
+#define QWT_MAGNIFIER_H 1
+
+#include "qwt/qwt_global.h"
+#include <qobject.h>
+
+class QWidget;
+class QMouseEvent;
+class QWheelEvent;
+class QKeyEvent;
+
+/*!
+  \brief QwtMagnifier provides zooming, by magnifying in steps.
+
+  Using QwtMagnifier a plot can be zoomed in/out in steps using
+  keys, the mouse wheel or moving a mouse button in vertical direction.
+*/
+class QWT_EXPORT QwtMagnifier: public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit QwtMagnifier( QWidget * );
+    virtual ~QwtMagnifier();
+
+    QWidget *parentWidget();
+    const QWidget *parentWidget() const;
+
+    void setEnabled( bool );
+    bool isEnabled() const;
+
+    // mouse
+    void setMouseFactor( double );
+    double mouseFactor() const;
+
+    void setMouseButton( Qt::MouseButton, Qt::KeyboardModifiers = Qt::NoModifier );
+    void getMouseButton( Qt::MouseButton &, Qt::KeyboardModifiers & ) const;
+
+    // mouse wheel
+    void setWheelFactor( double );
+    double wheelFactor() const;
+
+    void setWheelModifiers( Qt::KeyboardModifiers );
+    Qt::KeyboardModifiers wheelModifiers() const;
+
+    // keyboard
+    void setKeyFactor( double );
+    double keyFactor() const;
+
+    void setZoomInKey( int key, Qt::KeyboardModifiers = Qt::NoModifier );
+    void getZoomInKey( int &key, Qt::KeyboardModifiers & ) const;
+
+    void setZoomOutKey( int key, Qt::KeyboardModifiers = Qt::NoModifier );
+    void getZoomOutKey( int &key, Qt::KeyboardModifiers & ) const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+protected:
+    /*!
+       Rescale the parent widget
+       \param factor Scale factor
+     */
+    virtual void rescale( double factor ) = 0;
+
+    virtual void widgetMousePressEvent( QMouseEvent * );
+    virtual void widgetMouseReleaseEvent( QMouseEvent * );
+    virtual void widgetMouseMoveEvent( QMouseEvent * );
+    virtual void widgetWheelEvent( QWheelEvent * );
+    virtual void widgetKeyPressEvent( QKeyEvent * );
+    virtual void widgetKeyReleaseEvent( QKeyEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_math.h b/include/pli_vis/third_party/qwt/qwt_math.h
new file mode 100644
index 0000000000000000000000000000000000000000..7a69fec06de36c1830f844ef7d147766e002ab1b
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_math.h
@@ -0,0 +1,149 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_MATH_H
+#define QWT_MATH_H
+
+#include "qwt/qwt_global.h"
+
+#if defined(_MSC_VER)
+/*
+  Microsoft says:
+
+  Define _USE_MATH_DEFINES before including math.h to expose these macro
+  definitions for common math constants.  These are placed under an #ifdef
+  since these commonly-defined names are not part of the C/C++ standards.
+*/
+#define _USE_MATH_DEFINES 1
+#endif
+
+#include <qmath.h>
+#include "qwt/qwt_global.h"
+
+#ifndef M_PI_2
+// For Qt <= 4.8.4 M_PI_2 is not known by MinGW-w64 
+// when compiling with -std=c++11
+#define M_PI_2 (1.57079632679489661923)
+#endif
+
+#ifndef LOG_MIN
+//! Minimum value for logarithmic scales
+#define LOG_MIN 1.0e-100
+#endif
+
+#ifndef LOG_MAX
+//! Maximum value for logarithmic scales
+#define LOG_MAX 1.0e100
+#endif
+
+QWT_EXPORT double qwtGetMin( const double *array, int size );
+QWT_EXPORT double qwtGetMax( const double *array, int size );
+
+QWT_EXPORT double qwtNormalizeRadians( double radians );
+QWT_EXPORT double qwtNormalizeDegrees( double degrees );
+
+/*!
+  \brief Compare 2 values, relative to an interval
+
+  Values are "equal", when :
+  \f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$
+
+  \param value1 First value to compare
+  \param value2 Second value to compare
+  \param intervalSize interval size
+
+  \return 0: if equal, -1: if value2 > value1, 1: if value1 > value2
+*/
+inline int qwtFuzzyCompare( double value1, double value2, double intervalSize )
+{
+    const double eps = qAbs( 1.0e-6 * intervalSize );
+
+    if ( value2 - value1 > eps )
+        return -1;
+
+    if ( value1 - value2 > eps )
+        return 1;
+
+    return 0;
+}
+
+
+inline bool qwtFuzzyGreaterOrEqual( double d1, double d2 )
+{
+    return ( d1 >= d2 ) || qFuzzyCompare( d1, d2 );
+}
+
+inline bool qwtFuzzyLessOrEqual( double d1, double d2 )
+{
+    return ( d1 <= d2 ) || qFuzzyCompare( d1, d2 );
+}
+
+//! Return the sign
+inline int qwtSign( double x )
+{
+    if ( x > 0.0 )
+        return 1;
+    else if ( x < 0.0 )
+        return ( -1 );
+    else
+        return 0;
+}
+
+//! Return the square of a number
+inline double qwtSqr( double x )
+{
+    return x * x;
+}
+
+//! Approximation of arc tangent ( error below 0,005 radians )
+inline double qwtFastAtan( double x )
+{
+    if ( x < -1.0 )
+        return -M_PI_2 - x / ( x * x + 0.28 );
+
+    if ( x > 1.0 )
+        return M_PI_2 - x / ( x * x + 0.28 );
+
+    return x / ( 1.0 + x * x * 0.28 );
+}
+
+//! Approximation of arc tangent ( error below 0,005 radians )
+inline double qwtFastAtan2( double y, double x )
+{
+    if ( x > 0 )
+        return qwtFastAtan( y / x );
+
+    if ( x < 0 )
+    {
+        const double d = qwtFastAtan( y / x );
+        return ( y >= 0 ) ? d + M_PI : d - M_PI;
+    }
+
+    if ( y < 0.0 )
+        return -M_PI_2;
+
+    if ( y > 0.0 )
+        return M_PI_2;
+
+    return 0.0;
+}
+
+//! Translate degrees into radians
+inline double qwtRadians( double degrees )
+{
+    return degrees * M_PI / 180.0;
+}
+
+//! Translate radians into degrees
+inline double qwtDegrees( double degrees )
+{
+    return degrees * 180.0 / M_PI;
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_matrix_raster_data.h b/include/pli_vis/third_party/qwt/qwt_matrix_raster_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..475bdb9c3bf80ec4cd9b43713746b46477f8d110
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_matrix_raster_data.h
@@ -0,0 +1,74 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_MATRIX_RASTER_DATA_H
+#define QWT_MATRIX_RASTER_DATA_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_raster_data.h"
+#include <qvector.h>
+
+/*!
+  \brief A class representing a matrix of values as raster data
+
+  QwtMatrixRasterData implements an interface for a matrix of
+  equidistant values, that can be used by a QwtPlotRasterItem. 
+  It implements a couple of resampling algorithms, to provide
+  values for positions, that or not on the value matrix.
+*/
+class QWT_EXPORT QwtMatrixRasterData: public QwtRasterData
+{
+public:
+    /*!
+      \brief Resampling algorithm
+      The default setting is NearestNeighbour;
+    */
+    enum ResampleMode
+    {
+        /*!
+          Return the value from the matrix, that is nearest to the
+          the requested position.
+         */
+        NearestNeighbour,
+
+        /*!
+          Interpolate the value from the distances and values of the 
+          4 surrounding values in the matrix,
+         */
+        BilinearInterpolation
+    };
+
+    QwtMatrixRasterData();
+    virtual ~QwtMatrixRasterData();
+
+    void setResampleMode(ResampleMode mode);
+    ResampleMode resampleMode() const;
+
+    virtual void setInterval( Qt::Axis, const QwtInterval & );
+
+    void setValueMatrix( const QVector<double> &values, int numColumns );
+    const QVector<double> valueMatrix() const;
+
+    void setValue( int row, int col, double value );
+
+    int numColumns() const;
+    int numRows() const;
+
+    virtual QRectF pixelHint( const QRectF & ) const;
+
+    virtual double value( double x, double y ) const;
+
+private:
+    void update();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_null_paintdevice.h b/include/pli_vis/third_party/qwt/qwt_null_paintdevice.h
new file mode 100644
index 0000000000000000000000000000000000000000..825618b5a0def588d9a67e788f85967bc02d6b97
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_null_paintdevice.h
@@ -0,0 +1,126 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_NULL_PAINT_DEVICE_H
+#define QWT_NULL_PAINT_DEVICE_H 1
+
+#include "qwt/qwt_global.h"
+#include <qpaintdevice.h>
+#include <qpaintengine.h>
+
+/*!
+  \brief A null paint device doing nothing
+
+  Sometimes important layout/rendering geometries are not 
+  available or changeable from the public Qt class interface. 
+  ( f.e hidden in the style implementation ).
+
+  QwtNullPaintDevice can be used to manipulate or filter out 
+  this information by analyzing the stream of paint primitives.
+
+  F.e. QwtNullPaintDevice is used by QwtPlotCanvas to identify
+  styled backgrounds with rounded corners.
+*/
+
+class QWT_EXPORT QwtNullPaintDevice: public QPaintDevice
+{
+public:
+    /*!
+      \brief Render mode
+
+      \sa setMode(), mode()
+     */
+    enum Mode
+    {
+        /*!
+           All vector graphic primitives are painted by
+           the corresponding draw methods
+         */
+        NormalMode, 
+
+        /*!
+           Vector graphic primitives ( beside polygons ) are mapped to a QPainterPath
+           and are painted by drawPath. In PathMode mode
+           only a few draw methods are called:
+
+           - drawPath()
+           - drawPixmap()
+           - drawImage()
+           - drawPolygon()
+         */
+        PolygonPathMode,
+
+        /*!
+           Vector graphic primitives are mapped to a QPainterPath
+           and are painted by drawPath. In PathMode mode
+           only a few draw methods are called:
+
+           - drawPath()
+           - drawPixmap()
+           - drawImage()
+         */
+        PathMode
+    };
+
+    QwtNullPaintDevice();
+    virtual ~QwtNullPaintDevice();
+
+    void setMode( Mode );
+    Mode mode() const;
+
+    virtual QPaintEngine *paintEngine() const;
+
+    virtual int metric( PaintDeviceMetric metric ) const;
+
+    virtual void drawRects(const QRect *, int );
+    virtual void drawRects(const QRectF *, int );
+
+    virtual void drawLines(const QLine *, int );
+    virtual void drawLines(const QLineF *, int );
+
+    virtual void drawEllipse(const QRectF &);
+    virtual void drawEllipse(const QRect &);
+
+    virtual void drawPath(const QPainterPath &);
+
+    virtual void drawPoints(const QPointF *, int );
+    virtual void drawPoints(const QPoint *, int );
+
+    virtual void drawPolygon(
+        const QPointF *, int , QPaintEngine::PolygonDrawMode );
+
+    virtual void drawPolygon(
+        const QPoint *, int , QPaintEngine::PolygonDrawMode );
+
+    virtual void drawPixmap(const QRectF &,
+        const QPixmap &, const QRectF &);
+
+    virtual void drawTextItem(const QPointF &, const QTextItem &);
+
+    virtual void drawTiledPixmap(const QRectF &,
+        const QPixmap &, const QPointF &s);
+
+    virtual void drawImage(const QRectF &,
+        const QImage &, const QRectF &, Qt::ImageConversionFlags );
+
+    virtual void updateState( const QPaintEngineState &state );
+
+protected:
+    //! \return Size needed to implement metric()
+    virtual QSize sizeMetrics() const = 0;
+
+private:
+    class PaintEngine;
+    PaintEngine *d_engine;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_painter.h b/include/pli_vis/third_party/qwt/qwt_painter.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0e183a7673b72c4e208d8fca791413914bad4b2
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_painter.h
@@ -0,0 +1,188 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PAINTER_H
+#define QWT_PAINTER_H
+
+#include "qwt/qwt_global.h"
+
+#include <qpoint.h>
+#include <qrect.h>
+#include <qpen.h>
+#include <qline.h>
+#include <qpalette.h>
+
+class QPainter;
+class QBrush;
+class QColor;
+class QWidget;
+class QPolygonF;
+class QRectF;
+class QImage;
+class QPixmap;
+class QwtScaleMap;
+class QwtColorMap;
+class QwtInterval;
+
+class QTextDocument;
+class QPainterPath;
+
+/*!
+  \brief A collection of QPainter workarounds
+*/
+class QWT_EXPORT QwtPainter
+{
+public:
+    static void setPolylineSplitting( bool );
+    static bool polylineSplitting();
+
+    static void setRoundingAlignment( bool );
+    static bool roundingAlignment();
+    static bool roundingAlignment(QPainter *);
+
+    static void drawText( QPainter *, double x, double y, const QString & );
+    static void drawText( QPainter *, const QPointF &, const QString & );
+    static void drawText( QPainter *, double x, double y, double w, double h,
+        int flags, const QString & );
+    static void drawText( QPainter *, const QRectF &, 
+        int flags, const QString & );
+
+#ifndef QT_NO_RICHTEXT
+    static void drawSimpleRichText( QPainter *, const QRectF &,
+        int flags, const QTextDocument & );
+#endif
+
+    static void drawRect( QPainter *, double x, double y, double w, double h );
+    static void drawRect( QPainter *, const QRectF &rect );
+    static void fillRect( QPainter *, const QRectF &, const QBrush & );
+
+    static void drawEllipse( QPainter *, const QRectF & );
+    static void drawPie( QPainter *, const QRectF & r, int a, int alen );
+
+    static void drawLine( QPainter *, double x1, double y1, double x2, double y2 );
+    static void drawLine( QPainter *, const QPointF &p1, const QPointF &p2 );
+    static void drawLine( QPainter *, const QLineF & );
+
+    static void drawPolygon( QPainter *, const QPolygonF & );
+    static void drawPolyline( QPainter *, const QPolygonF & );
+    static void drawPolyline( QPainter *, const QPointF *, int pointCount );
+
+    static void drawPolygon( QPainter *, const QPolygon & );
+    static void drawPolyline( QPainter *, const QPolygon & );
+    static void drawPolyline( QPainter *, const QPoint *, int pointCount );
+
+    static void drawPoint( QPainter *, const QPoint & );
+    static void drawPoints( QPainter *, const QPolygon & );
+    static void drawPoints( QPainter *, const QPoint *, int pointCount );
+
+    static void drawPoint( QPainter *, double x, double y );
+    static void drawPoint( QPainter *, const QPointF & );
+    static void drawPoints( QPainter *, const QPolygonF & );
+    static void drawPoints( QPainter *, const QPointF *, int pointCount );
+
+    static void drawPath( QPainter *, const QPainterPath & );
+    static void drawImage( QPainter *, const QRectF &, const QImage & );
+    static void drawPixmap( QPainter *, const QRectF &, const QPixmap & );
+
+    static void drawRoundFrame( QPainter *,
+        const QRectF &, const QPalette &, int lineWidth, int frameStyle );
+
+    static void drawRoundedFrame( QPainter *, 
+        const QRectF &, double xRadius, double yRadius,
+        const QPalette &, int lineWidth, int frameStyle );
+
+    static void drawFrame( QPainter *, const QRectF &rect,
+        const QPalette &palette, QPalette::ColorRole foregroundRole,
+        int lineWidth, int midLineWidth, int frameStyle ); 
+
+    static void drawFocusRect( QPainter *, const QWidget * );
+    static void drawFocusRect( QPainter *, const QWidget *, const QRect & );
+
+    static void drawColorBar( QPainter *painter,
+        const QwtColorMap &, const QwtInterval &,
+        const QwtScaleMap &, Qt::Orientation, const QRectF & );
+
+    static bool isAligning( QPainter *painter );
+    static bool isX11GraphicsSystem();
+
+    static void fillPixmap( const QWidget *, 
+        QPixmap &, const QPoint &offset = QPoint() );
+
+    static void drawBackgound( QPainter *painter,
+        const QRectF &rect, const QWidget *widget );
+
+    static QPixmap backingStore( QWidget *, const QSize & );
+
+private:
+    static bool d_polylineSplitting;
+    static bool d_roundingAlignment;
+};
+
+//!  Wrapper for QPainter::drawPoint()
+inline void QwtPainter::drawPoint( QPainter *painter, double x, double y )
+{
+    QwtPainter::drawPoint( painter, QPointF( x, y ) );
+}
+
+//! Wrapper for QPainter::drawPoints()
+inline void QwtPainter::drawPoints( QPainter *painter, const QPolygon &polygon )
+{
+    drawPoints( painter, polygon.data(), polygon.size() );
+}
+
+//! Wrapper for QPainter::drawPoints()
+inline void QwtPainter::drawPoints( QPainter *painter, const QPolygonF &polygon )
+{
+    drawPoints( painter, polygon.data(), polygon.size() );
+}
+
+//!  Wrapper for QPainter::drawLine()
+inline void QwtPainter::drawLine( QPainter *painter,
+    double x1, double y1, double x2, double y2 )
+{
+    QwtPainter::drawLine( painter, QPointF( x1, y1 ), QPointF( x2, y2 ) );
+}
+
+//!  Wrapper for QPainter::drawLine()
+inline void QwtPainter::drawLine( QPainter *painter, const QLineF &line )
+{
+    QwtPainter::drawLine( painter, line.p1(), line.p2() );
+}
+
+/*!
+  \return True, when line splitting for the raster paint engine is enabled.
+  \sa setPolylineSplitting()
+*/
+inline bool QwtPainter::polylineSplitting()
+{
+    return d_polylineSplitting;
+}
+
+/*!
+  Check whether coordinates should be rounded, before they are painted
+  to a paint engine that rounds to integer values. For other paint engines
+  ( PDF, SVG ), this flag has no effect.
+
+  \return True, when rounding is enabled
+  \sa setRoundingAlignment(), isAligning()
+*/
+inline bool QwtPainter::roundingAlignment()
+{
+    return d_roundingAlignment;
+}
+
+/*!
+  \return roundingAlignment() && isAligning(painter);
+  \param painter Painter
+*/
+inline bool QwtPainter::roundingAlignment(QPainter *painter)
+{
+    return d_roundingAlignment && isAligning(painter);
+}
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_painter_command.h b/include/pli_vis/third_party/qwt/qwt_painter_command.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed57f2f35bbc8fd5477cb58652704e22d030ef7b
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_painter_command.h
@@ -0,0 +1,173 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PAINTER_COMMAND_H
+#define QWT_PAINTER_COMMAND_H
+
+#include "qwt/qwt_global.h"
+#include <qpaintengine.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qpolygon.h>
+
+class QPainterPath;
+
+/*!
+  QwtPainterCommand represents the attributes of a paint operation
+  how it is used between QPainter and QPaintDevice
+
+  It is used by QwtGraphic to record and replay paint operations
+
+  \sa QwtGraphic::commands()
+ */
+  
+class QWT_EXPORT QwtPainterCommand
+{
+public:
+    //! Type of the paint command
+    enum Type
+    {
+        //! Invalid command
+        Invalid = -1,
+
+        //! Draw a QPainterPath
+        Path,
+
+        //! Draw a QPixmap
+        Pixmap,
+
+        //! Draw a QImage
+        Image,
+
+        //! QPainter state change
+        State
+    };
+
+    //! Attributes how to paint a QPixmap 
+    struct PixmapData
+    {
+        QRectF rect;
+        QPixmap pixmap;
+        QRectF subRect;
+    };
+
+    //! Attributes how to paint a QImage 
+    struct ImageData
+    {
+        QRectF rect;
+        QImage image;
+        QRectF subRect;
+        Qt::ImageConversionFlags flags;
+    };
+
+    //! Attributes of a state change
+    struct StateData
+    {
+        QPaintEngine::DirtyFlags flags;
+
+        QPen pen;
+        QBrush brush;
+        QPointF brushOrigin;
+        QBrush backgroundBrush;
+        Qt::BGMode backgroundMode;
+        QFont font;
+        QMatrix matrix;
+        QTransform transform;
+
+        Qt::ClipOperation clipOperation;
+        QRegion clipRegion;
+        QPainterPath clipPath;
+        bool isClipEnabled;
+
+        QPainter::RenderHints renderHints;
+        QPainter::CompositionMode compositionMode;
+        qreal opacity;
+    };
+
+    QwtPainterCommand();
+    QwtPainterCommand(const QwtPainterCommand &);
+
+    QwtPainterCommand( const QPainterPath & );
+
+    QwtPainterCommand( const QRectF &rect,
+            const QPixmap &, const QRectF& subRect );
+
+    QwtPainterCommand( const QRectF &rect,
+            const QImage &, const QRectF& subRect,
+            Qt::ImageConversionFlags );
+
+    QwtPainterCommand( const QPaintEngineState & );
+
+    ~QwtPainterCommand();
+
+    QwtPainterCommand &operator=(const QwtPainterCommand & );
+
+    Type type() const;
+
+    QPainterPath *path();
+    const QPainterPath *path() const;
+
+    PixmapData* pixmapData();
+    const PixmapData* pixmapData() const;
+
+    ImageData* imageData();
+    const ImageData* imageData() const;
+
+    StateData* stateData();
+    const StateData* stateData() const;
+
+private:
+    void copy( const QwtPainterCommand & );
+    void reset();
+
+    Type d_type;
+
+    union
+    {
+        QPainterPath *d_path;
+        PixmapData *d_pixmapData;
+        ImageData *d_imageData;
+        StateData *d_stateData;
+    };
+};
+
+//! \return Type of the command
+inline QwtPainterCommand::Type QwtPainterCommand::type() const
+{
+    return d_type;
+}
+
+//! \return Painter path to be painted
+inline const QPainterPath *QwtPainterCommand::path() const
+{
+    return d_path;
+}
+
+//! \return Attributes how to paint a QPixmap
+inline const QwtPainterCommand::PixmapData* 
+QwtPainterCommand::pixmapData() const
+{
+    return d_pixmapData;
+}
+
+//! \return Attributes how to paint a QImage
+inline const QwtPainterCommand::ImageData * 
+QwtPainterCommand::imageData() const
+{
+    return d_imageData;
+}
+
+//! \return Attributes of a state change
+inline const QwtPainterCommand::StateData * 
+QwtPainterCommand::stateData() const
+{
+    return d_stateData;
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_panner.h b/include/pli_vis/third_party/qwt/qwt_panner.h
new file mode 100644
index 0000000000000000000000000000000000000000..fefe0a2b8a44c332c2a8f67662bc42fcee2b1c64
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_panner.h
@@ -0,0 +1,103 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PANNER_H
+#define QWT_PANNER_H 1
+
+#include "qwt/qwt_global.h"
+#include <qwidget.h>
+#include <qpixmap.h>
+
+class QCursor;
+
+/*!
+  \brief QwtPanner provides panning of a widget
+
+  QwtPanner grabs the contents of a widget, that can be dragged
+  in all directions. The offset between the start and the end position
+  is emitted by the panned signal.
+
+  QwtPanner grabs the content of the widget into a pixmap and moves
+  the pixmap around, without initiating any repaint events for the widget.
+  Areas, that are not part of content are not painted  while panning.
+  This makes panning fast enough for widgets, where
+  repaints are too slow for mouse movements.
+
+  For widgets, where repaints are very fast it might be better to
+  implement panning manually by mapping mouse events into paint events.
+*/
+class QWT_EXPORT QwtPanner: public QWidget
+{
+    Q_OBJECT
+
+public:
+    QwtPanner( QWidget* parent );
+    virtual ~QwtPanner();
+
+    void setEnabled( bool );
+    bool isEnabled() const;
+
+    void setMouseButton( Qt::MouseButton, 
+        Qt::KeyboardModifiers = Qt::NoModifier );
+    void getMouseButton( Qt::MouseButton &button, 
+        Qt::KeyboardModifiers & ) const;
+
+    void setAbortKey( int key, Qt::KeyboardModifiers = Qt::NoModifier );
+    void getAbortKey( int &key, Qt::KeyboardModifiers & ) const;
+
+    void setCursor( const QCursor & );
+    const QCursor cursor() const;
+
+    void setOrientations( Qt::Orientations );
+    Qt::Orientations orientations() const;
+
+    bool isOrientationEnabled( Qt::Orientation ) const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+Q_SIGNALS:
+    /*!
+      Signal emitted, when panning is done
+
+      \param dx Offset in horizontal direction
+      \param dy Offset in vertical direction
+    */
+    void panned( int dx, int dy );
+
+    /*!
+      Signal emitted, while the widget moved, but panning
+      is not finished.
+
+      \param dx Offset in horizontal direction
+      \param dy Offset in vertical direction
+    */
+    void moved( int dx, int dy );
+
+protected:
+    virtual void widgetMousePressEvent( QMouseEvent * );
+    virtual void widgetMouseReleaseEvent( QMouseEvent * );
+    virtual void widgetMouseMoveEvent( QMouseEvent * );
+    virtual void widgetKeyPressEvent( QKeyEvent * );
+    virtual void widgetKeyReleaseEvent( QKeyEvent * );
+
+    virtual void paintEvent( QPaintEvent * );
+
+    virtual QBitmap contentsMask() const;
+    virtual QPixmap grab() const;
+
+private:
+#ifndef QT_NO_CURSOR
+    void showCursor( bool );
+#endif
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_picker.h b/include/pli_vis/third_party/qwt/qwt_picker.h
new file mode 100644
index 0000000000000000000000000000000000000000..cf72f19a382c2759fb0b6d4d56cf56b20be5ad8c
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_picker.h
@@ -0,0 +1,329 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PICKER
+#define QWT_PICKER 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_event_pattern.h"
+#include <qobject.h>
+#include <qpen.h>
+#include <qfont.h>
+#include <qrect.h>
+#include <qpainterpath.h>
+
+class QWidget;
+class QMouseEvent;
+class QWheelEvent;
+class QKeyEvent;
+class QwtPickerMachine;
+class QwtWidgetOverlay;
+
+/*!
+  \brief QwtPicker provides selections on a widget
+
+  QwtPicker filters all enter, leave, mouse and keyboard events of a widget
+  and translates them into an array of selected points.
+
+  The way how the points are collected depends on type of state machine
+  that is connected to the picker. Qwt offers a couple of predefined
+  state machines for selecting:
+
+  - Nothing\n
+    QwtPickerTrackerMachine
+  - Single points\n
+    QwtPickerClickPointMachine, QwtPickerDragPointMachine
+  - Rectangles\n
+    QwtPickerClickRectMachine, QwtPickerDragRectMachine
+  - Polygons\n
+    QwtPickerPolygonMachine
+
+  While these state machines cover the most common ways to collect points
+  it is also possible to implement individual machines as well.
+
+  QwtPicker translates the picked points into a selection using the
+  adjustedPoints() method. adjustedPoints() is intended to be reimplemented
+  to fix up the selection according to application specific requirements.
+  (F.e. when an application accepts rectangles of a fixed aspect ratio only.)
+
+  Optionally QwtPicker support the process of collecting points by a
+  rubber band and tracker displaying a text for the current mouse
+  position.
+
+  \par Example
+  \verbatim #include <qwt_picker.h>
+#include <qwt_picker_machine.h>
+
+QwtPicker *picker = new QwtPicker(widget);
+picker->setStateMachine(new QwtPickerDragRectMachine);
+picker->setTrackerMode(QwtPicker::ActiveOnly);
+picker->setRubberBand(QwtPicker::RectRubberBand); \endverbatim\n
+
+  The state machine triggers the following commands:
+
+  - begin()\n
+    Activate/Initialize the selection.
+  - append()\n
+    Add a new point
+  - move() \n
+    Change the position of the last point.
+  - remove()\n
+    Remove the last point.
+  - end()\n
+    Terminate the selection and call accept to validate the picked points.
+
+  The picker is active (isActive()), between begin() and end().
+  In active state the rubber band is displayed, and the tracker is visible
+  in case of trackerMode is ActiveOnly or AlwaysOn.
+
+  The cursor can be moved using the arrow keys. All selections can be aborted
+  using the abort key. (QwtEventPattern::KeyPatternCode)
+
+  \warning In case of QWidget::NoFocus the focus policy of the observed
+           widget is set to QWidget::WheelFocus and mouse tracking
+           will be manipulated while the picker is active,
+           or if trackerMode() is AlwayOn.
+*/
+
+class QWT_EXPORT QwtPicker: public QObject, public QwtEventPattern
+{
+    Q_OBJECT
+
+    Q_ENUMS( RubberBand DisplayMode ResizeMode )
+
+    Q_PROPERTY( bool isEnabled READ isEnabled WRITE setEnabled )
+    Q_PROPERTY( ResizeMode resizeMode READ resizeMode WRITE setResizeMode )
+
+    Q_PROPERTY( DisplayMode trackerMode READ trackerMode WRITE setTrackerMode )
+    Q_PROPERTY( QPen trackerPen READ trackerPen WRITE setTrackerPen )
+    Q_PROPERTY( QFont trackerFont READ trackerFont WRITE setTrackerFont )
+
+    Q_PROPERTY( RubberBand rubberBand READ rubberBand WRITE setRubberBand )
+    Q_PROPERTY( QPen rubberBandPen READ rubberBandPen WRITE setRubberBandPen )
+
+public:
+    /*!
+      Rubber band style
+
+      The default value is QwtPicker::NoRubberBand.
+      \sa setRubberBand(), rubberBand()
+    */
+
+    enum RubberBand
+    {
+        //! No rubberband.
+        NoRubberBand = 0,
+
+        //! A horizontal line ( only for QwtPickerMachine::PointSelection )
+        HLineRubberBand,
+
+        //! A vertical line ( only for QwtPickerMachine::PointSelection )
+        VLineRubberBand,
+
+        //! A crosshair ( only for QwtPickerMachine::PointSelection )
+        CrossRubberBand,
+
+        //! A rectangle ( only for QwtPickerMachine::RectSelection )
+        RectRubberBand,
+
+        //! An ellipse ( only for QwtPickerMachine::RectSelection )
+        EllipseRubberBand,
+
+        //! A polygon ( only for QwtPickerMachine::PolygonSelection )
+        PolygonRubberBand,
+
+        /*!
+          Values >= UserRubberBand can be used to define additional
+          rubber bands.
+         */
+        UserRubberBand = 100
+    };
+
+    /*!
+      \brief Display mode
+      \sa setTrackerMode(), trackerMode(), isActive()
+    */
+    enum DisplayMode
+    {
+        //! Display never
+        AlwaysOff,
+
+        //! Display always
+        AlwaysOn,
+
+        //! Display only when the selection is active
+        ActiveOnly
+    };
+
+    /*!
+      Controls what to do with the selected points of an active
+         selection when the observed widget is resized.
+
+      The default value is QwtPicker::Stretch.
+      \sa setResizeMode()
+    */
+
+    enum ResizeMode
+    {
+        //! All points are scaled according to the new size,
+        Stretch,
+
+        //! All points remain unchanged.
+        KeepSize
+    };
+
+    explicit QwtPicker( QWidget *parent );
+    explicit QwtPicker( RubberBand rubberBand,
+                        DisplayMode trackerMode, QWidget * );
+
+    virtual ~QwtPicker();
+
+    void setStateMachine( QwtPickerMachine * );
+    const QwtPickerMachine *stateMachine() const;
+    QwtPickerMachine *stateMachine();
+
+    void setRubberBand( RubberBand );
+    RubberBand rubberBand() const;
+
+    void setTrackerMode( DisplayMode );
+    DisplayMode trackerMode() const;
+
+    void setResizeMode( ResizeMode );
+    ResizeMode resizeMode() const;
+
+    void setRubberBandPen( const QPen & );
+    QPen rubberBandPen() const;
+
+    void setTrackerPen( const QPen & );
+    QPen trackerPen() const;
+
+    void setTrackerFont( const QFont & );
+    QFont trackerFont() const;
+
+    bool isEnabled() const;
+    bool isActive() const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+    QWidget *parentWidget();
+    const QWidget *parentWidget() const;
+
+    virtual QPainterPath pickArea() const;
+
+    virtual void drawRubberBand( QPainter * ) const;
+    virtual void drawTracker( QPainter * ) const;
+
+    virtual QRegion rubberBandMask() const;
+
+    virtual QwtText trackerText( const QPoint &pos ) const;
+    QPoint trackerPosition() const;
+    virtual QRect trackerRect( const QFont & ) const;
+
+    QPolygon selection() const;
+
+public Q_SLOTS:
+    void setEnabled( bool );
+
+Q_SIGNALS:
+    /*!
+      A signal indicating, when the picker has been activated.
+      Together with setEnabled() it can be used to implement
+      selections with more than one picker.
+
+      \param on True, when the picker has been activated
+    */
+    void activated( bool on );
+
+    /*!
+      A signal emitting the selected points,
+      at the end of a selection.
+
+      \param polygon Selected points
+    */
+    void selected( const QPolygon &polygon );
+
+    /*!
+      A signal emitted when a point has been appended to the selection
+
+      \param pos Position of the appended point.
+      \sa append(). moved()
+    */
+    void appended( const QPoint &pos );
+
+    /*!
+      A signal emitted whenever the last appended point of the
+      selection has been moved.
+
+      \param pos Position of the moved last point of the selection.
+      \sa move(), appended()
+    */
+    void moved( const QPoint &pos );
+
+    /*!
+      A signal emitted whenever the last appended point of the
+      selection has been removed.
+
+      \param pos Position of the point, that has been removed
+      \sa remove(), appended()
+    */
+    void removed( const QPoint &pos );
+    /*!
+      A signal emitted when the active selection has been changed.
+      This might happen when the observed widget is resized.
+
+      \param selection Changed selection
+      \sa stretchSelection()
+    */
+    void changed( const QPolygon &selection );
+
+protected:
+    virtual QPolygon adjustedPoints( const QPolygon & ) const;
+
+    virtual void transition( const QEvent * );
+
+    virtual void begin();
+    virtual void append( const QPoint & );
+    virtual void move( const QPoint & );
+    virtual void remove();
+    virtual bool end( bool ok = true );
+
+    virtual bool accept( QPolygon & ) const;
+    virtual void reset();
+
+    virtual void widgetMousePressEvent( QMouseEvent * );
+    virtual void widgetMouseReleaseEvent( QMouseEvent * );
+    virtual void widgetMouseDoubleClickEvent( QMouseEvent * );
+    virtual void widgetMouseMoveEvent( QMouseEvent * );
+    virtual void widgetWheelEvent( QWheelEvent * );
+    virtual void widgetKeyPressEvent( QKeyEvent * );
+    virtual void widgetKeyReleaseEvent( QKeyEvent * );
+    virtual void widgetEnterEvent( QEvent * );
+    virtual void widgetLeaveEvent( QEvent * );
+
+    virtual void stretchSelection( const QSize &oldSize,
+                                   const QSize &newSize );
+
+    virtual void updateDisplay();
+
+    const QwtWidgetOverlay *rubberBandOverlay() const;
+    const QwtWidgetOverlay *trackerOverlay() const;
+
+    const QPolygon &pickedPoints() const;
+
+private:
+    void init( QWidget *, RubberBand rubberBand, DisplayMode trackerMode );
+
+    void setMouseTracking( bool );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_picker_machine.h b/include/pli_vis/third_party/qwt/qwt_picker_machine.h
new file mode 100644
index 0000000000000000000000000000000000000000..bbe4eea8d0a276b3ddcf3e9ce9c380571c2fa986
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_picker_machine.h
@@ -0,0 +1,214 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PICKER_MACHINE
+#define QWT_PICKER_MACHINE 1
+
+#include "qwt/qwt_global.h"
+#include <qlist.h>
+
+class QEvent;
+class QwtEventPattern;
+
+/*!
+  \brief A state machine for QwtPicker selections
+
+  QwtPickerMachine accepts key and mouse events and translates them
+  into selection commands.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+
+class QWT_EXPORT QwtPickerMachine
+{
+public:
+    /*!
+      Type of a selection.
+      \sa selectionType()
+    */
+    enum SelectionType
+    {
+        //! The state machine not usable for any type of selection.
+        NoSelection = -1,
+
+        //! The state machine is for selecting a single point.
+        PointSelection,
+
+        //! The state machine is for selecting a rectangle (2 points).
+        RectSelection,
+
+        //! The state machine is for selecting a polygon (many points).
+        PolygonSelection
+    };
+
+    //! Commands - the output of a state machine
+    enum Command
+    {
+        Begin,
+        Append,
+        Move,
+        Remove,
+        End
+    };
+
+    QwtPickerMachine( SelectionType );
+    virtual ~QwtPickerMachine();
+
+    //! Transition
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * ) = 0;
+    void reset();
+
+    int state() const;
+    void setState( int );
+
+    SelectionType selectionType() const;
+
+private:
+    const SelectionType d_selectionType;
+    int d_state;
+};
+
+/*!
+  \brief A state machine for indicating mouse movements
+
+  QwtPickerTrackerMachine supports displaying information
+  corresponding to mouse movements, but is not intended for
+  selecting anything. Begin/End are related to Enter/Leave events.
+*/
+class QWT_EXPORT QwtPickerTrackerMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerTrackerMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for point selections
+
+  Pressing QwtEventPattern::MouseSelect1 or
+  QwtEventPattern::KeySelect1 selects a point.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+class QWT_EXPORT QwtPickerClickPointMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerClickPointMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for point selections
+
+  Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1
+  starts the selection, releasing QwtEventPattern::MouseSelect1 or
+  a second press of QwtEventPattern::KeySelect1 terminates it.
+*/
+class QWT_EXPORT QwtPickerDragPointMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerDragPointMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for rectangle selections
+
+  Pressing QwtEventPattern::MouseSelect1 starts
+  the selection, releasing it selects the first point. Pressing it
+  again selects the second point and terminates the selection.
+  Pressing QwtEventPattern::KeySelect1 also starts the
+  selection, a second press selects the first point. A third one selects
+  the second point and terminates the selection.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+
+class QWT_EXPORT QwtPickerClickRectMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerClickRectMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for rectangle selections
+
+  Pressing QwtEventPattern::MouseSelect1 selects
+  the first point, releasing it the second point.
+  Pressing QwtEventPattern::KeySelect1 also selects the
+  first point, a second press selects the second point and terminates
+  the selection.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+
+class QWT_EXPORT QwtPickerDragRectMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerDragRectMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for line selections
+    
+  Pressing QwtEventPattern::MouseSelect1 selects
+  the first point, releasing it the second point.
+  Pressing QwtEventPattern::KeySelect1 also selects the
+  first point, a second press selects the second point and terminates
+  the selection.
+
+  A common use case of QwtPickerDragLineMachine are pickers for
+  distance measurements.
+  
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/              
+                    
+class QWT_EXPORT QwtPickerDragLineMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerDragLineMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+/*!
+  \brief A state machine for polygon selections
+
+  Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1
+  starts the selection and selects the first point, or appends a point.
+  Pressing QwtEventPattern::MouseSelect2 or QwtEventPattern::KeySelect2
+  appends the last point and terminates the selection.
+
+  \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode
+*/
+
+class QWT_EXPORT QwtPickerPolygonMachine: public QwtPickerMachine
+{
+public:
+    QwtPickerPolygonMachine();
+
+    virtual QList<Command> transition(
+        const QwtEventPattern &, const QEvent * );
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_pixel_matrix.h b/include/pli_vis/third_party/qwt/qwt_pixel_matrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..2d5f93b5206516cc4b45b87e3fc542832f46a017
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_pixel_matrix.h
@@ -0,0 +1,98 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PIXEL_MATRIX_H
+#define QWT_PIXEL_MATRIX_H
+
+#include "qwt/qwt_global.h"
+#include <qbitarray.h>
+#include <qrect.h>
+
+/*!
+  \brief A bit field corresponding to the pixels of a rectangle
+
+  QwtPixelMatrix is intended to filter out duplicates in an
+  unsorted array of points.
+*/
+class QWT_EXPORT QwtPixelMatrix: public QBitArray
+{
+public:
+    QwtPixelMatrix( const QRect& rect );
+    ~QwtPixelMatrix();
+
+    void setRect( const QRect& rect );
+    QRect rect() const;
+
+    bool testPixel( int x, int y ) const;
+    bool testAndSetPixel( int x, int y, bool on );
+
+    int index( int x, int y ) const;
+
+private:
+    QRect d_rect;
+};
+
+/*!
+  \brief Test if a pixel has been set
+
+  \param x X-coordinate
+  \param y Y-coordinate
+
+  \return true, when pos is outside of rect(), or when the pixel
+          has already been set.
+ */
+inline bool QwtPixelMatrix::testPixel( int x, int y ) const
+{
+    const int idx = index( x, y );
+    return ( idx >= 0 ) ? testBit( idx ) : true;
+}
+
+/*!
+  \brief Set a pixel and test if a pixel has been set before
+
+  \param x X-coordinate
+  \param y Y-coordinate
+  \param on Set/Clear the pixel
+
+  \return true, when pos is outside of rect(), or when the pixel
+          was set before.
+ */
+inline bool QwtPixelMatrix::testAndSetPixel( int x, int y, bool on )
+{
+    const int idx = index( x, y );
+    if ( idx < 0 )
+        return true;
+
+    const bool onBefore = testBit( idx );
+    setBit( idx, on );
+
+    return onBefore;
+}
+
+/*!
+  \brief Calculate the index in the bit field corresponding to a position
+
+  \param x X-coordinate
+  \param y Y-coordinate
+  \return Index, when rect() contains pos - otherwise -1.
+ */
+inline int QwtPixelMatrix::index( int x, int y ) const
+{
+    const int dx = x - d_rect.x();
+    if ( dx < 0 || dx >= d_rect.width() )
+        return -1;
+
+    const int dy = y - d_rect.y();
+    if ( dy < 0 || dy >= d_rect.height() )
+        return -1;
+
+    return dy * d_rect.width() + dx;
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot.h b/include/pli_vis/third_party/qwt/qwt_plot.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed5b58e3eefbbd606397f61fb2f93bdf7218c83a
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot.h
@@ -0,0 +1,312 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_H
+#define QWT_PLOT_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_plot_dict.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_interval.h"
+#include <qframe.h>
+#include <qlist.h>
+#include <qvariant.h>
+
+class QwtPlotLayout;
+class QwtAbstractLegend;
+class QwtScaleWidget;
+class QwtScaleEngine;
+class QwtScaleDiv;
+class QwtScaleDraw;
+class QwtTextLabel;
+
+/*!
+  \brief A 2-D plotting widget
+
+  QwtPlot is a widget for plotting two-dimensional graphs.
+  An unlimited number of plot items can be displayed on
+  its canvas. Plot items might be curves (QwtPlotCurve), markers
+  (QwtPlotMarker), the grid (QwtPlotGrid), or anything else derived
+  from QwtPlotItem.
+  A plot can have up to four axes, with each plot item attached to an x- and
+  a y axis. The scales at the axes can be explicitly set (QwtScaleDiv), or
+  are calculated from the plot items, using algorithms (QwtScaleEngine) which
+  can be configured separately for each axis.
+
+  The simpleplot example is a good starting point to see how to set up a 
+  plot widget.
+
+  \image html plot.png
+
+  \par Example
+  The following example shows (schematically) the most simple
+  way to use QwtPlot. By default, only the left and bottom axes are
+  visible and their scales are computed automatically.
+  \verbatim
+#include <qwt_plot.h>
+#include <qwt_plot_curve.h>
+
+QwtPlot *myPlot = new QwtPlot("Two Curves", parent);
+
+// add curves
+QwtPlotCurve *curve1 = new QwtPlotCurve("Curve 1");
+QwtPlotCurve *curve2 = new QwtPlotCurve("Curve 2");
+
+// connect or copy the data to the curves
+curve1->setData(...);
+curve2->setData(...);
+
+curve1->attach(myPlot);
+curve2->attach(myPlot);
+
+// finally, refresh the plot
+myPlot->replot();
+\endverbatim
+*/
+
+class QWT_EXPORT QwtPlot: public QFrame, public QwtPlotDict
+{
+    Q_OBJECT
+
+    Q_PROPERTY( QBrush canvasBackground 
+        READ canvasBackground WRITE setCanvasBackground )
+    Q_PROPERTY( bool autoReplot READ autoReplot WRITE setAutoReplot )
+
+#if 0
+    // This property is intended to configure the plot
+    // widget from a special dialog in the deigner plugin.
+    // Disabled until such a dialog has been implemented.
+
+    Q_PROPERTY( QString propertiesDocument
+        READ grabProperties WRITE applyProperties )
+#endif
+
+public:
+    //! \brief Axis index
+    enum Axis
+    {
+        //! Y axis left of the canvas
+        yLeft,
+
+        //! Y axis right of the canvas
+        yRight,
+
+        //! X axis below the canvas
+        xBottom,
+
+        //! X axis above the canvas
+        xTop,
+
+        //! Number of axes
+        axisCnt
+    };
+
+    /*!
+        Position of the legend, relative to the canvas.
+
+        \sa insertLegend()
+     */
+    enum LegendPosition
+    {
+        //! The legend will be left from the QwtPlot::yLeft axis.
+        LeftLegend,
+
+        //! The legend will be right from the QwtPlot::yRight axis.
+        RightLegend,
+
+        //! The legend will be below the footer 
+        BottomLegend,
+
+        //! The legend will be above the title
+        TopLegend
+    };
+
+    explicit QwtPlot( QWidget * = NULL );
+    explicit QwtPlot( const QwtText &title, QWidget * = NULL );
+
+    virtual ~QwtPlot();
+
+    void applyProperties( const QString & );
+    QString grabProperties() const;
+
+    void setAutoReplot( bool = true );
+    bool autoReplot() const;
+
+    // Layout
+
+    void setPlotLayout( QwtPlotLayout * );
+
+    QwtPlotLayout *plotLayout();
+    const QwtPlotLayout *plotLayout() const;
+
+    // Title
+
+    void setTitle( const QString & );
+    void setTitle( const QwtText &t );
+    QwtText title() const;
+
+    QwtTextLabel *titleLabel();
+    const QwtTextLabel *titleLabel() const;
+
+    // Footer
+
+    void setFooter( const QString & );
+    void setFooter( const QwtText &t );
+    QwtText footer() const;
+
+    QwtTextLabel *footerLabel();
+    const QwtTextLabel *footerLabel() const;
+
+    // Canvas
+
+    void setCanvas( QWidget * );
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+    void setCanvasBackground( const QBrush & );
+    QBrush canvasBackground() const;
+
+    virtual QwtScaleMap canvasMap( int axisId ) const;
+
+    double invTransform( int axisId, int pos ) const;
+    double transform( int axisId, double value ) const;
+
+    // Axes
+
+    QwtScaleEngine *axisScaleEngine( int axisId );
+    const QwtScaleEngine *axisScaleEngine( int axisId ) const;
+    void setAxisScaleEngine( int axisId, QwtScaleEngine * );
+
+    void setAxisAutoScale( int axisId, bool on = true );
+    bool axisAutoScale( int axisId ) const;
+
+    void enableAxis( int axisId, bool tf = true );
+    bool axisEnabled( int axisId ) const;
+
+    void setAxisFont( int axisId, const QFont &f );
+    QFont axisFont( int axisId ) const;
+
+    void setAxisScale( int axisId, double min, double max, double step = 0 );
+    void setAxisScaleDiv( int axisId, const QwtScaleDiv & );
+    void setAxisScaleDraw( int axisId, QwtScaleDraw * );
+
+    double axisStepSize( int axisId ) const;
+    QwtInterval axisInterval( int axisId ) const;
+
+    const QwtScaleDiv &axisScaleDiv( int axisId ) const;
+
+    const QwtScaleDraw *axisScaleDraw( int axisId ) const;
+    QwtScaleDraw *axisScaleDraw( int axisId );
+
+    const QwtScaleWidget *axisWidget( int axisId ) const;
+    QwtScaleWidget *axisWidget( int axisId );
+
+    void setAxisLabelAlignment( int axisId, Qt::Alignment );
+    void setAxisLabelRotation( int axisId, double rotation );
+
+    void setAxisTitle( int axisId, const QString & );
+    void setAxisTitle( int axisId, const QwtText & );
+    QwtText axisTitle( int axisId ) const;
+
+    void setAxisMaxMinor( int axisId, int maxMinor );
+    int axisMaxMinor( int axisId ) const;
+
+    void setAxisMaxMajor( int axisId, int maxMajor );
+    int axisMaxMajor( int axisId ) const;
+
+    // Legend
+
+    void insertLegend( QwtAbstractLegend *, 
+        LegendPosition = QwtPlot::RightLegend, double ratio = -1.0 );
+
+    QwtAbstractLegend *legend();
+    const QwtAbstractLegend *legend() const;
+
+    void updateLegend();
+    void updateLegend( const QwtPlotItem * );
+
+    // Misc
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    virtual void updateLayout();
+    virtual void drawCanvas( QPainter * );
+
+    void updateAxes();
+    void updateCanvasMargins();
+
+    virtual void getCanvasMarginsHint( 
+        const QwtScaleMap maps[], const QRectF &canvasRect,
+        double &left, double &top, double &right, double &bottom) const;
+
+    virtual bool event( QEvent * );
+    virtual bool eventFilter( QObject *, QEvent * );
+
+    virtual void drawItems( QPainter *, const QRectF &,
+        const QwtScaleMap maps[axisCnt] ) const;
+
+    virtual QVariant itemToInfo( QwtPlotItem * ) const;
+    virtual QwtPlotItem *infoToItem( const QVariant & ) const;
+
+Q_SIGNALS:
+    /*!
+      A signal indicating, that an item has been attached/detached
+
+      \param plotItem Plot item
+      \param on Attached/Detached
+     */
+    void itemAttached( QwtPlotItem *plotItem, bool on );
+
+    /*!
+      A signal with the attributes how to update 
+      the legend entries for a plot item.
+
+      \param itemInfo Info about a plot item, build from itemToInfo()
+      \param data Attributes of the entries ( usually <= 1 ) for
+                  the plot item.
+
+      \sa itemToInfo(), infoToItem(), QwtAbstractLegend::updateLegend()
+     */
+    void legendDataChanged( const QVariant &itemInfo, 
+        const QList<QwtLegendData> &data );
+
+public Q_SLOTS:
+    virtual void replot();
+    void autoRefresh();
+
+protected:
+    static bool axisValid( int axisId );
+
+    virtual void resizeEvent( QResizeEvent *e );
+
+private Q_SLOTS:
+    void updateLegendItems( const QVariant &itemInfo,
+        const QList<QwtLegendData> &data );
+
+private:
+    friend class QwtPlotItem;
+    void attachItem( QwtPlotItem *, bool );
+
+    void initAxesData();
+    void deleteAxesData();
+    void updateScaleDiv();
+
+    void initPlot( const QwtText &title );
+
+    class AxisData;
+    AxisData *d_axisData[axisCnt];
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_abstract_barchart.h b/include/pli_vis/third_party/qwt/qwt_plot_abstract_barchart.h
new file mode 100644
index 0000000000000000000000000000000000000000..e364097a3b96b49e65f50d9136865e18230ef2b5
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_abstract_barchart.h
@@ -0,0 +1,97 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_ABSTRACT_BAR_CHART_H
+#define QWT_PLOT_ABSTRACT_BAR_CHART_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_seriesitem.h"
+#include "qwt/qwt_series_data.h"
+
+/*!
+  \brief Abstract base class for bar chart items
+
+  In opposite to almost all other plot items bar charts can't be 
+  displayed inside of their bounding rectangle and need a special
+  API  how to calculate the width of the bars and how they affect
+  the layout of the attached plot.
+ */
+class QWT_EXPORT QwtPlotAbstractBarChart: public QwtPlotSeriesItem
+{
+public:
+    /*!
+        \brief Mode how to calculate the bar width
+
+        setLayoutPolicy(), setLayoutHint(), barWidthHint()
+     */
+    enum LayoutPolicy
+    {
+        /*!
+          The sample width is calculated by dividing the bounding rectangle
+          by the number of samples. The layoutHint() is used as a minimum width
+          in paint device coordinates.
+
+          \sa boundingRectangle()
+         */
+        AutoAdjustSamples,
+
+        /*!
+          layoutHint() defines an interval in axis coordinates
+         */
+        ScaleSamplesToAxes,
+
+        /*!
+          The bar width is calculated by multiplying layoutHint()
+          with the height or width of the canvas.
+
+          \sa boundingRectangle()
+         */
+        ScaleSampleToCanvas,
+
+        /*!
+          layoutHint() defines a fixed width in paint device coordinates.
+         */
+        FixedSampleSize
+    };
+
+    explicit QwtPlotAbstractBarChart( const QwtText &title );
+    virtual ~QwtPlotAbstractBarChart();
+
+    void setLayoutPolicy( LayoutPolicy );
+    LayoutPolicy layoutPolicy() const;
+
+    void setLayoutHint( double );
+    double layoutHint() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    void setMargin( int );
+    int margin() const;
+
+    void setBaseline( double );
+    double baseline() const;
+
+    virtual void getCanvasMarginHint( 
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect,
+        double &left, double &top, double &right, double &bottom) const;
+
+
+protected:
+    double sampleWidth( const QwtScaleMap &map,
+        double canvasSize, double dataSize,
+        double value ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_barchart.h b/include/pli_vis/third_party/qwt/qwt_plot_barchart.h
new file mode 100644
index 0000000000000000000000000000000000000000..d6894c674205ca69a938451b72996df12a595596
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_barchart.h
@@ -0,0 +1,118 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_BAR_CHART_H
+#define QWT_PLOT_BAR_CHART_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_abstract_barchart.h"
+#include "qwt/qwt_series_data.h"
+
+class QwtColumnRect;
+class QwtColumnSymbol;
+
+/*!
+  \brief QwtPlotBarChart displays a series of a values as bars.
+
+  Each bar might be customized individually by implementing
+  a specialSymbol(). Otherwise it is rendered using a default symbol.
+
+  Depending on its orientation() the bars are displayed horizontally 
+  or vertically. The bars cover the interval between the baseline() 
+  and the value.
+
+  By activating the LegendBarTitles mode each sample will have
+  its own entry on the legend.
+
+  The most common use case of a bar chart is to display a
+  list of y coordinates, where the x coordinate is simply the index
+  in the list. But for other situations ( f.e. when values are related
+  to dates ) it is also possible to set x coordinates explicitly.
+
+  \sa QwtPlotMultiBarChart, QwtPlotHistogram, QwtPlotCurve::Sticks,
+      QwtPlotSeriesItem::orientation(), QwtPlotAbstractBarChart::baseline()
+ */
+class QWT_EXPORT QwtPlotBarChart:
+    public QwtPlotAbstractBarChart, public QwtSeriesStore<QPointF>
+{
+public:
+    /*!
+      \brief Legend modes.
+
+      The default setting is QwtPlotBarChart::LegendChartTitle.
+      \sa setLegendMode(), legendMode()
+    */
+    enum LegendMode
+    {
+        /*! 
+          One entry on the legend showing the default symbol
+          and the title() of the chart
+
+          \sa QwtPlotItem::title()
+         */
+        LegendChartTitle,
+
+        /*!
+          One entry for each value showing the individual symbol
+          of the corresponding bar and the bar title.
+
+          \sa specialSymbol(), barTitle()
+         */
+        LegendBarTitles
+    };
+
+    explicit QwtPlotBarChart( const QString &title = QString::null );
+    explicit QwtPlotBarChart( const QwtText &title );
+
+    virtual ~QwtPlotBarChart();
+
+    virtual int rtti() const;
+
+    void setSamples( const QVector<QPointF> & );
+    void setSamples( const QVector<double> & );
+    void setSamples( QwtSeriesData<QPointF> *series );
+
+    void setSymbol( QwtColumnSymbol * );
+    const QwtColumnSymbol *symbol() const;
+
+    void setLegendMode( LegendMode );
+    LegendMode legendMode() const;
+
+    virtual void drawSeries( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtColumnSymbol *specialSymbol( 
+        int sampleIndex, const QPointF& ) const;
+
+    virtual QwtText barTitle( int sampleIndex ) const;
+
+protected:
+    virtual void drawSample( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, const QwtInterval &boundingInterval,
+        int index, const QPointF& sample ) const;
+
+    virtual void drawBar( QPainter *,
+        int sampleIndex, const QPointF& point, 
+        const QwtColumnRect & ) const;
+
+    QList<QwtLegendData> legendData() const;
+    QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_canvas.h b/include/pli_vis/third_party/qwt/qwt_plot_canvas.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f91d8113e3e7d7f1b52282c0eba491331763d8f
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_canvas.h
@@ -0,0 +1,171 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_CANVAS_H
+#define QWT_PLOT_CANVAS_H
+
+#include "qwt/qwt_global.h"
+#include <qframe.h>
+#include <qpainterpath.h>
+
+class QwtPlot;
+class QPixmap;
+
+/*!
+  \brief Canvas of a QwtPlot.
+  
+   Canvas is the widget where all plot items are displayed
+
+  \sa QwtPlot::setCanvas(), QwtPlotGLCanvas
+*/
+class QWT_EXPORT QwtPlotCanvas : public QFrame
+{
+    Q_OBJECT
+
+    Q_PROPERTY( double borderRadius READ borderRadius WRITE setBorderRadius )
+
+public:
+
+    /*!
+      \brief Paint attributes
+
+      The default setting enables BackingStore and Opaque.
+
+      \sa setPaintAttribute(), testPaintAttribute()
+     */
+    enum PaintAttribute
+    {
+        /*!
+          \brief Paint double buffered reusing the content 
+                 of the pixmap buffer when possible. 
+
+          Using a backing store might improve the performance
+          significantly, when working with widget overlays ( like rubber bands ).
+          Disabling the cache might improve the performance for
+          incremental paints (using QwtPlotDirectPainter ).
+
+          \sa backingStore(), invalidateBackingStore()
+         */
+        BackingStore = 1,
+
+        /*!
+          \brief Try to fill the complete contents rectangle
+                 of the plot canvas
+
+          When using styled backgrounds Qt assumes, that the
+          canvas doesn't fill its area completely 
+          ( f.e because of rounded borders ) and fills the area
+          below the canvas. When this is done with gradients it might
+          result in a serious performance bottleneck - depending on the size.
+
+          When the Opaque attribute is enabled the canvas tries to
+          identify the gaps with some heuristics and to fill those only. 
+
+          \warning Will not work for semitransparent backgrounds 
+         */
+        Opaque       = 2,
+
+        /*!
+          \brief Try to improve painting of styled backgrounds
+
+          QwtPlotCanvas supports the box model attributes for
+          customizing the layout with style sheets. Unfortunately
+          the design of Qt style sheets has no concept how to
+          handle backgrounds with rounded corners - beside of padding.
+
+          When HackStyledBackground is enabled the plot canvas tries
+          to separate the background from the background border
+          by reverse engineering to paint the background before and
+          the border after the plot items. In this order the border
+          gets perfectly antialiased and you can avoid some pixel
+          artifacts in the corners.
+         */
+        HackStyledBackground = 4,
+
+        /*!
+          When ImmediatePaint is set replot() calls repaint()
+          instead of update().
+
+          \sa replot(), QWidget::repaint(), QWidget::update()
+         */
+        ImmediatePaint = 8
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    /*!
+      \brief Focus indicator
+      The default setting is NoFocusIndicator
+      \sa setFocusIndicator(), focusIndicator(), drawFocusIndicator()
+    */
+
+    enum FocusIndicator
+    {
+        //! Don't paint a focus indicator
+        NoFocusIndicator,
+
+        /*!
+          The focus is related to the complete canvas.
+          Paint the focus indicator using drawFocusIndicator()
+         */
+        CanvasFocusIndicator,
+
+        /*!
+          The focus is related to an item (curve, point, ...) on
+          the canvas. It is up to the application to display a
+          focus indication using f.e. highlighting.
+         */
+        ItemFocusIndicator
+    };
+
+    explicit QwtPlotCanvas( QwtPlot * = NULL );
+    virtual ~QwtPlotCanvas();
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+    void setFocusIndicator( FocusIndicator );
+    FocusIndicator focusIndicator() const;
+
+    void setBorderRadius( double );
+    double borderRadius() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    const QPixmap *backingStore() const;
+    void invalidateBackingStore();
+
+    virtual bool event( QEvent * );
+
+    Q_INVOKABLE QPainterPath borderPath( const QRect & ) const;
+
+public Q_SLOTS:
+    void replot();
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+    virtual void resizeEvent( QResizeEvent * );
+
+    virtual void drawFocusIndicator( QPainter * );
+    virtual void drawBorder( QPainter * );
+
+    void updateStyleSheetInfo();
+
+private:
+    void drawCanvas( QPainter *, bool withBackground );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCanvas::PaintAttributes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_curve.h b/include/pli_vis/third_party/qwt/qwt_plot_curve.h
new file mode 100644
index 0000000000000000000000000000000000000000..79ac6818ceda4560e0412e63ebc4fe9fc5c2aa89
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_curve.h
@@ -0,0 +1,337 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_CURVE_H
+#define QWT_PLOT_CURVE_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_seriesitem.h"
+#include "qwt/qwt_series_data.h"
+#include "qwt/qwt_text.h"
+#include <qpen.h>
+#include <qstring.h>
+
+class QPainter;
+class QPolygonF;
+class QwtScaleMap;
+class QwtSymbol;
+class QwtCurveFitter;
+
+/*!
+  \brief A plot item, that represents a series of points
+
+  A curve is the representation of a series of points in the x-y plane.
+  It supports different display styles, interpolation ( f.e. spline )
+  and symbols.
+
+  \par Usage
+  <dl><dt>a) Assign curve properties</dt>
+  <dd>When a curve is created, it is configured to draw black solid lines
+  with in QwtPlotCurve::Lines style and no symbols. 
+  You can change this by calling
+  setPen(), setStyle() and setSymbol().</dd>
+  <dt>b) Connect/Assign data.</dt>
+  <dd>QwtPlotCurve gets its points using a QwtSeriesData object offering
+  a bridge to the real storage of the points ( like QAbstractItemModel ).
+  There are several convenience classes derived from QwtSeriesData, that also store
+  the points inside ( like QStandardItemModel ). QwtPlotCurve also offers
+  a couple of variations of setSamples(), that build QwtSeriesData objects from
+  arrays internally.</dd>
+  <dt>c) Attach the curve to a plot</dt>
+  <dd>See QwtPlotItem::attach()
+  </dd></dl>
+
+  \par Example:
+  see examples/bode
+
+  \sa QwtPointSeriesData, QwtSymbol, QwtScaleMap
+*/
+class QWT_EXPORT QwtPlotCurve: 
+    public QwtPlotSeriesItem, public QwtSeriesStore<QPointF>
+{
+public:
+    /*!
+        Curve styles.
+        \sa setStyle(), style()
+    */
+    enum CurveStyle
+    {
+        /*!
+           Don't draw a curve. Note: This doesn't affect the symbols.
+        */
+        NoCurve = -1,
+
+        /*!
+           Connect the points with straight lines. The lines might
+           be interpolated depending on the 'Fitted' attribute. Curve
+           fitting can be configured using setCurveFitter().
+        */
+        Lines,
+
+        /*!
+           Draw vertical or horizontal sticks ( depending on the 
+           orientation() ) from a baseline which is defined by setBaseline().
+        */
+        Sticks,
+
+        /*!
+           Connect the points with a step function. The step function
+           is drawn from the left to the right or vice versa,
+           depending on the QwtPlotCurve::Inverted attribute.
+        */
+        Steps,
+
+        /*!
+           Draw dots at the locations of the data points. Note:
+           This is different from a dotted line (see setPen()), and faster
+           as a curve in QwtPlotCurve::NoStyle style and a symbol 
+           painting a point.
+        */
+        Dots,
+
+        /*!
+           Styles >= QwtPlotCurve::UserCurve are reserved for derived
+           classes of QwtPlotCurve that overload drawCurve() with
+           additional application specific curve types.
+        */
+        UserCurve = 100
+    };
+
+    /*!
+      Attribute for drawing the curve
+      \sa setCurveAttribute(), testCurveAttribute(), curveFitter()
+    */
+    enum CurveAttribute
+    {
+        /*!
+           For QwtPlotCurve::Steps only. 
+           Draws a step function from the right to the left.
+         */
+        Inverted = 0x01,
+
+        /*!
+          Only in combination with QwtPlotCurve::Lines
+          A QwtCurveFitter tries to
+          interpolate/smooth the curve, before it is painted.
+
+          \note Curve fitting requires temporary memory
+          for calculating coefficients and additional points.
+          If painting in QwtPlotCurve::Fitted mode is slow it might be better
+          to fit the points, before they are passed to QwtPlotCurve.
+         */
+        Fitted = 0x02
+    };
+
+    //! Curve attributes
+    typedef QFlags<CurveAttribute> CurveAttributes;
+
+    /*!
+        Attributes how to represent the curve on the legend
+
+        \sa setLegendAttribute(), testLegendAttribute(),
+            QwtPlotItem::legendData(), legendIcon()
+     */
+
+    enum LegendAttribute
+    {
+        /*!
+          QwtPlotCurve tries to find a color representing the curve 
+          and paints a rectangle with it.
+         */
+        LegendNoAttribute = 0x00,
+
+        /*!
+          If the style() is not QwtPlotCurve::NoCurve a line 
+          is painted with the curve pen().
+         */
+        LegendShowLine = 0x01,
+
+        /*!
+          If the curve has a valid symbol it is painted.
+         */
+        LegendShowSymbol = 0x02,
+
+        /*!
+          If the curve has a brush a rectangle filled with the
+          curve brush() is painted.
+         */
+        LegendShowBrush = 0x04
+    };
+
+    //! Legend attributes
+    typedef QFlags<LegendAttribute> LegendAttributes;
+
+    /*!
+        Attributes to modify the drawing algorithm.
+        The default setting enables ClipPolygons | FilterPoints
+
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        /*!
+          Clip polygons before painting them. In situations, where points
+          are far outside the visible area (f.e when zooming deep) this
+          might be a substantial improvement for the painting performance
+         */
+        ClipPolygons = 0x01,
+
+        /*!
+          Tries to reduce the data that has to be painted, by sorting out
+          duplicates, or paintings outside the visible area. Might have a
+          notable impact on curves with many close points.
+          Only a couple of very basic filtering algorithms are implemented.
+         */
+        FilterPoints = 0x02,
+
+        /*!
+          Minimize memory usage that is temporarily needed for the 
+          translated points, before they get painted.
+          This might slow down the performance of painting 
+         */
+        MinimizeMemory = 0x04,
+
+        /*!
+          Render the points to a temporary image and paint the image.
+          This is a very special optimization for Dots style, when
+          having a huge amount of points. 
+          With a reasonable number of points QPainter::drawPoints()
+          will be faster.
+         */
+        ImageBuffer = 0x08
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotCurve( const QString &title = QString::null );
+    explicit QwtPlotCurve( const QwtText &title );
+
+    virtual ~QwtPlotCurve();
+
+    virtual int rtti() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setLegendAttribute( LegendAttribute, bool on = true );
+    bool testLegendAttribute( LegendAttribute ) const;
+
+#ifndef QWT_NO_COMPAT
+    void setRawSamples( const double *xData, const double *yData, int size );
+    void setSamples( const double *xData, const double *yData, int size );
+    void setSamples( const QVector<double> &xData, const QVector<double> &yData );
+#endif
+    void setSamples( const QVector<QPointF> & );
+    void setSamples( QwtSeriesData<QPointF> * );
+
+    int closestPoint( const QPoint &pos, double *dist = NULL ) const;
+
+    double minXValue() const;
+    double maxXValue() const;
+    double minYValue() const;
+    double maxYValue() const;
+
+    void setCurveAttribute( CurveAttribute, bool on = true );
+    bool testCurveAttribute( CurveAttribute ) const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen &pen() const;
+
+    void setBrush( const QBrush & );
+    const QBrush &brush() const;
+
+    void setBaseline( double );
+    double baseline() const;
+
+    void setStyle( CurveStyle style );
+    CurveStyle style() const;
+
+    void setSymbol( QwtSymbol * );
+    const QwtSymbol *symbol() const;
+
+    void setCurveFitter( QwtCurveFitter * );
+    QwtCurveFitter *curveFitter() const;
+
+    virtual void drawSeries( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+
+    void init();
+
+    virtual void drawCurve( QPainter *p, int style,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawSymbols( QPainter *p, const QwtSymbol &,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawLines( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawSticks( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawDots( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawSteps( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void fillCurve( QPainter *,
+        const QwtScaleMap &, const QwtScaleMap &, 
+        const QRectF &canvasRect, QPolygonF & ) const;
+
+    void closePolyline( QPainter *,
+        const QwtScaleMap &, const QwtScaleMap &, QPolygonF & ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+//! boundingRect().left()
+inline double QwtPlotCurve::minXValue() const
+{
+    return boundingRect().left();
+}
+
+//! boundingRect().right()
+inline double QwtPlotCurve::maxXValue() const
+{
+    return boundingRect().right();
+}
+
+//! boundingRect().top()
+inline double QwtPlotCurve::minYValue() const
+{
+    return boundingRect().top();
+}
+
+//! boundingRect().bottom()
+inline double QwtPlotCurve::maxYValue() const
+{
+    return boundingRect().bottom();
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::PaintAttributes )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::LegendAttributes )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::CurveAttributes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_dict.h b/include/pli_vis/third_party/qwt/qwt_plot_dict.h
new file mode 100644
index 0000000000000000000000000000000000000000..e370907a770c2cb17af705e26a75be963e4959a3
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_dict.h
@@ -0,0 +1,58 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+/*! \file !*/
+#ifndef QWT_PLOT_DICT
+#define QWT_PLOT_DICT
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include <qlist.h>
+
+/// \var typedef QList< QwtPlotItem *> QwtPlotItemList
+/// \brief See QT 4.x assistant documentation for QList
+typedef QList<QwtPlotItem *> QwtPlotItemList;
+typedef QList<QwtPlotItem *>::ConstIterator QwtPlotItemIterator;
+
+/*!
+  \brief A dictionary for plot items
+
+  QwtPlotDict organizes plot items in increasing z-order.
+  If autoDelete() is enabled, all attached items will be deleted
+  in the destructor of the dictionary.
+  QwtPlotDict can be used to get access to all QwtPlotItem items - or all
+  items of a specific type -  that are currently on the plot.
+
+  \sa QwtPlotItem::attach(), QwtPlotItem::detach(), QwtPlotItem::z()
+*/
+class QWT_EXPORT QwtPlotDict
+{
+public:
+    explicit QwtPlotDict();
+    virtual ~QwtPlotDict();
+
+    void setAutoDelete( bool );
+    bool autoDelete() const;
+
+    const QwtPlotItemList& itemList() const;
+    QwtPlotItemList itemList( int rtti ) const;
+
+    void detachItems( int rtti = QwtPlotItem::Rtti_PlotItem,
+        bool autoDelete = true );
+
+protected:
+    void insertItem( QwtPlotItem * );
+    void removeItem( QwtPlotItem * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_directpainter.h b/include/pli_vis/third_party/qwt/qwt_plot_directpainter.h
new file mode 100644
index 0000000000000000000000000000000000000000..e0510b1b3e680223ee2a7aea232088ff493a853b
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_directpainter.h
@@ -0,0 +1,100 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_DIRECT_PAINTER_H
+#define QWT_PLOT_DIRECT_PAINTER_H
+
+#include "qwt/qwt_global.h"
+#include <qobject.h>
+
+class QRegion;
+class QwtPlotSeriesItem;
+
+/*!
+    \brief Painter object trying to paint incrementally
+
+    Often applications want to display samples while they are
+    collected. When there are too many samples complete replots
+    will be expensive to be processed in a collection cycle.
+
+    QwtPlotDirectPainter offers an API to paint
+    subsets ( f.e all additions points ) without erasing/repainting
+    the plot canvas.
+
+    On certain environments it might be important to calculate a proper
+    clip region before painting. F.e. for Qt Embedded only the clipped part
+    of the backing store will be copied to a ( maybe unaccelerated ) 
+    frame buffer.
+
+    \warning Incremental painting will only help when no replot is triggered
+             by another operation ( like changing scales ) and nothing needs
+             to be erased.
+*/
+class QWT_EXPORT QwtPlotDirectPainter: public QObject
+{
+public:
+    /*!
+      \brief Paint attributes
+      \sa setAttribute(), testAttribute(), drawSeries()
+    */
+    enum Attribute
+    {
+        /*!
+          Initializing a QPainter is an expensive operation.
+          When AtomicPainter is set each call of drawSeries() opens/closes
+          a temporary QPainter. Otherwise QwtPlotDirectPainter tries to
+          use the same QPainter as long as possible.
+         */
+        AtomicPainter = 0x01,
+
+        /*!
+          When FullRepaint is set the plot canvas is explicitly repainted
+          after the samples have been rendered.
+         */
+        FullRepaint = 0x02,
+
+        /*!
+          When QwtPlotCanvas::BackingStore is enabled the painter
+          has to paint to the backing store and the widget. In certain 
+          situations/environments it might be faster to paint to 
+          the backing store only and then copy the backing store to the canvas.
+          This flag can also be useful for settings, where Qt fills the
+          the clip region with the widget background.
+         */
+        CopyBackingStore = 0x04
+    };
+
+    //! Paint attributes
+    typedef QFlags<Attribute> Attributes;
+
+    QwtPlotDirectPainter( QObject *parent = NULL );
+    virtual ~QwtPlotDirectPainter();
+
+    void setAttribute( Attribute, bool on );
+    bool testAttribute( Attribute ) const;
+
+    void setClipping( bool );
+    bool hasClipping() const;
+
+    void setClipRegion( const QRegion & );
+    QRegion clipRegion() const;
+
+    void drawSeries( QwtPlotSeriesItem *, int from, int to );
+    void reset();
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotDirectPainter::Attributes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_glcanvas.h b/include/pli_vis/third_party/qwt/qwt_plot_glcanvas.h
new file mode 100644
index 0000000000000000000000000000000000000000..fd597313303c7388d3ac0ea3ce2a49e683a54611
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_glcanvas.h
@@ -0,0 +1,131 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_GLCANVAS_H
+#define QWT_PLOT_GLCANVAS_H
+
+#include "qwt/qwt_global.h"
+#include <qframe.h>
+#include <qopengl.h>
+#include <qopenglwidget.h>
+
+class QwtPlot;
+
+/*!
+  \brief An alternative canvas for a QwtPlot derived from QGLWidget
+  
+  QwtPlotGLCanvas implements the very basics to act as canvas
+  inside of a QwtPlot widget. It might be extended to a full
+  featured alternative to QwtPlotCanvas in a future version of Qwt.
+
+  Even if QwtPlotGLCanvas is not derived from QFrame it imitates
+  its API. When using style sheets it supports the box model - beside
+  backgrounds with rounded borders.
+
+  \sa QwtPlot::setCanvas(), QwtPlotCanvas
+
+  \note With Qt4 you might want to use the QPaintEngine::OpenGL paint engine
+        ( see QGL::setPreferredPaintEngine() ). On a Linux test system 
+        QPaintEngine::OpenGL2 shows very basic problems like translated
+        geometries.
+*/
+class QWT_EXPORT QwtPlotGLCanvas: public QOpenGLWidget
+{
+    Q_OBJECT
+
+    Q_ENUMS( Shape Shadow )
+
+    Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow )
+    Q_PROPERTY( Shape frameShape READ frameShape WRITE setFrameShape )
+    Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth )
+    Q_PROPERTY( int midLineWidth READ midLineWidth WRITE setMidLineWidth )
+    Q_PROPERTY( int frameWidth READ frameWidth )
+    Q_PROPERTY( QRect frameRect READ frameRect DESIGNABLE false )
+
+public:
+    /*!
+        \brief Frame shadow
+
+         Unfortunately it is not possible to use QFrame::Shadow
+         as a property of a widget that is not derived from QFrame.
+         The following enum is made for the designer only. It is safe
+         to use QFrame::Shadow instead.
+     */
+    enum Shadow
+    {
+        //! QFrame::Plain
+        Plain = QFrame::Plain,
+
+        //! QFrame::Raised
+        Raised = QFrame::Raised,
+
+        //! QFrame::Sunken
+        Sunken = QFrame::Sunken
+    };
+
+    /*!
+        \brief Frame shape
+
+        Unfortunately it is not possible to use QFrame::Shape
+        as a property of a widget that is not derived from QFrame.
+        The following enum is made for the designer only. It is safe
+        to use QFrame::Shadow instead.
+
+        \note QFrame::StyledPanel and QFrame::WinPanel are unsuported 
+              and will be displayed as QFrame::Panel.
+     */
+    enum Shape
+    {
+        NoFrame = QFrame::NoFrame,
+
+        Box = QFrame::Box,
+        Panel = QFrame::Panel
+    };
+
+    explicit QwtPlotGLCanvas( QwtPlot * = NULL );
+    virtual ~QwtPlotGLCanvas();
+
+    void setFrameStyle( int style );
+    int frameStyle() const;
+
+    void setFrameShadow( Shadow );
+    Shadow frameShadow() const;
+
+    void setFrameShape( Shape );
+    Shape frameShape() const;
+
+    void setLineWidth( int );
+    int lineWidth() const;
+
+    void setMidLineWidth( int );
+    int midLineWidth() const;
+
+    int frameWidth() const;
+    QRect frameRect() const;
+
+    Q_INVOKABLE QPainterPath borderPath( const QRect & ) const;
+
+    virtual bool event( QEvent * );
+
+public Q_SLOTS:
+    void replot();
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+
+    virtual void drawBackground( QPainter * );
+    virtual void drawBorder( QPainter * );
+    virtual void drawItems( QPainter * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_grid.h b/include/pli_vis/third_party/qwt/qwt_plot_grid.h
new file mode 100644
index 0000000000000000000000000000000000000000..d6d5da025e9753cabb3c54bf8bb7fbb83f7cd61d
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_grid.h
@@ -0,0 +1,87 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_GRID_H
+#define QWT_PLOT_GRID_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_scale_div.h"
+
+class QPainter;
+class QPen;
+class QwtScaleMap;
+class QwtScaleDiv;
+
+/*!
+  \brief A class which draws a coordinate grid
+
+  The QwtPlotGrid class can be used to draw a coordinate grid.
+  A coordinate grid consists of major and minor vertical
+  and horizontal grid lines. The locations of the grid lines
+  are determined by the X and Y scale divisions which can
+  be assigned with setXDiv() and setYDiv().
+  The draw() member draws the grid within a bounding
+  rectangle.
+*/
+
+class QWT_EXPORT QwtPlotGrid: public QwtPlotItem
+{
+public:
+    explicit QwtPlotGrid();
+    virtual ~QwtPlotGrid();
+
+    virtual int rtti() const;
+
+    void enableX( bool tf );
+    bool xEnabled() const;
+
+    void enableY( bool tf );
+    bool yEnabled() const;
+
+    void enableXMin( bool tf );
+    bool xMinEnabled() const;
+
+    void enableYMin( bool tf );
+    bool yMinEnabled() const;
+
+    void setXDiv( const QwtScaleDiv &sx );
+    const QwtScaleDiv &xScaleDiv() const;
+
+    void setYDiv( const QwtScaleDiv &sy );
+    const QwtScaleDiv &yScaleDiv() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+
+    void setMajorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setMajorPen( const QPen & );
+    const QPen& majorPen() const;
+
+    void setMinorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setMinorPen( const QPen &p );
+    const QPen& minorPen() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual void updateScaleDiv( 
+        const QwtScaleDiv &xMap, const QwtScaleDiv &yMap );
+
+private:
+    void drawLines( QPainter *painter, const QRectF &,
+        Qt::Orientation orientation, const QwtScaleMap &,
+        const QList<double> & ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_histogram.h b/include/pli_vis/third_party/qwt/qwt_plot_histogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..0cdb0c0c1d01968a24b1c04b3730ef606298e625
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_histogram.h
@@ -0,0 +1,139 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_HISTOGRAM_H
+#define QWT_PLOT_HISTOGRAM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_seriesitem.h"
+#include "qwt/qwt_column_symbol.h"
+#include <qcolor.h>
+#include <qvector.h>
+
+class QwtIntervalData;
+class QString;
+class QPolygonF;
+
+/*!
+  \brief QwtPlotHistogram represents a series of samples, where an interval
+         is associated with a value ( \f$y = f([x1,x2])\f$ ).
+
+  The representation depends on the style() and an optional symbol()
+  that is displayed for each interval.
+
+  \note The term "histogram" is used in a different way in the areas of
+        digital image processing and statistics. Wikipedia introduces the
+        terms "image histogram" and "color histogram" to avoid confusions.
+        While "image histograms" can be displayed by a QwtPlotCurve there
+        is no applicable plot item for a "color histogram" yet.
+
+  \sa QwtPlotBarChart, QwtPlotMultiBarChart
+*/
+
+class QWT_EXPORT QwtPlotHistogram: 
+    public QwtPlotSeriesItem, public QwtSeriesStore<QwtIntervalSample>
+{
+public:
+    /*!
+        Histogram styles.
+        The default style is QwtPlotHistogram::Columns.
+
+        \sa setStyle(), style(), setSymbol(), symbol(), setBaseline()
+    */
+    enum HistogramStyle
+    {
+        /*!
+           Draw an outline around the area, that is build by all intervals
+           using the pen() and fill it with the brush(). The outline style
+           requires, that the intervals are in increasing order and
+           not overlapping.
+         */
+        Outline,
+
+        /*!
+           Draw a column for each interval. When a symbol() has been set
+           the symbol is used otherwise the column is displayed as 
+           plain rectangle using pen() and brush().
+         */
+        Columns,
+
+        /*!
+           Draw a simple line using the pen() for each interval.
+         */
+        Lines,
+
+        /*!
+           Styles >= UserStyle are reserved for derived
+           classes that overload drawSeries() with
+           additional application specific ways to display a histogram.
+         */
+        UserStyle = 100
+    };
+
+    explicit QwtPlotHistogram( const QString &title = QString::null );
+    explicit QwtPlotHistogram( const QwtText &title );
+    virtual ~QwtPlotHistogram();
+
+    virtual int rtti() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen &pen() const;
+
+    void setBrush( const QBrush & );
+    const QBrush &brush() const;
+
+    void setSamples( const QVector<QwtIntervalSample> & );
+    void setSamples( QwtSeriesData<QwtIntervalSample> * );
+
+    void setBaseline( double reference );
+    double baseline() const;
+
+    void setStyle( HistogramStyle style );
+    HistogramStyle style() const;
+
+    void setSymbol( const QwtColumnSymbol * );
+    const QwtColumnSymbol *symbol() const;
+
+    virtual void drawSeries( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+    virtual QwtColumnRect columnRect( const QwtIntervalSample &,
+        const QwtScaleMap &, const QwtScaleMap & ) const;
+
+    virtual void drawColumn( QPainter *, const QwtColumnRect &,
+        const QwtIntervalSample & ) const;
+
+    void drawColumns( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        int from, int to ) const;
+
+    void drawOutline( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        int from, int to ) const;
+
+    void drawLines( QPainter *,
+         const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+         int from, int to ) const;
+
+private:
+    void init();
+    void flushPolygon( QPainter *, double baseLine, QPolygonF & ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_intervalcurve.h b/include/pli_vis/third_party/qwt/qwt_plot_intervalcurve.h
new file mode 100644
index 0000000000000000000000000000000000000000..81dcadf011962cc33f18f0cfc1cf396b2569a1ae
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_intervalcurve.h
@@ -0,0 +1,132 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_INTERVAL_CURVE_H
+#define QWT_PLOT_INTERVAL_CURVE_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_seriesitem.h"
+#include "qwt/qwt_series_data.h"
+
+class QwtIntervalSymbol;
+
+/*!
+  \brief QwtPlotIntervalCurve represents a series of samples, where each value
+         is associated with an interval ( \f$[y1,y2] = f(x)\f$ ).
+
+  The representation depends on the style() and an optional symbol()
+  that is displayed for each interval. QwtPlotIntervalCurve might be used
+  to display error bars or the area between 2 curves.
+*/
+class QWT_EXPORT QwtPlotIntervalCurve: 
+    public QwtPlotSeriesItem, public QwtSeriesStore<QwtIntervalSample>
+{
+public:
+    /*!
+        \brief Curve styles.
+        The default setting is QwtPlotIntervalCurve::Tube.
+
+        \sa setStyle(), style()
+    */
+    enum CurveStyle
+    {
+        /*!
+           Don't draw a curve. Note: This doesn't affect the symbols.
+         */
+        NoCurve,
+
+        /*!
+           Build 2 curves from the upper and lower limits of the intervals
+           and draw them with the pen(). The area between the curves is
+           filled with the brush().
+         */
+        Tube,
+
+        /*!
+           Styles >= QwtPlotIntervalCurve::UserCurve are reserved for derived
+           classes that overload drawSeries() with
+           additional application specific curve types.
+         */
+        UserCurve = 100
+    };
+
+    /*!
+        Attributes to modify the drawing algorithm.
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        /*!
+          Clip polygons before painting them. In situations, where points
+          are far outside the visible area (f.e when zooming deep) this
+          might be a substantial improvement for the painting performance.
+         */
+        ClipPolygons = 0x01,
+
+        //! Check if a symbol is on the plot canvas before painting it.
+        ClipSymbol   = 0x02
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotIntervalCurve( const QString &title = QString::null );
+    explicit QwtPlotIntervalCurve( const QwtText &title );
+
+    virtual ~QwtPlotIntervalCurve();
+
+    virtual int rtti() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setSamples( const QVector<QwtIntervalSample> & );
+    void setSamples( QwtSeriesData<QwtIntervalSample> * );
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen &pen() const;
+
+    void setBrush( const QBrush & );
+    const QBrush &brush() const;
+
+    void setStyle( CurveStyle style );
+    CurveStyle style() const;
+
+    void setSymbol( const QwtIntervalSymbol * );
+    const QwtIntervalSymbol *symbol() const;
+
+    virtual void drawSeries( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+
+    void init();
+
+    virtual void drawTube( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawSymbols( QPainter *, const QwtIntervalSymbol &,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotIntervalCurve::PaintAttributes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_item.h b/include/pli_vis/third_party/qwt/qwt_plot_item.h
new file mode 100644
index 0000000000000000000000000000000000000000..2cbac2909ca1f944c0e734060caf6f9483cf4f12
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_item.h
@@ -0,0 +1,307 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_ITEM_H
+#define QWT_PLOT_ITEM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_legend_data.h"
+#include "qwt/qwt_graphic.h"
+#include <qrect.h>
+#include <qlist.h>
+#include <qmetatype.h>
+
+class QPainter;
+class QwtScaleMap;
+class QwtScaleDiv;
+class QwtPlot;
+
+/*!
+  \brief Base class for items on the plot canvas
+
+  A plot item is "something", that can be painted on the plot canvas,
+  or only affects the scales of the plot widget. They can be categorized as:
+
+  - Representator\n
+    A "Representator" is an item that represents some sort of data
+    on the plot canvas. The different representator classes are organized
+    according to the characteristics of the data:
+    - QwtPlotMarker
+      Represents a point or a horizontal/vertical coordinate
+    - QwtPlotCurve
+      Represents a series of points
+    - QwtPlotSpectrogram ( QwtPlotRasterItem )
+      Represents raster data
+    - ...
+
+  - Decorators\n
+    A "Decorator" is an item, that displays additional information, that
+    is not related to any data:
+    - QwtPlotGrid
+    - QwtPlotScaleItem
+    - QwtPlotSvgItem
+    - ...
+
+  Depending on the QwtPlotItem::ItemAttribute flags, an item is included
+  into autoscaling or has an entry on the legend.
+
+  Before misusing the existing item classes it might be better to
+  implement a new type of plot item
+  ( don't implement a watermark as spectrogram ).
+  Deriving a new type of QwtPlotItem primarily means to implement
+  the YourPlotItem::draw() method.
+
+  \sa The cpuplot example shows the implementation of additional plot items.
+*/
+
+class QWT_EXPORT QwtPlotItem
+{
+public:
+    /*!
+        \brief Runtime type information
+
+        RttiValues is used to cast plot items, without
+        having to enable runtime type information of the compiler.
+     */
+    enum RttiValues
+    {
+        //! Unspecific value, that can be used, when it doesn't matter
+        Rtti_PlotItem = 0,
+
+        //! For QwtPlotGrid
+        Rtti_PlotGrid,
+
+        //! For QwtPlotScaleItem
+        Rtti_PlotScale,
+
+        //! For QwtPlotLegendItem
+        Rtti_PlotLegend,
+
+        //! For QwtPlotMarker
+        Rtti_PlotMarker,
+
+        //! For QwtPlotCurve
+        Rtti_PlotCurve,
+
+        //! For QwtPlotSpectroCurve
+        Rtti_PlotSpectroCurve,
+
+        //! For QwtPlotIntervalCurve
+        Rtti_PlotIntervalCurve,
+
+        //! For QwtPlotHistogram
+        Rtti_PlotHistogram,
+
+        //! For QwtPlotSpectrogram
+        Rtti_PlotSpectrogram,
+
+        //! For QwtPlotSvgItem
+        Rtti_PlotSVG,
+
+        //! For QwtPlotTradingCurve
+        Rtti_PlotTradingCurve,
+
+        //! For QwtPlotBarChart
+        Rtti_PlotBarChart,
+
+        //! For QwtPlotMultiBarChart
+        Rtti_PlotMultiBarChart,
+
+        //! For QwtPlotShapeItem
+        Rtti_PlotShape,
+
+        //! For QwtPlotTextLabel
+        Rtti_PlotTextLabel,
+
+        //! For QwtPlotZoneItem
+        Rtti_PlotZone,
+
+        /*! 
+           Values >= Rtti_PlotUserItem are reserved for plot items
+           not implemented in the Qwt library.
+         */
+        Rtti_PlotUserItem = 1000
+    };
+
+    /*!
+       \brief Plot Item Attributes
+
+       Various aspects of a plot widget depend on the attributes of
+       the attached plot items. If and how a single plot item 
+       participates in these updates depends on its attributes.
+       
+       \sa setItemAttribute(), testItemAttribute(), ItemInterest
+     */
+    enum ItemAttribute
+    {
+        //! The item is represented on the legend.
+        Legend = 0x01,
+
+        /*!
+           The boundingRect() of the item is included in the
+           autoscaling calculation as long as its width or height
+           is >= 0.0.
+         */
+        AutoScale = 0x02,
+
+        /*!
+           The item needs extra space to display something outside
+           its bounding rectangle. 
+           \sa getCanvasMarginHint()
+         */
+        Margins = 0x04
+    };
+
+    //! Plot Item Attributes
+    typedef QFlags<ItemAttribute> ItemAttributes;
+
+    /*!
+       \brief Plot Item Interests
+
+       Plot items might depend on the situation of the corresponding
+       plot widget. By enabling an interest the plot item will be
+       notified, when the corresponding attribute of the plot widgets
+       has changed.
+
+       \sa setItemAttribute(), testItemAttribute(), ItemInterest
+     */
+    enum ItemInterest
+    {
+        /*! 
+           The item is interested in updates of the scales
+           \sa updateScaleDiv()
+         */
+        ScaleInterest = 0x01,
+
+        /*! 
+           The item is interested in updates of the legend ( of other items )
+           This flag is intended for items, that want to implement a legend
+           for displaying entries of other plot item.
+
+           \note If the plot item wants to be represented on a legend
+                 enable QwtPlotItem::Legend instead.
+
+           \sa updateLegend()
+         */
+        LegendInterest = 0x02
+    };
+
+    //! Plot Item Interests
+    typedef QFlags<ItemInterest> ItemInterests;
+
+    //! Render hints
+    enum RenderHint
+    {
+        //! Enable antialiasing
+        RenderAntialiased = 0x1
+    };
+
+    //! Render hints
+    typedef QFlags<RenderHint> RenderHints;
+
+    explicit QwtPlotItem( const QwtText &title = QwtText() );
+    virtual ~QwtPlotItem();
+
+    void attach( QwtPlot *plot );
+    void detach();
+
+    QwtPlot *plot() const;
+
+    void setTitle( const QString &title );
+    void setTitle( const QwtText &title );
+    const QwtText &title() const;
+
+    virtual int rtti() const;
+
+    void setItemAttribute( ItemAttribute, bool on = true );
+    bool testItemAttribute( ItemAttribute ) const;
+
+    void setItemInterest( ItemInterest, bool on = true );
+    bool testItemInterest( ItemInterest ) const;
+
+    void setRenderHint( RenderHint, bool on = true );
+    bool testRenderHint( RenderHint ) const;
+
+    void setRenderThreadCount( uint numThreads );
+    uint renderThreadCount() const;
+
+    void setLegendIconSize( const QSize & );
+    QSize legendIconSize() const;
+
+    double z() const;
+    void setZ( double z );
+
+    void show();
+    void hide();
+    virtual void setVisible( bool );
+    bool isVisible () const;
+
+    void setAxes( int xAxis, int yAxis );
+
+    void setXAxis( int axis );
+    int xAxis() const;
+
+    void setYAxis( int axis );
+    int yAxis() const;
+
+    virtual void itemChanged();
+    virtual void legendChanged();
+
+    /*!
+      \brief Draw the item
+
+      \param painter Painter
+      \param xMap Maps x-values into pixel coordinates.
+      \param yMap Maps y-values into pixel coordinates.
+      \param canvasRect Contents rect of the canvas in painter coordinates
+    */
+    virtual void draw( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect ) const = 0;
+
+    virtual QRectF boundingRect() const;
+
+    virtual void getCanvasMarginHint( 
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasSize,
+        double &left, double &top, double &right, double &bottom) const;
+
+    virtual void updateScaleDiv( 
+        const QwtScaleDiv&, const QwtScaleDiv& );
+
+    virtual void updateLegend( const QwtPlotItem *,
+        const QList<QwtLegendData> & );
+
+    QRectF scaleRect( const QwtScaleMap &, const QwtScaleMap & ) const;
+    QRectF paintRect( const QwtScaleMap &, const QwtScaleMap & ) const;
+
+    virtual QList<QwtLegendData> legendData() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF  & ) const;
+
+protected:
+    QwtGraphic defaultIcon( const QBrush &, const QSizeF & ) const;
+
+private:
+    // Disabled copy constructor and operator=
+    QwtPlotItem( const QwtPlotItem & );
+    QwtPlotItem &operator=( const QwtPlotItem & );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemAttributes )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemInterests )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::RenderHints )
+
+Q_DECLARE_METATYPE( QwtPlotItem * )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_layout.h b/include/pli_vis/third_party/qwt/qwt_plot_layout.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc31b6105b6e973b733bca72df5ffca813127e6e
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_layout.h
@@ -0,0 +1,122 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_LAYOUT_H
+#define QWT_PLOT_LAYOUT_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot.h"
+
+/*!
+  \brief Layout engine for QwtPlot.
+
+  It is used by the QwtPlot widget to organize its internal widgets
+  or by QwtPlot::print() to render its content to a QPaintDevice like
+  a QPrinter, QPixmap/QImage or QSvgRenderer.
+
+  \sa QwtPlot::setPlotLayout()
+*/
+
+class QWT_EXPORT QwtPlotLayout
+{
+public:
+    /*!
+      Options to configure the plot layout engine
+      \sa activate(), QwtPlotRenderer
+     */
+    enum Option
+    {
+        //! Unused
+        AlignScales = 0x01,
+
+        /*!
+          Ignore the dimension of the scrollbars. There are no
+          scrollbars, when the plot is not rendered to widgets.
+         */
+        IgnoreScrollbars = 0x02,
+
+        //! Ignore all frames. 
+        IgnoreFrames = 0x04,
+
+        //! Ignore the legend.
+        IgnoreLegend = 0x08,
+
+        //! Ignore the title.
+        IgnoreTitle = 0x10,
+
+        //! Ignore the footer.
+        IgnoreFooter = 0x20
+    };
+
+    //! Layout options
+    typedef QFlags<Option> Options;
+
+    explicit QwtPlotLayout();
+    virtual ~QwtPlotLayout();
+
+    void setCanvasMargin( int margin, int axis = -1 );
+    int canvasMargin( int axis ) const;
+
+    void setAlignCanvasToScales( bool );
+
+    void setAlignCanvasToScale( int axisId, bool );
+    bool alignCanvasToScale( int axisId ) const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    void setLegendPosition( QwtPlot::LegendPosition pos, double ratio );
+    void setLegendPosition( QwtPlot::LegendPosition pos );
+    QwtPlot::LegendPosition legendPosition() const;
+
+    void setLegendRatio( double ratio );
+    double legendRatio() const;
+
+    virtual QSize minimumSizeHint( const QwtPlot * ) const;
+
+    virtual void activate( const QwtPlot *,
+        const QRectF &rect, Options options = 0x00 );
+
+    virtual void invalidate();
+
+    QRectF titleRect() const;
+    QRectF footerRect() const;
+    QRectF legendRect() const;
+    QRectF scaleRect( int axis ) const;
+    QRectF canvasRect() const;
+
+    class LayoutData;
+
+protected:
+
+    void setTitleRect( const QRectF & );
+    void setFooterRect( const QRectF & );
+    void setLegendRect( const QRectF & );
+    void setScaleRect( int axis, const QRectF & );
+    void setCanvasRect( const QRectF & );
+
+    QRectF layoutLegend( Options options, const QRectF & ) const;
+    QRectF alignLegend( const QRectF &canvasRect,
+        const QRectF &legendRect ) const;
+
+    void expandLineBreaks( Options options, const QRectF &rect,
+        int &dimTitle, int &dimFooter, int dimAxes[QwtPlot::axisCnt] ) const;
+
+    void alignScales( Options options, QRectF &canvasRect,
+        QRectF scaleRect[QwtPlot::axisCnt] ) const;
+
+private:
+    class PrivateData;
+
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotLayout::Options )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_legenditem.h b/include/pli_vis/third_party/qwt/qwt_plot_legenditem.h
new file mode 100644
index 0000000000000000000000000000000000000000..81e90b74a8c00497fe55c61eda05d87eb76f9d98
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_legenditem.h
@@ -0,0 +1,136 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_LEGEND_ITEM_H
+#define QWT_PLOT_LEGEND_ITEM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_legend_data.h"
+
+class QFont;
+
+/*!
+  \brief A class which draws a legend inside the plot canvas
+
+  QwtPlotLegendItem can be used to draw a inside the plot canvas.
+  It can be used together with a QwtLegend or instead of it
+  to have more space for the plot canvas.
+
+  In opposite to QwtLegend the legend item is not interactive. 
+  To identify mouse clicks on a legend item an event filter
+  needs to be installed catching mouse events ob the plot canvas.
+  The geometries of the legend items are available using
+  legendGeometries().
+  
+  The legend item is aligned to plot canvas according to 
+  its alignment() flags. It might have a background for the
+  complete legend ( usually semi transparent ) or for
+  each legend item.
+
+  \note An external QwtLegend with a transparent background 
+        on top the plot canvas might be another option 
+        with a similar effect.
+*/
+
+class QWT_EXPORT QwtPlotLegendItem: public QwtPlotItem
+{
+public:
+    /*!
+      \brief Background mode
+
+      Depending on the mode the complete legend or each item 
+      might have an background.
+
+      The default setting is LegendBackground.
+
+       \sa setBackgroundMode(), setBackgroundBrush(), drawBackground()
+     */
+    enum BackgroundMode
+    {
+        //! The legend has a background
+        LegendBackground,
+
+        //! Each item has a background
+        ItemBackground
+    };
+
+    explicit QwtPlotLegendItem();
+    virtual ~QwtPlotLegendItem();
+
+    virtual int rtti() const;
+
+    void setAlignment( Qt::Alignment );
+    Qt::Alignment alignment() const;
+
+    void setMaxColumns( uint );
+    uint maxColumns() const;
+
+    void setMargin( int );
+    int margin() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    void setItemMargin( int );
+    int itemMargin() const;
+
+    void setItemSpacing( int );
+    int itemSpacing() const;
+    
+    void setFont( const QFont& );
+    QFont font() const;
+
+    void setBorderDistance( int numPixels );
+    int borderDistance() const;
+
+    void setBorderRadius( double );
+    double borderRadius() const;
+
+    void setBorderPen( const QPen & );
+    QPen borderPen() const;
+
+    void setBackgroundBrush( const QBrush & );
+    QBrush backgroundBrush() const;
+
+    void setBackgroundMode( BackgroundMode );
+    BackgroundMode backgroundMode() const;
+
+    void setTextPen( const QPen & );
+    QPen textPen() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    void clearLegend();
+
+    virtual void updateLegend( const QwtPlotItem *,
+        const QList<QwtLegendData> & );
+
+    virtual QRect geometry( const QRectF &canvasRect ) const;
+
+    virtual QSize minimumSize( const QwtLegendData & ) const;
+    virtual int heightForWidth( const QwtLegendData &, int w ) const;
+
+    QList< const QwtPlotItem * > plotItems() const;
+    QList< QRect > legendGeometries( const QwtPlotItem * ) const;
+
+protected:
+    virtual void drawLegendData( QPainter *painter, 
+        const QwtPlotItem *, const QwtLegendData &, const QRectF & ) const;
+
+    virtual void drawBackground( QPainter *, const QRectF &rect ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_magnifier.h b/include/pli_vis/third_party/qwt/qwt_plot_magnifier.h
new file mode 100644
index 0000000000000000000000000000000000000000..96d01f261ef7e924fcf2e0407c3cfe6e42549b99
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_magnifier.h
@@ -0,0 +1,54 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_MAGNIFIER_H
+#define QWT_PLOT_MAGNIFIER_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_magnifier.h"
+
+class QwtPlot;
+
+/*!
+  \brief QwtPlotMagnifier provides zooming, by magnifying in steps.
+
+  Using QwtPlotMagnifier a plot can be zoomed in/out in steps using
+  keys, the mouse wheel or moving a mouse button in vertical direction.
+
+  Together with QwtPlotZoomer and QwtPlotPanner it is possible to implement
+  individual and powerful navigation of the plot canvas.
+
+  \sa QwtPlotZoomer, QwtPlotPanner, QwtPlot
+*/
+class QWT_EXPORT QwtPlotMagnifier: public QwtMagnifier
+{
+    Q_OBJECT
+
+public:
+    explicit QwtPlotMagnifier( QWidget * );
+    virtual ~QwtPlotMagnifier();
+
+    void setAxisEnabled( int axis, bool on );
+    bool isAxisEnabled( int axis ) const;
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+protected:
+    virtual void rescale( double factor );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_marker.h b/include/pli_vis/third_party/qwt/qwt_plot_marker.h
new file mode 100644
index 0000000000000000000000000000000000000000..9fab1edc6e316a53d5d85862019b8c26efcff1c7
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_marker.h
@@ -0,0 +1,130 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_MARKER_H
+#define QWT_PLOT_MARKER_H
+
+#include <qpen.h>
+#include <qfont.h>
+#include <qstring.h>
+#include <qbrush.h>
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+
+class QRectF;
+class QwtText;
+class QwtSymbol;
+
+/*!
+  \brief A class for drawing markers
+
+  A marker can be a horizontal line, a vertical line,
+  a symbol, a label or any combination of them, which can
+  be drawn around a center point inside a bounding rectangle.
+
+  The setSymbol() member assigns a symbol to the marker.
+  The symbol is drawn at the specified point.
+
+  With setLabel(), a label can be assigned to the marker.
+  The setLabelAlignment() member specifies where the label is
+  drawn. All the Align*-constants in Qt::AlignmentFlags (see Qt documentation)
+  are valid. The interpretation of the alignment depends on the marker's
+  line style. The alignment refers to the center point of
+  the marker, which means, for example, that the label would be printed
+  left above the center point if the alignment was set to 
+  Qt::AlignLeft | Qt::AlignTop.
+
+  \note QwtPlotTextLabel is intended to align a text label
+        according to the geometry of canvas 
+        ( unrelated to plot coordinates )
+*/
+
+class QWT_EXPORT QwtPlotMarker: public QwtPlotItem
+{
+public:
+
+    /*!
+        Line styles.
+        \sa setLineStyle(), lineStyle()
+    */
+    enum LineStyle
+    {
+        //! No line
+        NoLine,
+
+        //! A horizontal line
+        HLine,
+
+        //! A vertical line
+        VLine,
+
+        //! A crosshair
+        Cross
+    };
+
+    explicit QwtPlotMarker( const QString &title = QString::null );
+    explicit QwtPlotMarker( const QwtText &title );
+
+    virtual ~QwtPlotMarker();
+
+    virtual int rtti() const;
+
+    double xValue() const;
+    double yValue() const;
+    QPointF value() const;
+
+    void setXValue( double );
+    void setYValue( double );
+    void setValue( double, double );
+    void setValue( const QPointF & );
+
+    void setLineStyle( LineStyle st );
+    LineStyle lineStyle() const;
+
+    void setLinePen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setLinePen( const QPen &p );
+    const QPen &linePen() const;
+
+    void setSymbol( const QwtSymbol * );
+    const QwtSymbol *symbol() const;
+
+    void setLabel( const QwtText& );
+    QwtText label() const;
+
+    void setLabelAlignment( Qt::Alignment );
+    Qt::Alignment labelAlignment() const;
+
+    void setLabelOrientation( Qt::Orientation );
+    Qt::Orientation labelOrientation() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF & ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+    virtual void drawLines( QPainter *, 
+        const QRectF &, const QPointF & ) const;
+
+    virtual void drawLabel( QPainter *, 
+        const QRectF &, const QPointF & ) const;
+
+private:
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_multi_barchart.h b/include/pli_vis/third_party/qwt/qwt_plot_multi_barchart.h
new file mode 100644
index 0000000000000000000000000000000000000000..acade4910c7c636111ba909f4fad4219d0bc196e
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_multi_barchart.h
@@ -0,0 +1,127 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_MULTI_BAR_CHART_H
+#define QWT_PLOT_MULTI_BAR_CHART_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_abstract_barchart.h"
+#include "qwt/qwt_series_data.h"
+
+class QwtColumnRect;
+class QwtColumnSymbol;
+
+/*!
+  \brief QwtPlotMultiBarChart displays a series of a samples that consist
+         each of a set of values. 
+
+  Each value is displayed as a bar, the bars of each set can be organized 
+  side by side or accumulated.
+
+  Each bar of a set is rendered by a QwtColumnSymbol, that is set by setSymbol().
+  The bars of different sets use the same symbols. Exceptions are possible
+  by overloading specialSymbol() or overloading drawBar().
+
+  Depending on its orientation() the bars are displayed horizontally 
+  or vertically. The bars cover the interval between the baseline() 
+  and the value.
+
+  In opposite to most other plot items, QwtPlotMultiBarChart returns more
+  than one entry for the legend - one for each symbol.
+   
+  \sa QwtPlotBarChart, QwtPlotHistogram
+      QwtPlotSeriesItem::orientation(), QwtPlotAbstractBarChart::baseline()
+ */
+class QWT_EXPORT QwtPlotMultiBarChart: 
+    public QwtPlotAbstractBarChart, public QwtSeriesStore<QwtSetSample>
+{
+public:
+    /*!
+        \brief Chart styles.
+
+        The default setting is QwtPlotMultiBarChart::Grouped.
+        \sa setStyle(), style()
+    */
+    enum ChartStyle
+    {
+        //! The bars of a set are displayed side by side
+        Grouped,
+
+        /*!
+            The bars are displayed on top of each other accumulating
+            to a single bar. All values of a set need to have the same
+            sign.
+         */
+        Stacked
+    };
+
+    explicit QwtPlotMultiBarChart( const QString &title = QString::null );
+    explicit QwtPlotMultiBarChart( const QwtText &title );
+
+    virtual ~QwtPlotMultiBarChart();
+
+    virtual int rtti() const;
+
+    void setBarTitles( const QList<QwtText> & );
+    QList<QwtText> barTitles() const;
+
+    void setSamples( const QVector<QwtSetSample> & );
+    void setSamples( const QVector< QVector<double> > & );
+    void setSamples( QwtSeriesData<QwtSetSample> * );
+
+    void setStyle( ChartStyle style );
+    ChartStyle style() const;
+
+    void setSymbol( int barIndex, QwtColumnSymbol *symbol );
+    const QwtColumnSymbol *symbol( int barIndex ) const;
+
+    void resetSymbolMap();
+
+    virtual void drawSeries( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QList<QwtLegendData> legendData() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+    QwtColumnSymbol *symbol( int barIndex );
+
+    virtual QwtColumnSymbol *specialSymbol( 
+        int sampleIndex, int valueIndex ) const;
+
+    virtual void drawSample( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, const QwtInterval &boundingInterval,
+        int index, const QwtSetSample& sample ) const;
+
+    virtual void drawBar( QPainter *, int sampleIndex,
+        int barIndex, const QwtColumnRect & ) const;
+
+    void drawStackedBars( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int index,
+        double sampleWidth, const QwtSetSample& sample ) const;
+
+    void drawGroupedBars( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int index,
+        double sampleWidth, const QwtSetSample& sample ) const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_panner.h b/include/pli_vis/third_party/qwt/qwt_plot_panner.h
new file mode 100644
index 0000000000000000000000000000000000000000..b7e392055ab545c9f4b7ea81ed242579ee7da98b
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_panner.h
@@ -0,0 +1,60 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_PANNER_H
+#define QWT_PLOT_PANNER_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_panner.h"
+
+class QwtPlot;
+
+/*!
+  \brief QwtPlotPanner provides panning of a plot canvas
+
+  QwtPlotPanner is a panner for a plot canvas, that
+  adjusts the scales of the axes after dropping
+  the canvas on its new position.
+
+  Together with QwtPlotZoomer and QwtPlotMagnifier powerful ways
+  of navigating on a QwtPlot widget can be implemented easily.
+
+  \note The axes are not updated, while dragging the canvas
+  \sa QwtPlotZoomer, QwtPlotMagnifier
+*/
+class QWT_EXPORT QwtPlotPanner: public QwtPanner
+{
+    Q_OBJECT
+
+public:
+    explicit QwtPlotPanner( QWidget * );
+    virtual ~QwtPlotPanner();
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+    void setAxisEnabled( int axis, bool on );
+    bool isAxisEnabled( int axis ) const;
+
+protected Q_SLOTS:
+    virtual void moveCanvas( int dx, int dy );
+
+protected:
+    virtual QBitmap contentsMask() const;
+    virtual QPixmap grab() const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_picker.h b/include/pli_vis/third_party/qwt/qwt_plot_picker.h
new file mode 100644
index 0000000000000000000000000000000000000000..d48e89f3ed9b670e69f1b22903aa191c60fadda6
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_picker.h
@@ -0,0 +1,111 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_PICKER_H
+#define QWT_PLOT_PICKER_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_picker.h"
+#include <qvector.h>
+
+class QwtPlot;
+
+/*!
+  \brief QwtPlotPicker provides selections on a plot canvas
+
+  QwtPlotPicker is a QwtPicker tailored for selections on
+  a plot canvas. It is set to a x-Axis and y-Axis and
+  translates all pixel coordinates into this coordinate system.
+*/
+
+class QWT_EXPORT QwtPlotPicker: public QwtPicker
+{
+    Q_OBJECT
+
+public:
+    explicit QwtPlotPicker( QWidget *canvas );
+    virtual ~QwtPlotPicker();
+
+    explicit QwtPlotPicker( int xAxis, int yAxis, QWidget * );
+
+    explicit QwtPlotPicker( int xAxis, int yAxis,
+        RubberBand rubberBand, DisplayMode trackerMode, QWidget * );
+
+    virtual void setAxis( int xAxis, int yAxis );
+
+    int xAxis() const;
+    int yAxis() const;
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+Q_SIGNALS:
+
+    /*!
+      A signal emitted in case of QwtPickerMachine::PointSelection.
+      \param pos Selected point
+    */
+    void selected( const QPointF &pos );
+
+    /*!
+      A signal emitted in case of QwtPickerMachine::RectSelection.
+      \param rect Selected rectangle
+    */
+    void selected( const QRectF &rect );
+
+    /*!
+      A signal emitting the selected points,
+      at the end of a selection.
+
+      \param pa Selected points
+    */
+    void selected( const QVector<QPointF> &pa );
+
+    /*!
+      A signal emitted when a point has been appended to the selection
+
+      \param pos Position of the appended point.
+      \sa append(). moved()
+    */
+    void appended( const QPointF &pos );
+
+    /*!
+      A signal emitted whenever the last appended point of the
+      selection has been moved.
+
+      \param pos Position of the moved last point of the selection.
+      \sa move(), appended()
+    */
+    void moved( const QPointF &pos );
+
+protected:
+    QRectF scaleRect() const;
+
+    QRectF invTransform( const QRect & ) const;
+    QRect transform( const QRectF & ) const;
+
+    QPointF invTransform( const QPoint & ) const;
+    QPoint transform( const QPointF & ) const;
+
+    virtual QwtText trackerText( const QPoint & ) const;
+    virtual QwtText trackerTextF( const QPointF & ) const;
+
+    virtual void move( const QPoint & );
+    virtual void append( const QPoint & );
+    virtual bool end( bool ok = true );
+
+private:
+    int d_xAxis;
+    int d_yAxis;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_rasteritem.h b/include/pli_vis/third_party/qwt/qwt_plot_rasteritem.h
new file mode 100644
index 0000000000000000000000000000000000000000..5409aa9cf4b71fabf9c7957a850a63217867a2a4
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_rasteritem.h
@@ -0,0 +1,152 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_RASTERITEM_H
+#define QWT_PLOT_RASTERITEM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_interval.h"
+#include <qglobal.h>
+#include <qstring.h>
+#include <qimage.h>
+
+/*!
+  \brief A class, which displays raster data
+
+  Raster data is a grid of pixel values, that can be represented
+  as a QImage. It is used for many types of information like
+  spectrograms, cartograms, geographical maps ...
+
+  Often a plot has several types of raster data organized in layers.
+  ( f.e a geographical map, with weather statistics ).
+  Using setAlpha() raster items can be stacked easily.
+
+  QwtPlotRasterItem is only implemented for images of the following formats:
+  QImage::Format_Indexed8, QImage::Format_ARGB32.
+
+  \sa QwtPlotSpectrogram
+*/
+
+class QWT_EXPORT QwtPlotRasterItem: public QwtPlotItem
+{
+public:
+    /*!
+      \brief Cache policy
+      The default policy is NoCache
+     */
+    enum CachePolicy
+    {
+        /*!
+          renderImage() is called each time the item has to be repainted
+         */
+        NoCache,
+
+        /*!
+          renderImage() is called, whenever the image cache is not valid,
+          or the scales, or the size of the canvas has changed. 
+
+          This type of cache is useful for improving the performance 
+          of hide/show operations or manipulations of the alpha value. 
+          All other situations are handled by the canvas backing store.
+         */
+        PaintCache
+    };
+
+    /*!
+        Attributes to modify the drawing algorithm.
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        /*!
+          When the image is rendered according to the data pixels
+          ( QwtRasterData::pixelHint() ) it can be expanded to paint
+          device resolution before it is passed to QPainter. 
+          The expansion algorithm rounds the pixel borders in the same 
+          way as the axis ticks, what is usually better than the
+          scaling algorithm implemented in Qt.
+          Disabling this flag might make sense, to reduce the size of a 
+          document/file. If this is possible for a document format
+          depends on the implementation of the specific QPaintEngine.
+         */
+
+        PaintInDeviceResolution = 1
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotRasterItem( const QString& title = QString::null );
+    explicit QwtPlotRasterItem( const QwtText& title );
+    virtual ~QwtPlotRasterItem();
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setAlpha( int alpha );
+    int alpha() const;
+
+    void setCachePolicy( CachePolicy );
+    CachePolicy cachePolicy() const;
+
+    void invalidateCache();
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual QRectF pixelHint( const QRectF & ) const;
+
+    virtual QwtInterval interval(Qt::Axis) const;
+    virtual QRectF boundingRect() const;
+
+protected:
+    /*!
+      \brief Render an image 
+
+      An implementation of render() might iterate over all
+      pixels of imageRect. Each pixel has to be translated into 
+      the corresponding position in scale coordinates using the maps.
+      This position can be used to look up a value in a implementation
+      specific way and to map it into a color.
+
+      \param xMap X-Scale Map
+      \param yMap Y-Scale Map
+      \param area Requested area for the image in scale coordinates
+      \param imageSize Requested size of the image
+   
+      \return Rendered image
+     */
+    virtual QImage renderImage( const QwtScaleMap &xMap,
+        const QwtScaleMap &yMap, const QRectF &area,
+        const QSize &imageSize ) const = 0;
+
+    virtual QwtScaleMap imageMap( Qt::Orientation,
+        const QwtScaleMap &map, const QRectF &area,
+        const QSize &imageSize, double pixelSize) const;
+
+private:
+    QwtPlotRasterItem( const QwtPlotRasterItem & );
+    QwtPlotRasterItem &operator=( const QwtPlotRasterItem & );
+
+    void init();
+
+    QImage compose( const QwtScaleMap &, const QwtScaleMap &,
+        const QRectF &imageArea, const QRectF &paintRect,
+        const QSize &imageSize, bool doCache) const;
+
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRasterItem::PaintAttributes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_renderer.h b/include/pli_vis/third_party/qwt/qwt_plot_renderer.h
new file mode 100644
index 0000000000000000000000000000000000000000..080654e1bfc22d9d4f0eb2e3778ba315c9dfc0e6
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_renderer.h
@@ -0,0 +1,170 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_RENDERER_H
+#define QWT_PLOT_RENDERER_H
+
+#include "qwt/qwt_global.h"
+#include <qobject.h>
+#include <qsize.h>
+
+class QwtPlot;
+class QwtScaleMap;
+class QRectF;
+class QPainter;
+class QPaintDevice;
+
+#ifndef QT_NO_PRINTER
+class QPrinter;
+#endif
+
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+class QSvgGenerator;
+#endif
+#endif
+
+/*!
+    \brief Renderer for exporting a plot to a document, a printer
+           or anything else, that is supported by QPainter/QPaintDevice
+*/
+class QWT_EXPORT QwtPlotRenderer: public QObject
+{
+    Q_OBJECT
+
+public:
+    //! Disard flags
+    enum DiscardFlag
+    {
+        //! Render all components of the plot
+        DiscardNone             = 0x00,
+
+        //! Don't render the background of the plot
+        DiscardBackground       = 0x01,
+
+        //! Don't render the title of the plot
+        DiscardTitle            = 0x02,
+
+        //! Don't render the legend of the plot
+        DiscardLegend           = 0x04,
+
+        //! Don't render the background of the canvas
+        DiscardCanvasBackground = 0x08,
+
+        //! Don't render the footer of the plot
+        DiscardFooter           = 0x10,
+
+        /*! 
+            Don't render the frame of the canvas
+
+            \note This flag has no effect when using
+                  style sheets, where the frame is part
+                  of the background
+         */
+        DiscardCanvasFrame           = 0x20
+
+    };
+
+    //! Disard flags
+    typedef QFlags<DiscardFlag> DiscardFlags;
+
+    /*!
+       \brief Layout flags
+       \sa setLayoutFlag(), testLayoutFlag()
+     */
+    enum LayoutFlag
+    {
+        //! Use the default layout as on screen
+        DefaultLayout   = 0x00,
+
+        /*!
+          Instead of the scales a box is painted around the plot canvas,
+          where the scale ticks are aligned to.
+         */
+        FrameWithScales = 0x01
+    };
+
+    //! Layout flags
+    typedef QFlags<LayoutFlag> LayoutFlags;
+
+    explicit QwtPlotRenderer( QObject * = NULL );
+    virtual ~QwtPlotRenderer();
+
+    void setDiscardFlag( DiscardFlag flag, bool on = true );
+    bool testDiscardFlag( DiscardFlag flag ) const;
+
+    void setDiscardFlags( DiscardFlags flags );
+    DiscardFlags discardFlags() const;
+
+    void setLayoutFlag( LayoutFlag flag, bool on = true );
+    bool testLayoutFlag( LayoutFlag flag ) const;
+
+    void setLayoutFlags( LayoutFlags flags );
+    LayoutFlags layoutFlags() const;
+
+    void renderDocument( QwtPlot *, const QString &fileName,
+        const QSizeF &sizeMM, int resolution = 85 );
+
+    void renderDocument( QwtPlot *,
+        const QString &fileName, const QString &format,
+        const QSizeF &sizeMM, int resolution = 85 );
+
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+#if QT_VERSION >= 0x040500
+    void renderTo( QwtPlot *, QSvgGenerator & ) const;
+#endif
+#endif
+#endif
+
+#ifndef QT_NO_PRINTER
+    void renderTo( QwtPlot *, QPrinter & ) const;
+#endif
+
+    void renderTo( QwtPlot *, QPaintDevice &p ) const;
+
+    virtual void render( QwtPlot *,
+        QPainter *, const QRectF &rect ) const;
+
+    virtual void renderTitle( const QwtPlot *,
+        QPainter *, const QRectF & ) const;
+
+    virtual void renderFooter( const QwtPlot *,
+        QPainter *, const QRectF & ) const;
+
+    virtual void renderScale( const QwtPlot *, QPainter *,
+        int axisId, int startDist, int endDist,
+        int baseDist, const QRectF & ) const;
+
+    virtual void renderCanvas( const QwtPlot *,
+        QPainter *, const QRectF &canvasRect,
+        const QwtScaleMap* maps ) const;
+
+    virtual void renderLegend( 
+        const QwtPlot *, QPainter *, const QRectF & ) const;
+
+    bool exportTo( QwtPlot *, const QString &documentName,
+        const QSizeF &sizeMM = QSizeF( 300, 200 ), int resolution = 85 );
+
+private:
+    void buildCanvasMaps( const QwtPlot *,
+        const QRectF &, QwtScaleMap maps[] ) const;
+
+    bool updateCanvasMargins( QwtPlot *,
+        const QRectF &, const QwtScaleMap maps[] ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRenderer::DiscardFlags )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRenderer::LayoutFlags )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_rescaler.h b/include/pli_vis/third_party/qwt/qwt_plot_rescaler.h
new file mode 100644
index 0000000000000000000000000000000000000000..1cd9099a7fc28cbebcf304441768e3cfcec253bc
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_rescaler.h
@@ -0,0 +1,142 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_RESCALER_H
+#define QWT_PLOT_RESCALER_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_interval.h"
+#include "qwt/qwt_plot.h"
+#include <qobject.h>
+
+class QwtPlot;
+class QResizeEvent;
+
+/*!
+    \brief QwtPlotRescaler takes care of fixed aspect ratios for plot scales
+
+    QwtPlotRescaler auto adjusts the axes of a QwtPlot according
+    to fixed aspect ratios.
+*/
+
+class QWT_EXPORT QwtPlotRescaler: public QObject
+{
+public:
+    /*!
+      The rescale policy defines how to rescale the reference axis and
+      their depending axes.
+
+      \sa ExpandingDirection, setIntervalHint()
+    */
+    enum RescalePolicy
+    {
+        /*!
+          The interval of the reference axis remains unchanged, when the
+          geometry of the canvas changes. All other axes
+          will be adjusted according to their aspect ratio.
+         */
+        Fixed,
+
+        /*!
+          The interval of the reference axis will be shrunk/expanded,
+          when the geometry of the canvas changes. All other axes
+          will be adjusted according to their aspect ratio.
+
+          The interval, that is represented by one pixel is fixed.
+
+         */
+        Expanding,
+
+        /*!
+          The intervals of the axes are calculated, so that all axes include
+          their interval hint.
+         */
+        Fitting
+    };
+
+    /*!
+       When rescalePolicy() is set to Expanding its direction depends
+       on ExpandingDirection
+     */
+    enum ExpandingDirection
+    {
+        //! The upper limit of the scale is adjusted
+        ExpandUp,
+
+        //! The lower limit of the scale is adjusted
+        ExpandDown,
+
+        //! Both limits of the scale are adjusted
+        ExpandBoth
+    };
+
+    explicit QwtPlotRescaler( QWidget *canvas,
+        int referenceAxis = QwtPlot::xBottom,
+        RescalePolicy = Expanding );
+
+    virtual ~QwtPlotRescaler();
+
+    void setEnabled( bool );
+    bool isEnabled() const;
+
+    void setRescalePolicy( RescalePolicy );
+    RescalePolicy rescalePolicy() const;
+
+    void setExpandingDirection( ExpandingDirection );
+    void setExpandingDirection( int axis, ExpandingDirection );
+    ExpandingDirection expandingDirection( int axis ) const;
+
+    void setReferenceAxis( int axis );
+    int referenceAxis() const;
+
+    void setAspectRatio( double ratio );
+    void setAspectRatio( int axis, double ratio );
+    double aspectRatio( int axis ) const;
+
+    void setIntervalHint( int axis, const QwtInterval& );
+    QwtInterval intervalHint( int axis ) const;
+
+    QWidget *canvas();
+    const QWidget *canvas() const;
+
+    QwtPlot *plot();
+    const QwtPlot *plot() const;
+
+    virtual bool eventFilter( QObject *, QEvent * );
+
+    void rescale() const;
+
+protected:
+    virtual void canvasResizeEvent( QResizeEvent * );
+
+    virtual void rescale( const QSize &oldSize, const QSize &newSize ) const;
+    virtual QwtInterval expandScale( 
+        int axis, const QSize &oldSize, const QSize &newSize ) const;
+
+    virtual QwtInterval syncScale(
+        int axis, const QwtInterval& reference,
+        const QSize &size ) const;
+
+    virtual void updateScales(
+        QwtInterval intervals[QwtPlot::axisCnt] ) const;
+
+    Qt::Orientation orientation( int axis ) const;
+    QwtInterval interval( int axis ) const;
+    QwtInterval expandInterval( const QwtInterval &,
+        double width, ExpandingDirection ) const;
+
+private:
+    double pixelDist( int axis, const QSize & ) const;
+
+    class AxisData;
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_scaleitem.h b/include/pli_vis/third_party/qwt/qwt_plot_scaleitem.h
new file mode 100644
index 0000000000000000000000000000000000000000..c67a5fd20233eacf0bee15e5c8d23abed8d69832
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_scaleitem.h
@@ -0,0 +1,94 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SCALE_ITEM_H
+#define QWT_PLOT_SCALE_ITEM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_scale_draw.h"
+
+class QPalette;
+
+/*!
+  \brief A class which draws a scale inside the plot canvas
+
+  QwtPlotScaleItem can be used to draw an axis inside the plot canvas.
+  It might by synchronized to one of the axis of the plot, but can
+  also display its own ticks and labels.
+
+  It is allowed to synchronize the scale item with a disabled axis.
+  In plots with vertical and horizontal scale items, it might be
+  necessary to remove ticks at the intersections, by overloading
+  updateScaleDiv().
+
+  The scale might be at a specific position (f.e 0.0) or it might be
+  aligned to a canvas border.
+
+  \par Example
+  The following example shows how to replace the left axis, by a scale item
+  at the x position 0.0.
+  \verbatim
+QwtPlotScaleItem *scaleItem =
+    new QwtPlotScaleItem(QwtScaleDraw::RightScale, 0.0);
+scaleItem->setFont(plot->axisWidget(QwtPlot::yLeft)->font());
+scaleItem->attach(plot);
+
+plot->enableAxis(QwtPlot::yLeft, false);
+\endverbatim
+*/
+
+class QWT_EXPORT QwtPlotScaleItem: public QwtPlotItem
+{
+public:
+    explicit QwtPlotScaleItem(
+        QwtScaleDraw::Alignment = QwtScaleDraw::BottomScale,
+        const double pos = 0.0 );
+
+    virtual ~QwtPlotScaleItem();
+
+    virtual int rtti() const;
+
+    void setScaleDiv( const QwtScaleDiv& );
+    const QwtScaleDiv& scaleDiv() const;
+
+    void setScaleDivFromAxis( bool on );
+    bool isScaleDivFromAxis() const;
+
+    void setPalette( const QPalette & );
+    QPalette palette() const;
+
+    void setFont( const QFont& );
+    QFont font() const;
+
+    void setScaleDraw( QwtScaleDraw * );
+
+    const QwtScaleDraw *scaleDraw() const;
+    QwtScaleDraw *scaleDraw();
+
+    void setPosition( double pos );
+    double position() const;
+
+    void setBorderDistance( int numPixels );
+    int borderDistance() const;
+
+    void setAlignment( QwtScaleDraw::Alignment );
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual void updateScaleDiv( const QwtScaleDiv &, const QwtScaleDiv & );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_seriesitem.h b/include/pli_vis/third_party/qwt/qwt_plot_seriesitem.h
new file mode 100644
index 0000000000000000000000000000000000000000..7df3aad9ee374c68844ca2244946b7a79b5cadf0
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_seriesitem.h
@@ -0,0 +1,66 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SERIES_ITEM_H
+#define QWT_PLOT_SERIES_ITEM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_series_data.h"
+#include "qwt/qwt_series_store.h"
+
+/*!
+  \brief Base class for plot items representing a series of samples
+*/
+class QWT_EXPORT QwtPlotSeriesItem: public QwtPlotItem,
+    public virtual QwtAbstractSeriesStore
+{
+public:
+    explicit QwtPlotSeriesItem( const QString &title = QString::null );
+    explicit QwtPlotSeriesItem( const QwtText &title );
+
+    virtual ~QwtPlotSeriesItem();
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF & ) const;
+
+    /*!
+      Draw a subset of the samples
+
+      \param painter Painter
+      \param xMap Maps x-values into pixel coordinates.
+      \param yMap Maps y-values into pixel coordinates.
+      \param canvasRect Contents rectangle of the canvas
+      \param from Index of the first point to be painted
+      \param to Index of the last point to be painted. If to < 0 the
+             curve will be painted to its last point.
+    */
+    virtual void drawSeries( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const = 0;
+
+    virtual QRectF boundingRect() const;
+
+    virtual void updateScaleDiv( 
+        const QwtScaleDiv &, const QwtScaleDiv & );
+
+protected:
+    virtual void dataChanged();
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_shapeitem.h b/include/pli_vis/third_party/qwt/qwt_plot_shapeitem.h
new file mode 100644
index 0000000000000000000000000000000000000000..006f885c84cdab7744735678cf31050f89756a1f
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_shapeitem.h
@@ -0,0 +1,111 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SHAPE_ITEM_H
+#define QWT_PLOT_SHAPE_ITEM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include <qpainterpath.h>
+
+/*!
+  \brief A plot item, which displays any graphical shape, 
+         that can be defined by a QPainterPath
+
+  A QPainterPath is a shape composed from intersecting and uniting
+  regions, rectangles, ellipses or irregular areas defined by lines, and curves.
+  QwtPlotShapeItem displays a shape with a pen and brush.
+
+  QwtPlotShapeItem offers a couple of optimizations like clipping or weeding.
+  These algorithms need to convert the painter path into polygons that might be
+  less performant for paths built from curves and ellipses.
+
+  \sa QwtPlotZone
+*/
+class QWT_EXPORT QwtPlotShapeItem: public QwtPlotItem
+{
+public:
+    /*!
+        Attributes to modify the drawing algorithm.
+        The default disables all attributes
+
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        /*!
+          Clip polygons before painting them. In situations, where points
+          are far outside the visible area (f.e when zooming deep) this
+          might be a substantial improvement for the painting performance
+
+          But polygon clipping will convert the painter path into
+          polygons what might introduce a negative impact on the
+          performance of paths composed from curves or ellipses.
+         */
+        ClipPolygons = 0x01,
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    //! Mode how to display the item on the legend
+    enum LegendMode
+    {
+        //! Display a scaled down version of the shape
+        LegendShape,
+
+        //! Display a filled rectangle 
+        LegendColor
+    };
+
+    explicit QwtPlotShapeItem( const QString &title = QString::null );
+    explicit QwtPlotShapeItem( const QwtText &title );
+
+    virtual ~QwtPlotShapeItem();
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setLegendMode( LegendMode );
+    LegendMode legendMode() const;
+
+    void setRect( const QRectF & );
+    void setPolygon( const QPolygonF & );
+
+    void setShape( const QPainterPath & );
+    QPainterPath shape() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    QPen pen() const;
+
+    void setBrush( const QBrush & );
+    QBrush brush() const;
+
+    void setRenderTolerance( double );
+    double renderTolerance() const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+    virtual int rtti() const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_spectrocurve.h b/include/pli_vis/third_party/qwt/qwt_plot_spectrocurve.h
new file mode 100644
index 0000000000000000000000000000000000000000..2aa69a5a1e74ca0b11a452e26d8228d11f11abd8
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_spectrocurve.h
@@ -0,0 +1,79 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_CURVE_3D_H
+#define QWT_PLOT_CURVE_3D_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_seriesitem.h"
+#include "qwt/qwt_series_data.h"
+
+class QwtSymbol;
+class QwtColorMap;
+
+/*!
+    \brief Curve that displays 3D points as dots, where the z coordinate is
+           mapped to a color.
+*/
+class QWT_EXPORT QwtPlotSpectroCurve: 
+    public QwtPlotSeriesItem, QwtSeriesStore<QwtPoint3D>
+{
+public:
+    //! Paint attributes
+    enum PaintAttribute
+    {
+        //! Clip points outside the canvas rectangle
+        ClipPoints = 1
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotSpectroCurve( const QString &title = QString::null );
+    explicit QwtPlotSpectroCurve( const QwtText &title );
+
+    virtual ~QwtPlotSpectroCurve();
+
+    virtual int rtti() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setSamples( const QVector<QwtPoint3D> & );
+    void setSamples( QwtSeriesData<QwtPoint3D> * );
+
+
+    void setColorMap( QwtColorMap * );
+    const QwtColorMap *colorMap() const;
+
+    void setColorRange( const QwtInterval & );
+    QwtInterval & colorRange() const;
+
+    virtual void drawSeries( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    void setPenWidth(double width);
+    double penWidth() const;
+
+protected:
+    virtual void drawDots( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotSpectroCurve::PaintAttributes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_spectrogram.h b/include/pli_vis/third_party/qwt/qwt_plot_spectrogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..2acca3cd4d38ac53c0ceff241eb6edbfb9dc197a
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_spectrogram.h
@@ -0,0 +1,118 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SPECTROGRAM_H
+#define QWT_PLOT_SPECTROGRAM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_raster_data.h"
+#include "qwt/qwt_plot_rasteritem.h"
+#include <qlist.h>
+
+class QwtColorMap;
+
+/*!
+  \brief A plot item, which displays a spectrogram
+
+  A spectrogram displays 3-dimensional data, where the 3rd dimension
+  ( the intensity ) is displayed using colors. The colors are calculated
+  from the values using a color map.
+
+  On multi-core systems the performance of the image composition
+  can often be improved by dividing the area into tiles - each of them
+  rendered in a different thread ( see QwtPlotItem::setRenderThreadCount() ).
+
+  In ContourMode contour lines are painted for the contour levels.
+
+  \image html spectrogram3.png
+
+  \sa QwtRasterData, QwtColorMap, QwtPlotItem::setRenderThreadCount()
+*/
+
+class QWT_EXPORT QwtPlotSpectrogram: public QwtPlotRasterItem
+{
+public:
+    /*!
+      The display mode controls how the raster data will be represented.
+      \sa setDisplayMode(), testDisplayMode()
+    */
+
+    enum DisplayMode
+    {
+        //! The values are mapped to colors using a color map.
+        ImageMode = 0x01,
+
+        //! The data is displayed using contour lines
+        ContourMode = 0x02
+    };
+
+    //! Display modes
+    typedef QFlags<DisplayMode> DisplayModes;
+
+    explicit QwtPlotSpectrogram( const QString &title = QString::null );
+    virtual ~QwtPlotSpectrogram();
+
+    void setDisplayMode( DisplayMode, bool on = true );
+    bool testDisplayMode( DisplayMode ) const;
+
+    void setData( QwtRasterData *data );
+    const QwtRasterData *data() const;
+    QwtRasterData *data();
+
+    void setColorMap( QwtColorMap * );
+    const QwtColorMap *colorMap() const;
+
+    virtual QwtInterval interval(Qt::Axis) const;
+    virtual QRectF pixelHint( const QRectF & ) const;
+
+    void setDefaultContourPen( const QColor &, 
+        qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setDefaultContourPen( const QPen & );
+    QPen defaultContourPen() const;
+
+    virtual QPen contourPen( double level ) const;
+
+    void setConrecFlag( QwtRasterData::ConrecFlag, bool on );
+    bool testConrecFlag( QwtRasterData::ConrecFlag ) const;
+
+    void setContourLevels( const QList<double> & );
+    QList<double> contourLevels() const;
+
+    virtual int rtti() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+protected:
+    virtual QImage renderImage(
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &area, const QSize &imageSize ) const;
+
+    virtual QSize contourRasterSize(
+        const QRectF &, const QRect & ) const;
+
+    virtual QwtRasterData::ContourLines renderContourLines(
+        const QRectF &rect, const QSize &raster ) const;
+
+    virtual void drawContourLines( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtRasterData::ContourLines& lines ) const;
+
+    void renderTile( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRect &imageRect, QImage *image ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotSpectrogram::DisplayModes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_svgitem.h b/include/pli_vis/third_party/qwt/qwt_plot_svgitem.h
new file mode 100644
index 0000000000000000000000000000000000000000..9d6af167410bf56fdd2272c426d374bac06922a3
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_svgitem.h
@@ -0,0 +1,65 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_SVGITEM_H
+#define QWT_PLOT_SVGITEM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include <qstring.h>
+
+#ifndef QT_NO_SVG
+
+class QSvgRenderer;
+class QByteArray;
+
+/*!
+  \brief A plot item, which displays
+         data in Scalable Vector Graphics (SVG) format.
+
+  SVG images are often used to display maps
+*/
+
+class QWT_EXPORT QwtPlotSvgItem: public QwtPlotItem
+{
+public:
+    explicit QwtPlotSvgItem( const QString& title = QString::null );
+    explicit QwtPlotSvgItem( const QwtText& title );
+    virtual ~QwtPlotSvgItem();
+
+    bool loadFile( const QRectF&, const QString &fileName );
+    bool loadData( const QRectF&, const QByteArray & );
+
+    virtual QRectF boundingRect() const;
+
+    virtual void draw( QPainter *p,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &rect ) const;
+
+    virtual int rtti() const;
+
+protected:
+    const QSvgRenderer &renderer() const;
+    QSvgRenderer &renderer();
+
+    void render( QPainter *painter,
+        const QRectF &viewBox, const QRectF &rect ) const;
+
+    QRectF viewBox( const QRectF &area ) const;
+
+private:
+    void init();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_textlabel.h b/include/pli_vis/third_party/qwt/qwt_plot_textlabel.h
new file mode 100644
index 0000000000000000000000000000000000000000..063adf283f336e262a83aeeeefa3e81a7257a5b7
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_textlabel.h
@@ -0,0 +1,75 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_TEXT_LABEL_H
+#define QWT_PLOT_TEXT_LABEL_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_text.h"
+
+/*!
+  \brief A plot item, which displays a text label
+
+  QwtPlotTextLabel displays a text label aligned to the plot canvas.
+
+  In opposite to QwtPlotMarker the position of the label is unrelated to
+  plot coordinates.
+    
+  As drawing a text is an expensive operation the label is cached
+  in a pixmap to speed up replots.
+
+  \par Example
+  The following code shows how to add a title.
+
+\verbatim
+    QwtText title( "Plot Title" );
+    title.setRenderFlags( Qt::AlignHCenter | Qt::AlignTop );
+
+    QFont font;
+    font.setBold( true );
+    title.setFont( font );
+
+    QwtPlotTextLabel *titleItem = new QwtPlotTextLabel();
+    titleItem->setText( title );
+    titleItem->attach( this );
+\endverbatim
+
+  \sa QwtPlotMarker
+*/  
+
+class QWT_EXPORT QwtPlotTextLabel: public QwtPlotItem
+{
+public:
+    QwtPlotTextLabel();
+    virtual ~QwtPlotTextLabel();
+
+    virtual int rtti() const;
+
+    void setText( const QwtText & );
+    QwtText text() const;
+
+    void setMargin( int margin );
+    int margin() const;
+
+    virtual QRectF textRect( const QRectF &, const QSizeF & ) const;
+
+protected:
+    virtual void draw( QPainter *,
+        const QwtScaleMap &, const QwtScaleMap &,
+        const QRectF &) const;
+
+    void invalidateCache();
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_tradingcurve.h b/include/pli_vis/third_party/qwt/qwt_plot_tradingcurve.h
new file mode 100644
index 0000000000000000000000000000000000000000..923a81b58d6e8b716cfe84821a2dd85e3ad87d87
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_tradingcurve.h
@@ -0,0 +1,174 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_TRADING_CURVE_H
+#define QWT_PLOT_TRADING_CURVE_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_seriesitem.h"
+#include "qwt/qwt_series_data.h"
+
+/*!
+  \brief QwtPlotTradingCurve illustrates movements in the price of a
+         financial instrument over time.
+
+  QwtPlotTradingCurve supports candlestick or bar ( OHLC ) charts
+  that are used in the domain of technical analysis.
+
+  While the length ( height or width depending on orientation() ) 
+  of each symbol depends on the corresponding OHLC sample the size
+  of the other dimension can be controlled using:
+
+  - setSymbolExtent()
+  - setSymbolMinWidth()
+  - setSymbolMaxWidth()
+
+  The extent is a size in scale coordinates, so that the symbol width
+  is increasing when the plot is zoomed in. Minimum/Maximum width
+  is in widget coordinates independent from the zoom level. 
+  When setting the minimum and maximum to the same value, the width of 
+  the symbol is fixed. 
+*/
+class QWT_EXPORT QwtPlotTradingCurve: 
+    public QwtPlotSeriesItem, QwtSeriesStore<QwtOHLCSample>
+{
+public:
+    /*!
+        \brief Symbol styles.
+
+        The default setting is QwtPlotSeriesItem::CandleStick.
+        \sa setSymbolStyle(), symbolStyle()
+    */
+    enum SymbolStyle
+    {
+        //! Nothing is displayed
+        NoSymbol = -1,
+
+        /*!
+          A line on the chart shows the price range (the highest and lowest
+          prices) over one unit of time, e.g. one day or one hour.
+          Tick marks project from each side of the line indicating the
+          opening and closing price.
+         */
+        Bar,
+
+        /*!
+          The range between opening/closing price are displayed as
+          a filled box. The fill brush depends on the direction of the
+          price movement. The box is connected to the highest/lowest
+          values by lines.
+        */
+        CandleStick,
+
+        /*!
+          SymbolTypes >= UserSymbol are displayed by drawUserSymbol(),
+          that needs to be overloaded and implemented in derived
+          curve classes.
+
+          \sa drawUserSymbol()
+        */
+        UserSymbol = 100
+    };
+
+    /*!
+        \brief Direction of a price movement
+     */
+    enum Direction
+    {
+        //! The closing price is higher than the opening price
+        Increasing,
+
+        //! The closing price is lower than the opening price
+        Decreasing
+    };
+
+    /*!
+        Attributes to modify the drawing algorithm.
+        \sa setPaintAttribute(), testPaintAttribute()
+    */
+    enum PaintAttribute
+    {
+        //! Check if a symbol is on the plot canvas before painting it.
+        ClipSymbols   = 0x01
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    explicit QwtPlotTradingCurve( const QString &title = QString::null );
+    explicit QwtPlotTradingCurve( const QwtText &title );
+
+    virtual ~QwtPlotTradingCurve();
+
+    virtual int rtti() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setSamples( const QVector<QwtOHLCSample> & );
+    void setSamples( QwtSeriesData<QwtOHLCSample> * );
+
+    void setSymbolStyle( SymbolStyle style );
+    SymbolStyle symbolStyle() const;
+
+    void setSymbolPen( const QColor &, 
+        qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setSymbolPen( const QPen & );
+    QPen symbolPen() const;
+
+    void setSymbolBrush( Direction, const QBrush & );
+    QBrush symbolBrush( Direction ) const;
+
+    void setSymbolExtent( double width );
+    double symbolExtent() const;
+
+    void setMinSymbolWidth( double );
+    double minSymbolWidth() const;
+
+    void setMaxSymbolWidth( double );
+    double maxSymbolWidth() const;
+
+    virtual void drawSeries( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual QRectF boundingRect() const;
+
+    virtual QwtGraphic legendIcon( int index, const QSizeF & ) const;
+
+protected:
+
+    void init();
+
+    virtual void drawSymbols( QPainter *,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect, int from, int to ) const;
+
+    virtual void drawUserSymbol( QPainter *, 
+        SymbolStyle, const QwtOHLCSample &,
+        Qt::Orientation, bool inverted, double width ) const;
+
+    void drawBar( QPainter *painter, const QwtOHLCSample &, 
+        Qt::Orientation, bool inverted, double width ) const;
+
+    void drawCandleStick( QPainter *, const QwtOHLCSample &, 
+        Qt::Orientation, double width ) const;
+
+    virtual double scaledSymbolWidth(
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotTradingCurve::PaintAttributes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_zoneitem.h b/include/pli_vis/third_party/qwt/qwt_plot_zoneitem.h
new file mode 100644
index 0000000000000000000000000000000000000000..a79ce6e4eb086ef9a64baa7a734ae392885cd5f1
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_zoneitem.h
@@ -0,0 +1,65 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_ZONE_ITEM_H
+#define QWT_PLOT_ZONE_ITEM_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_interval.h"
+
+class QPen;
+class QBrush;
+
+/*!
+  \brief A plot item, which displays a zone
+
+  A horizontal zone highlights an interval of the y axis - a vertical 
+  zone an interval of the x axis - and is unbounded in the opposite direction.
+  It is filled with a brush and its border lines are optionally displayed with a pen. 
+
+  \note For displaying an area that is bounded for x and y coordinates 
+        use QwtPlotShapeItem
+*/
+
+class QWT_EXPORT QwtPlotZoneItem: 
+    public QwtPlotItem
+{
+public:
+    explicit QwtPlotZoneItem();
+    virtual ~QwtPlotZoneItem();
+
+    virtual int rtti() const;
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation();
+
+    void setInterval( double min, double max );
+    void setInterval( const QwtInterval & );
+    QwtInterval interval() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen &pen() const;
+
+    void setBrush( const QBrush & );
+    const QBrush &brush() const;
+
+    virtual void draw( QPainter *,
+        const QwtScaleMap &, const QwtScaleMap &,
+        const QRectF &) const;
+
+    virtual QRectF boundingRect() const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_plot_zoomer.h b/include/pli_vis/third_party/qwt/qwt_plot_zoomer.h
new file mode 100644
index 0000000000000000000000000000000000000000..eab37c8d814785aa5436cc27232cd595333b0123
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_plot_zoomer.h
@@ -0,0 +1,140 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_PLOT_ZOOMER_H
+#define QWT_PLOT_ZOOMER_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_plot_picker.h"
+#include <qstack.h>
+
+/*!
+  \brief QwtPlotZoomer provides stacked zooming for a plot widget
+
+  QwtPlotZoomer selects rectangles from user inputs ( mouse or keyboard )
+  translates them into plot coordinates and adjusts the axes to them.
+  The selection is supported by a rubber band and optionally by displaying
+  the coordinates of the current mouse position.
+
+  Zooming can be repeated as often as possible, limited only by 
+  maxStackDepth() or minZoomSize().  Each rectangle is pushed on a stack.
+
+  The default setting how to select rectangles is 
+  a QwtPickerDragRectMachine with the following bindings:
+
+  - QwtEventPattern::MouseSelect1\n
+    The first point of the zoom rectangle is selected by a mouse press, 
+    the second point from the position, where the mouse is released.
+
+  - QwtEventPattern::KeySelect1\n
+    The first key press selects the first, the second key press
+    selects the second point.
+
+  - QwtEventPattern::KeyAbort\n
+    Discard the selection in the state, where the first point
+    is selected.
+
+  To traverse the zoom stack the following bindings are used:
+
+  - QwtEventPattern::MouseSelect3, QwtEventPattern::KeyUndo\n
+    Zoom out one position on the zoom stack
+    
+  - QwtEventPattern::MouseSelect6, QwtEventPattern::KeyRedo\n
+    Zoom in one position on the zoom stack
+
+  - QwtEventPattern::MouseSelect2, QwtEventPattern::KeyHome\n
+    Zoom to the zoom base
+
+  The setKeyPattern() and setMousePattern() functions can be used
+  to configure the zoomer actions. The following example 
+  shows, how to configure the 'I' and 'O' keys for zooming in and out 
+  one position on the zoom stack. The "Home" key is used to 
+  "unzoom" the plot.
+
+  \code
+   zoomer = new QwtPlotZoomer( plot );
+   zoomer->setKeyPattern( QwtEventPattern::KeyRedo, Qt::Key_I, Qt::ShiftModifier );
+   zoomer->setKeyPattern( QwtEventPattern::KeyUndo, Qt::Key_O, Qt::ShiftModifier );
+   zoomer->setKeyPattern( QwtEventPattern::KeyHome, Qt::Key_Home );
+  \endcode
+
+  QwtPlotZoomer is tailored for plots with one x and y axis, but it is
+  allowed to attach a second QwtPlotZoomer ( without rubber band and tracker )
+  for the other axes.
+
+  \note The realtime example includes an derived zoomer class that adds
+        scrollbars to the plot canvas.
+
+  \sa QwtPlotPanner, QwtPlotMagnifier
+*/
+
+class QWT_EXPORT QwtPlotZoomer: public QwtPlotPicker
+{
+    Q_OBJECT
+public:
+    explicit QwtPlotZoomer( QWidget *, bool doReplot = true );
+    explicit QwtPlotZoomer( int xAxis, int yAxis,
+                            QWidget *, bool doReplot = true );
+
+    virtual ~QwtPlotZoomer();
+
+    virtual void setZoomBase( bool doReplot = true );
+    virtual void setZoomBase( const QRectF & );
+
+    QRectF zoomBase() const;
+    QRectF zoomRect() const;
+
+    virtual void setAxis( int xAxis, int yAxis );
+
+    void setMaxStackDepth( int );
+    int maxStackDepth() const;
+
+    const QStack<QRectF> &zoomStack() const;
+    void setZoomStack( const QStack<QRectF> &,
+        int zoomRectIndex = -1 );
+
+    uint zoomRectIndex() const;
+
+public Q_SLOTS:
+    void moveBy( double x, double y );
+    virtual void moveTo( const QPointF & );
+
+    virtual void zoom( const QRectF & );
+    virtual void zoom( int up );
+
+Q_SIGNALS:
+    /*!
+      A signal emitting the zoomRect(), when the plot has been
+      zoomed in or out.
+
+      \param rect Current zoom rectangle.
+    */
+
+    void zoomed( const QRectF &rect );
+
+protected:
+    virtual void rescale();
+
+    virtual QSizeF minZoomSize() const;
+
+    virtual void widgetMouseReleaseEvent( QMouseEvent * );
+    virtual void widgetKeyPressEvent( QKeyEvent * );
+
+    virtual void begin();
+    virtual bool end( bool ok = true );
+    virtual bool accept( QPolygon & ) const;
+
+private:
+    void init( bool doReplot );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_point_3d.h b/include/pli_vis/third_party/qwt/qwt_point_3d.h
new file mode 100644
index 0000000000000000000000000000000000000000..af8dbc39a855f11e858dc2369b8bece599bdfab8
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_point_3d.h
@@ -0,0 +1,189 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+/*! \file */
+#ifndef QWT_POINT_3D_H
+#define QWT_POINT_3D_H 1
+
+#include "qwt/qwt_global.h"
+#include <qpoint.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+/*!
+  \brief QwtPoint3D class defines a 3D point in double coordinates
+*/
+
+class QWT_EXPORT QwtPoint3D
+{
+public:
+    QwtPoint3D();
+    QwtPoint3D( double x, double y, double z );
+    QwtPoint3D( const QwtPoint3D & );
+    QwtPoint3D( const QPointF & );
+
+    bool isNull()    const;
+
+    double x() const;
+    double y() const;
+    double z() const;
+
+    double &rx();
+    double &ry();
+    double &rz();
+
+    void setX( double x );
+    void setY( double y );
+    void setZ( double y );
+
+    QPointF toPoint() const;
+
+    bool operator==( const QwtPoint3D & ) const;
+    bool operator!=( const QwtPoint3D & ) const;
+
+private:
+    double d_x;
+    double d_y;
+    double d_z;
+};
+
+Q_DECLARE_TYPEINFO(QwtPoint3D, Q_MOVABLE_TYPE);
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtPoint3D & );
+#endif
+
+/*!
+    Constructs a null point.
+    \sa isNull()
+*/
+inline QwtPoint3D::QwtPoint3D():
+    d_x( 0.0 ),
+    d_y( 0.0 ),
+    d_z( 0.0 )
+{
+}
+
+//! Constructs a point with coordinates specified by x, y and z.
+inline QwtPoint3D::QwtPoint3D( double x, double y, double z = 0.0 ):
+    d_x( x ),
+    d_y( y ),
+    d_z( z )
+{
+}
+
+/*!
+    Copy constructor.
+    Constructs a point using the values of the point specified.
+*/
+inline QwtPoint3D::QwtPoint3D( const QwtPoint3D &other ):
+    d_x( other.d_x ),
+    d_y( other.d_y ),
+    d_z( other.d_z )
+{
+}
+
+/*!
+    Constructs a point with x and y coordinates from a 2D point,
+    and a z coordinate of 0.
+*/
+inline QwtPoint3D::QwtPoint3D( const QPointF &other ):
+    d_x( other.x() ),
+    d_y( other.y() ),
+    d_z( 0.0 )
+{
+}
+
+/*!
+    \return True if the point is null; otherwise returns false.
+
+    A point is considered to be null if x, y and z-coordinates
+    are equal to zero.
+*/
+inline bool QwtPoint3D::isNull() const
+{
+    return d_x == 0.0 && d_y == 0.0 && d_z == 0.0;
+}
+
+//! \return The x-coordinate of the point.
+inline double QwtPoint3D::x() const
+{
+    return d_x;
+}
+
+//! \return The y-coordinate of the point.
+inline double QwtPoint3D::y() const
+{
+    return d_y;
+}
+
+//! \return The z-coordinate of the point.
+inline double QwtPoint3D::z() const
+{
+    return d_z;
+}
+
+//! \return A reference to the x-coordinate of the point.
+inline double &QwtPoint3D::rx()
+{
+    return d_x;
+}
+
+//! \return A reference to the y-coordinate of the point.
+inline double &QwtPoint3D::ry()
+{
+    return d_y;
+}
+
+//! \return A reference to the z-coordinate of the point.
+inline double &QwtPoint3D::rz()
+{
+    return d_z;
+}
+
+//! Sets the x-coordinate of the point to the value specified by x.
+inline void QwtPoint3D::setX( double x )
+{
+    d_x = x;
+}
+
+//! Sets the y-coordinate of the point to the value specified by y.
+inline void QwtPoint3D::setY( double y )
+{
+    d_y = y;
+}
+
+//! Sets the z-coordinate of the point to the value specified by z.
+inline void QwtPoint3D::setZ( double z )
+{
+    d_z = z;
+}
+
+/*!
+   \return 2D point, where the z coordinate is dropped.
+*/
+inline QPointF QwtPoint3D::toPoint() const
+{
+    return QPointF( d_x, d_y );
+}
+
+//! \return True, if this point and other are equal; otherwise returns false.
+inline bool QwtPoint3D::operator==( const QwtPoint3D &other ) const
+{
+    return ( d_x == other.d_x ) && ( d_y == other.d_y ) && ( d_z == other.d_z );
+}
+
+//! \return True if this rect and other are different; otherwise returns false.
+inline bool QwtPoint3D::operator!=( const QwtPoint3D &other ) const
+{
+    return !operator==( other );
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_point_data.h b/include/pli_vis/third_party/qwt/qwt_point_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..e914d06667e15c3a144b65ead4d28639d861d775
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_point_data.h
@@ -0,0 +1,146 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_POINT_DATA_H
+#define QWT_POINT_DATA_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_series_data.h"
+
+/*!
+  \brief Interface for iterating over two QVector<double> objects.
+*/
+class QWT_EXPORT QwtPointArrayData: public QwtSeriesData<QPointF>
+{
+public:
+    QwtPointArrayData( const QVector<double> &x, const QVector<double> &y );
+    QwtPointArrayData( const double *x, const double *y, size_t size );
+
+    virtual QRectF boundingRect() const;
+
+    virtual size_t size() const;
+    virtual QPointF sample( size_t i ) const;
+
+    const QVector<double> &xData() const;
+    const QVector<double> &yData() const;
+
+private:
+    QVector<double> d_x;
+    QVector<double> d_y;
+};
+
+/*!
+  \brief Data class containing two pointers to memory blocks of doubles.
+ */
+class QWT_EXPORT QwtCPointerData: public QwtSeriesData<QPointF>
+{
+public:
+    QwtCPointerData( const double *x, const double *y, size_t size );
+
+    virtual QRectF boundingRect() const;
+    virtual size_t size() const;
+    virtual QPointF sample( size_t i ) const;
+
+    const double *xData() const;
+    const double *yData() const;
+
+private:
+    const double *d_x;
+    const double *d_y;
+    size_t d_size;
+};
+
+/*!
+  \brief Synthetic point data
+
+  QwtSyntheticPointData provides a fixed number of points for an interval.
+  The points are calculated in equidistant steps in x-direction.
+
+  If the interval is invalid, the points are calculated for
+  the "rectangle of interest", what normally is the displayed area on the
+  plot canvas. In this mode you get different levels of detail, when
+  zooming in/out.
+
+  \par Example
+
+  The following example shows how to implement a sinus curve.
+
+  \code
+#include <cmath>
+#include <qwt_series_data.h>
+#include <qwt_plot_curve.h>
+#include <qwt_plot.h>
+#include <qapplication.h>
+
+class SinusData: public QwtSyntheticPointData
+{
+public:
+    SinusData():
+        QwtSyntheticPointData( 100 )
+    {
+    }
+
+    virtual double y( double x ) const
+    {
+        return qSin( x );
+    }
+};
+
+int main(int argc, char **argv)
+{
+    QApplication a( argc, argv );
+
+    QwtPlot plot;
+    plot.setAxisScale( QwtPlot::xBottom, 0.0, 10.0 );
+    plot.setAxisScale( QwtPlot::yLeft, -1.0, 1.0 );
+
+    QwtPlotCurve *curve = new QwtPlotCurve( "y = sin(x)" );
+    curve->setData( new SinusData() );
+    curve->attach( &plot );
+
+    plot.show();
+    return a.exec();
+}
+   \endcode
+*/
+class QWT_EXPORT QwtSyntheticPointData: public QwtSeriesData<QPointF>
+{
+public:
+    QwtSyntheticPointData( size_t size,
+        const QwtInterval & = QwtInterval() );
+
+    void setSize( size_t size );
+    virtual size_t size() const;
+
+    void setInterval( const QwtInterval& );
+    QwtInterval interval() const;
+
+    virtual QRectF boundingRect() const;
+    virtual QPointF sample( size_t i ) const;
+
+    /*!
+       Calculate a y value for a x value
+
+       \param x x value
+       \return Corresponding y value
+     */
+    virtual double y( double x ) const = 0;
+    virtual double x( uint index ) const;
+
+    virtual void setRectOfInterest( const QRectF & );
+    QRectF rectOfInterest() const;
+
+private:
+    size_t d_size;
+    QwtInterval d_interval;
+    QRectF d_rectOfInterest;
+    QwtInterval d_intervalOfInterest;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_point_mapper.h b/include/pli_vis/third_party/qwt/qwt_point_mapper.h
new file mode 100644
index 0000000000000000000000000000000000000000..d2931df0d5aab9509418cd536287d053e53dcefb
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_point_mapper.h
@@ -0,0 +1,89 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_POINT_MAPPER_H
+#define QWT_POINT_MAPPER_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_series_data.h"
+#include <qimage.h>
+
+class QwtScaleMap;
+class QPolygonF;
+class QPolygon;
+
+/*!
+  \brief A helper class for translating a series of points
+
+  QwtPointMapper is a collection of methods and optimizations
+  for translating a series of points into paint device coordinates. 
+  It is used by QwtPlotCurve but might also be useful for 
+  similar plot items displaying a QwtSeriesData<QPointF>.
+ */
+class QWT_EXPORT QwtPointMapper
+{
+public:
+    /*!  
+      \brief Flags affecting the transformation process
+      \sa setFlag(), setFlags()
+     */
+    enum TransformationFlag
+    {
+        //! Round points to integer values
+        RoundPoints = 0x01,
+
+        /*! 
+          Try to remove points, that are translated to the
+          same position.
+         */
+        WeedOutPoints = 0x02
+    };
+
+    /*!  
+      \brief Flags affecting the transformation process
+      \sa setFlag(), setFlags()
+     */
+    typedef QFlags<TransformationFlag> TransformationFlags;
+
+    QwtPointMapper();
+    ~QwtPointMapper();
+
+    void setFlags( TransformationFlags );
+    TransformationFlags flags() const;
+
+    void setFlag( TransformationFlag, bool on = true );
+    bool testFlag( TransformationFlag ) const;
+
+    void setBoundingRect( const QRectF & );
+    QRectF boundingRect() const;
+
+    QPolygonF toPolygonF( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to ) const;
+
+    QPolygon toPolygon( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to ) const;
+
+    QPolygon toPoints( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to ) const;
+
+    QPolygonF toPointsF( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to ) const;
+
+    QImage toImage( const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtSeriesData<QPointF> *series, int from, int to, 
+        const QPen &, bool antialiased, uint numThreads ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPointMapper::TransformationFlags )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_point_polar.h b/include/pli_vis/third_party/qwt/qwt_point_polar.h
new file mode 100644
index 0000000000000000000000000000000000000000..d141811c43ee75795c04b04523010663197d2b34
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_point_polar.h
@@ -0,0 +1,201 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+/*! \file */
+#ifndef _QWT_POINT_POLAR_H_
+#define _QWT_POINT_POLAR_H_ 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_math.h"
+#include <qpoint.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+/*!
+  \brief A point in polar coordinates
+
+  In polar coordinates a point is determined by an angle and a distance.
+  See http://en.wikipedia.org/wiki/Polar_coordinate_system
+*/
+
+class QWT_EXPORT QwtPointPolar
+{
+public:
+    QwtPointPolar();
+    QwtPointPolar( double azimuth, double radius );
+    QwtPointPolar( const QwtPointPolar & );
+    QwtPointPolar( const QPointF & );
+
+    void setPoint( const QPointF & );
+    QPointF toPoint() const;
+
+    bool isValid() const;
+    bool isNull() const;
+
+    double radius() const;
+    double azimuth() const;
+
+    double &rRadius();
+    double &rAzimuth();
+
+    void setRadius( double );
+    void setAzimuth( double );
+
+    bool operator==( const QwtPointPolar & ) const;
+    bool operator!=( const QwtPointPolar & ) const;
+
+    QwtPointPolar normalized() const;
+
+private:
+    double d_azimuth;
+    double d_radius;
+};
+
+/*!
+    Constructs a null point, with a radius and azimuth set to 0.0.
+    \sa QPointF::isNull()
+*/
+inline QwtPointPolar::QwtPointPolar():
+    d_azimuth( 0.0 ),
+    d_radius( 0.0 )
+{
+}
+
+/*!
+   Constructs a point with coordinates specified by radius and azimuth.
+
+   \param azimuth Azimuth
+   \param radius Radius
+*/
+inline QwtPointPolar::QwtPointPolar( double azimuth, double radius ):
+    d_azimuth( azimuth ),
+    d_radius( radius )
+{
+}
+
+/*!
+    Constructs a point using the values of the point specified.
+    \param other Other point
+*/
+inline QwtPointPolar::QwtPointPolar( const QwtPointPolar &other ):
+    d_azimuth( other.d_azimuth ),
+    d_radius( other.d_radius )
+{
+}
+
+//! Returns true if radius() >= 0.0
+inline bool QwtPointPolar::isValid() const
+{
+    return d_radius >= 0.0;
+}
+
+//! Returns true if radius() >= 0.0
+inline bool QwtPointPolar::isNull() const
+{
+    return d_radius == 0.0;
+}
+
+//! Returns the radius.
+inline double QwtPointPolar::radius() const
+{
+    return d_radius;
+}
+
+//! Returns the azimuth.
+inline double QwtPointPolar::azimuth() const
+{
+    return d_azimuth;
+}
+
+//! Returns the radius.
+inline double &QwtPointPolar::rRadius()
+{
+    return d_radius;
+}
+
+//! Returns the azimuth.
+inline double &QwtPointPolar::rAzimuth()
+{
+    return d_azimuth;
+}
+
+//! Sets the radius to radius.
+inline void QwtPointPolar::setRadius( double radius )
+{
+    d_radius = radius;
+}
+
+//! Sets the atimuth to atimuth.
+inline void QwtPointPolar::setAzimuth( double azimuth )
+{
+    d_azimuth = azimuth;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtPointPolar & );
+#endif
+
+inline QPoint qwtPolar2Pos( const QPoint &pole,
+    double radius, double angle )
+{
+    const double x = pole.x() + radius * qCos( angle );
+    const double y = pole.y() - radius * qSin( angle );
+
+    return QPoint( qRound( x ), qRound( y ) );
+}
+
+inline QPoint qwtDegree2Pos( const QPoint &pole,
+    double radius, double angle )
+{
+    return qwtPolar2Pos( pole, radius, angle / 180.0 * M_PI );
+}
+
+inline QPointF qwtPolar2Pos( const QPointF &pole,
+    double radius, double angle )
+{
+    const double x = pole.x() + radius * qCos( angle );
+    const double y = pole.y() - radius * qSin( angle );
+
+    return QPointF( x, y);
+}
+
+inline QPointF qwtDegree2Pos( const QPointF &pole,
+    double radius, double angle )
+{
+    return qwtPolar2Pos( pole, radius, angle / 180.0 * M_PI );
+}
+
+inline QPointF qwtFastPolar2Pos( const QPointF &pole,
+    double radius, double angle )
+{
+#if QT_VERSION < 0x040601
+    const double x = pole.x() + radius * ::cos( angle );
+    const double y = pole.y() - radius * ::sin( angle );
+#else
+    const double x = pole.x() + radius * qFastCos( angle );
+    const double y = pole.y() - radius * qFastSin( angle );
+#endif
+
+    return QPointF( x, y);
+}
+
+inline QPointF qwtFastDegree2Pos( const QPointF &pole,
+    double radius, double angle )
+{   
+    return qwtFastPolar2Pos( pole, radius, angle / 180.0 * M_PI );
+} 
+
+inline QwtPointPolar qwtFastPos2Polar( const QPointF &pos )
+{
+    return QwtPointPolar( qwtFastAtan2( pos.y(), pos.x() ),
+        qSqrt( qwtSqr( pos.x() ) + qwtSqr( pos.y() ) ) );
+}
+
+#endif 
diff --git a/include/pli_vis/third_party/qwt/qwt_raster_data.h b/include/pli_vis/third_party/qwt/qwt_raster_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..754b7b1ca5e469af03431d27835bf6c5ab59b4fb
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_raster_data.h
@@ -0,0 +1,95 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_RASTER_DATA_H
+#define QWT_RASTER_DATA_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_interval.h"
+#include <qmap.h>
+#include <qlist.h>
+#include <qpolygon.h>
+
+class QwtScaleMap;
+
+/*!
+  \brief QwtRasterData defines an interface to any type of raster data.
+
+  QwtRasterData is an abstract interface, that is used by
+  QwtPlotRasterItem to find the values at the pixels of its raster.
+
+  Often a raster item is used to display values from a matrix. Then the
+  derived raster data class needs to implement some sort of resampling,
+  that maps the raster of the matrix into the requested raster of
+  the raster item ( depending on resolution and scales of the canvas ).
+*/
+class QWT_EXPORT QwtRasterData
+{
+public:
+    //! Contour lines
+    typedef QMap<double, QPolygonF> ContourLines;
+
+    //! Flags to modify the contour algorithm
+    enum ConrecFlag
+    {
+        //! Ignore all vertices on the same level
+        IgnoreAllVerticesOnLevel = 0x01,
+
+        //! Ignore all values, that are out of range
+        IgnoreOutOfRange = 0x02
+    };
+
+    //! Flags to modify the contour algorithm
+    typedef QFlags<ConrecFlag> ConrecFlags;
+
+    QwtRasterData();
+    virtual ~QwtRasterData();
+
+    virtual void setInterval( Qt::Axis, const QwtInterval & );
+    const QwtInterval &interval(Qt::Axis) const;
+
+    virtual QRectF pixelHint( const QRectF & ) const;
+
+    virtual void initRaster( const QRectF &, const QSize& raster );
+    virtual void discardRaster();
+
+    /*!
+       \return the value at a raster position
+       \param x X value in plot coordinates
+       \param y Y value in plot coordinates
+    */
+    virtual double value( double x, double y ) const = 0;
+
+    virtual ContourLines contourLines( const QRectF &rect,
+        const QSize &raster, const QList<double> &levels,
+        ConrecFlags ) const;
+
+    class Contour3DPoint;
+    class ContourPlane;
+
+private:
+    // Disabled copy constructor and operator=
+    QwtRasterData( const QwtRasterData & );
+    QwtRasterData &operator=( const QwtRasterData & );
+
+    QwtInterval d_intervals[3];
+};
+
+/*!
+   \return Bounding interval for a axis
+   \sa setInterval
+*/
+inline const QwtInterval &QwtRasterData::interval( Qt::Axis axis) const
+{
+    return d_intervals[axis];
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtRasterData::ConrecFlags )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_round_scale_draw.h b/include/pli_vis/third_party/qwt/qwt_round_scale_draw.h
new file mode 100644
index 0000000000000000000000000000000000000000..346b12a33325a6325e92c9b4e66a4d6f746a1e2f
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_round_scale_draw.h
@@ -0,0 +1,66 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_ROUND_SCALE_DRAW_H
+#define QWT_ROUND_SCALE_DRAW_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_abstract_scale_draw.h"
+#include <qpoint.h>
+
+/*!
+  \brief A class for drawing round scales
+
+  QwtRoundScaleDraw can be used to draw round scales.
+  The circle segment can be adjusted by setAngleRange().
+  The geometry of the scale can be specified with
+  moveCenter() and setRadius().
+
+  After a scale division has been specified as a QwtScaleDiv object
+  using QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &s),
+  the scale can be drawn with the QwtAbstractScaleDraw::draw() member.
+*/
+
+class QWT_EXPORT QwtRoundScaleDraw: public QwtAbstractScaleDraw
+{
+public:
+    QwtRoundScaleDraw();
+    virtual ~QwtRoundScaleDraw();
+
+    void setRadius( double radius );
+    double radius() const;
+
+    void moveCenter( double x, double y );
+    void moveCenter( const QPointF & );
+    QPointF center() const;
+
+    void setAngleRange( double angle1, double angle2 );
+
+    virtual double extent( const QFont & ) const;
+
+protected:
+    virtual void drawTick( QPainter *, double val, double len ) const;
+    virtual void drawBackbone( QPainter * ) const;
+    virtual void drawLabel( QPainter *, double val ) const;
+
+private:
+    QwtRoundScaleDraw( const QwtRoundScaleDraw & );
+    QwtRoundScaleDraw &operator=( const QwtRoundScaleDraw &other );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+//! Move the center of the scale draw, leaving the radius unchanged
+inline void QwtRoundScaleDraw::moveCenter( double x, double y )
+{
+    moveCenter( QPointF( x, y ) );
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_samples.h b/include/pli_vis/third_party/qwt/qwt_samples.h
new file mode 100644
index 0000000000000000000000000000000000000000..0c26438da364ab9a943298a499db91d1f6cda4a2
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_samples.h
@@ -0,0 +1,239 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SAMPLES_H
+#define QWT_SAMPLES_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_interval.h"
+#include <qvector.h>
+#include <qrect.h>
+
+//! \brief A sample of the types (x1-x2, y) or (x, y1-y2)
+class QWT_EXPORT QwtIntervalSample
+{
+public:
+    QwtIntervalSample();
+    QwtIntervalSample( double, const QwtInterval & );
+    QwtIntervalSample( double value, double min, double max );
+
+    bool operator==( const QwtIntervalSample & ) const;
+    bool operator!=( const QwtIntervalSample & ) const;
+
+    //! Value
+    double value;
+
+    //! Interval
+    QwtInterval interval;
+};
+
+/*!
+  Constructor
+  The value is set to 0.0, the interval is invalid
+*/
+inline QwtIntervalSample::QwtIntervalSample():
+    value( 0.0 )
+{
+}
+
+//! Constructor
+inline QwtIntervalSample::QwtIntervalSample(
+        double v, const QwtInterval &intv ):
+    value( v ),
+    interval( intv )
+{
+}
+
+//! Constructor
+inline QwtIntervalSample::QwtIntervalSample(
+        double v, double min, double max ):
+    value( v ),
+    interval( min, max )
+{
+}
+
+//! Compare operator
+inline bool QwtIntervalSample::operator==(
+    const QwtIntervalSample &other ) const
+{
+    return value == other.value && interval == other.interval;
+}
+
+//! Compare operator
+inline bool QwtIntervalSample::operator!=(
+    const QwtIntervalSample &other ) const
+{
+    return !( *this == other );
+}
+
+//! \brief A sample of the types (x1...xn, y) or (x, y1..yn)
+class QWT_EXPORT QwtSetSample
+{
+public:
+    QwtSetSample();
+    QwtSetSample( double, const QVector<double> & = QVector<double>() );
+
+    bool operator==( const QwtSetSample &other ) const;
+    bool operator!=( const QwtSetSample &other ) const;
+
+    double added() const;
+
+    //! value
+    double value;
+
+    //! Vector of values associated to value
+    QVector<double> set;
+};
+
+/*!
+  Constructor
+  The value is set to 0.0
+*/
+inline QwtSetSample::QwtSetSample():
+    value( 0.0 )
+{
+}
+
+/*!
+  Constructor
+
+  \param v Value
+  \param s Set of values
+*/
+inline QwtSetSample::QwtSetSample( double v, const QVector< double > &s ):
+    value( v ),
+    set( s )
+{
+}
+
+//! Compare operator
+inline bool QwtSetSample::operator==( const QwtSetSample &other ) const
+{
+    return value == other.value && set == other.set;
+}
+
+//! Compare operator
+inline bool QwtSetSample::operator!=( const QwtSetSample &other ) const
+{
+    return !( *this == other );
+}
+
+//! \return All values of the set added
+inline double QwtSetSample::added() const
+{
+    double y = 0.0;
+    for ( int i = 0; i < set.size(); i++ )
+        y += set[i];
+
+    return y;
+}
+
+/*!
+   \brief Open-High-Low-Close sample used in financial charts
+
+   In financial charts the movement of a price in a time interval is often
+   represented by the opening/closing prices and the lowest/highest prices
+   in this interval.
+
+   \sa QwtTradingChartData
+*/
+class QWT_EXPORT QwtOHLCSample
+{
+public:
+    QwtOHLCSample( double time = 0.0,
+        double open = 0.0, double high = 0.0,
+        double low = 0.0, double close = 0.0 );
+
+    QwtInterval boundingInterval() const;
+
+    bool isValid() const;
+
+    /*!
+      Time of the sample, usually a number representing
+      a specific interval - like a day.
+    */
+    double time;
+
+    //! Opening price
+    double open;
+
+    //! Highest price
+    double high;
+
+    //! Lowest price
+    double low;
+
+    //! Closing price
+    double close;
+};
+
+
+/*!
+  Constructor
+
+  \param t Time value
+  \param o Open value
+  \param h High value
+  \param l Low value
+  \param c Close value
+*/
+inline QwtOHLCSample::QwtOHLCSample( double t,
+        double o, double h, double l, double c ):
+    time( t ),
+    open( o ),
+    high( h ),
+    low( l ),
+    close( c )
+{
+}
+
+/*!
+  \brief Check if a sample is valid
+
+  A sample is valid, when all of the following checks are true:
+
+  - low <= high
+  - low <= open <= high
+  - low <= close <= high
+
+  \return True, when the sample is valid
+ */
+inline bool QwtOHLCSample::isValid() const
+{
+    return ( low <= high )
+        && ( open >= low )
+        && ( open <= high )
+        && ( close >= low )
+        && ( close <= high );
+}
+
+/*!
+   \brief Calculate the bounding interval of the OHLC values
+
+   For valid samples the limits of this interval are always low/high.
+
+   \return Bounding interval
+   \sa isValid()
+ */
+inline QwtInterval QwtOHLCSample::boundingInterval() const
+{
+    double minY = open;
+    minY = qMin( minY, high );
+    minY = qMin( minY, low );
+    minY = qMin( minY, close );
+
+    double maxY = open;
+    maxY = qMax( maxY, high );
+    maxY = qMax( maxY, low );
+    maxY = qMax( maxY, close );
+
+    return QwtInterval( minY, maxY );
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_sampling_thread.h b/include/pli_vis/third_party/qwt/qwt_sampling_thread.h
new file mode 100644
index 0000000000000000000000000000000000000000..f73bd94476295141a1d109fb0b584e97b5b05551
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_sampling_thread.h
@@ -0,0 +1,50 @@
+#ifndef _QWT_SAMPLING_THREAD_H_
+#define _QWT_SAMPLING_THREAD_H_
+
+#include "qwt/qwt_global.h"
+#include <qthread.h>
+
+/*!
+  \brief A thread collecting samples at regular intervals.
+
+  Continuous signals are converted into a discrete signal by
+  collecting samples at regular intervals. A discrete signal
+  can be displayed by a QwtPlotSeriesItem on a QwtPlot widget.
+
+  QwtSamplingThread starts a thread calling periodically sample(),
+  to collect and store ( or emit ) a single sample.
+
+  \sa QwtPlotCurve, QwtPlotSeriesItem
+*/
+class QWT_EXPORT QwtSamplingThread: public QThread
+{
+    Q_OBJECT
+
+public:
+    virtual ~QwtSamplingThread();
+
+    double interval() const;
+    double elapsed() const;
+
+public Q_SLOTS:
+    void setInterval( double interval );
+    void stop();
+
+protected:
+    explicit QwtSamplingThread( QObject *parent = NULL );
+
+    virtual void run();
+
+    /*!
+       Collect a sample
+
+       \param elapsed Time since the thread was started in milliseconds
+     */
+    virtual void sample( double elapsed ) = 0;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_scale_div.h b/include/pli_vis/third_party/qwt/qwt_scale_div.h
new file mode 100644
index 0000000000000000000000000000000000000000..1b912cb7d12eea03c2bb35881a2addc464918cec
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_scale_div.h
@@ -0,0 +1,110 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_DIV_H
+#define QWT_SCALE_DIV_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_interval.h"
+#include <qlist.h>
+
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+/*!
+  \brief A class representing a scale division
+
+  A Qwt scale is defined by its boundaries and 3 list
+  for the positions of the major, medium and minor ticks.
+
+  The upperBound() might be smaller than the lowerBound()
+  to indicate inverted scales.
+
+  Scale divisions can be calculated from a QwtScaleEngine.
+
+  \sa QwtScaleEngine::divideScale(), QwtPlot::setAxisScaleDiv(),
+      QwtAbstractSlider::setScaleDiv()
+*/
+
+class QWT_EXPORT QwtScaleDiv
+{
+public:
+    //! Scale tick types
+    enum TickType
+    {
+        //! No ticks
+        NoTick = -1,
+
+        //! Minor ticks
+        MinorTick,
+
+        //! Medium ticks
+        MediumTick,
+
+        //! Major ticks
+        MajorTick,
+
+        //! Number of valid tick types
+        NTickTypes
+    };
+
+    explicit QwtScaleDiv( double lowerBound = 0.0, 
+        double upperBound = 0.0 );
+
+    explicit QwtScaleDiv( const QwtInterval &, QList<double>[NTickTypes] );
+
+    explicit QwtScaleDiv( double lowerBound, double upperBound,
+        QList<double>[NTickTypes] );
+
+    explicit QwtScaleDiv( double lowerBound, double upperBound, 
+        const QList<double> &minorTicks, const QList<double> &mediumTicks,
+        const QList<double> &majorTicks );
+
+    bool operator==( const QwtScaleDiv & ) const;
+    bool operator!=( const QwtScaleDiv & ) const;
+
+    void setInterval( double lowerBound, double upperBound );
+    void setInterval( const QwtInterval & );
+    QwtInterval interval() const;
+
+    void setLowerBound( double );
+    double lowerBound() const;
+
+    void setUpperBound( double );
+    double upperBound() const;
+
+    double range() const;
+
+    bool contains( double value ) const;
+
+    void setTicks( int tickType, const QList<double> & );
+    QList<double> ticks( int tickType ) const;
+
+    bool isEmpty() const;
+    bool isIncreasing() const;
+
+    void invert();
+    QwtScaleDiv inverted() const;
+
+    QwtScaleDiv bounded( double lowerBound, double upperBound ) const;
+
+private:
+    double d_lowerBound;
+    double d_upperBound;
+    QList<double> d_ticks[NTickTypes];
+};
+
+Q_DECLARE_TYPEINFO( QwtScaleDiv, Q_MOVABLE_TYPE );
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtScaleDiv & );
+#endif
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_scale_draw.h b/include/pli_vis/third_party/qwt/qwt_scale_draw.h
new file mode 100644
index 0000000000000000000000000000000000000000..c187c4ad56533b2333e83c1f2db174cc9458116f
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_scale_draw.h
@@ -0,0 +1,120 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_DRAW_H
+#define QWT_SCALE_DRAW_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_abstract_scale_draw.h"
+#include <qpoint.h>
+#include <qrect.h>
+#include <qtransform.h>
+
+/*!
+  \brief A class for drawing scales
+
+  QwtScaleDraw can be used to draw linear or logarithmic scales.
+  A scale has a position, an alignment and a length, which can be specified .
+  The labels can be rotated and aligned
+  to the ticks using setLabelRotation() and setLabelAlignment().
+
+  After a scale division has been specified as a QwtScaleDiv object
+  using QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &s),
+  the scale can be drawn with the QwtAbstractScaleDraw::draw() member.
+*/
+class QWT_EXPORT QwtScaleDraw: public QwtAbstractScaleDraw
+{
+public:
+    /*!
+        Alignment of the scale draw
+        \sa setAlignment(), alignment()
+     */
+    enum Alignment 
+    { 
+        //! The scale is below
+        BottomScale, 
+
+        //! The scale is above
+        TopScale, 
+
+        //! The scale is left
+        LeftScale, 
+
+        //! The scale is right
+        RightScale 
+    };
+
+    QwtScaleDraw();
+    virtual ~QwtScaleDraw();
+
+    void getBorderDistHint( const QFont &, int &start, int &end ) const;
+    int minLabelDist( const QFont & ) const;
+
+    int minLength( const QFont & ) const;
+    virtual double extent( const QFont & ) const;
+
+    void move( double x, double y );
+    void move( const QPointF & );
+    void setLength( double length );
+
+    Alignment alignment() const;
+    void setAlignment( Alignment );
+
+    Qt::Orientation orientation() const;
+
+    QPointF pos() const;
+    double length() const;
+
+    void setLabelAlignment( Qt::Alignment );
+    Qt::Alignment labelAlignment() const;
+
+    void setLabelRotation( double rotation );
+    double labelRotation() const;
+
+    int maxLabelHeight( const QFont & ) const;
+    int maxLabelWidth( const QFont & ) const;
+
+    QPointF labelPosition( double val ) const;
+
+    QRectF labelRect( const QFont &, double val ) const;
+    QSizeF labelSize( const QFont &, double val ) const;
+
+    QRect boundingLabelRect( const QFont &, double val ) const;
+
+protected:
+    QTransform labelTransformation( const QPointF &, const QSizeF & ) const;
+
+    virtual void drawTick( QPainter *, double val, double len ) const;
+    virtual void drawBackbone( QPainter * ) const;
+    virtual void drawLabel( QPainter *, double val ) const;
+
+private:
+    QwtScaleDraw( const QwtScaleDraw & );
+    QwtScaleDraw &operator=( const QwtScaleDraw &other );
+
+    void updateMap();
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+   Move the position of the scale
+
+   \param x X coordinate
+   \param y Y coordinate
+
+   \sa move(const QPointF &)
+*/
+inline void QwtScaleDraw::move( double x, double y )
+{
+    move( QPointF( x, y ) );
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_scale_engine.h b/include/pli_vis/third_party/qwt/qwt_scale_engine.h
new file mode 100644
index 0000000000000000000000000000000000000000..93edb724500b4c590b1a06c1894e6de1ea0c585e
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_scale_engine.h
@@ -0,0 +1,220 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_ENGINE_H
+#define QWT_SCALE_ENGINE_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_interval.h"
+
+class QwtTransform;
+
+/*!
+  \brief Arithmetic including a tolerance
+*/
+class QWT_EXPORT QwtScaleArithmetic
+{
+public:
+    static double ceilEps( double value, double intervalSize );
+    static double floorEps( double value, double intervalSize );
+
+    static double divideEps( double interval, double steps );
+
+    static double divideInterval( double interval, 
+        int numSteps, uint base );
+};
+
+/*!
+  \brief Base class for scale engines.
+
+  A scale engine tries to find "reasonable" ranges and step sizes
+  for scales.
+
+  The layout of the scale can be varied with setAttribute().
+
+  Qwt offers implementations for logarithmic and linear scales. 
+*/
+
+class QWT_EXPORT QwtScaleEngine
+{
+public:
+    /*! 
+       Layout attributes
+       \sa setAttribute(), testAttribute(), reference(),
+           lowerMargin(), upperMargin()
+     */
+
+    enum Attribute
+    {
+        //! No attributes
+        NoAttribute = 0x00,
+
+        //! Build a scale which includes the reference() value.
+        IncludeReference = 0x01,
+
+        //! Build a scale which is symmetric to the reference() value.
+        Symmetric = 0x02,
+
+        /*!
+           The endpoints of the scale are supposed to be equal the
+           outmost included values plus the specified margins 
+           (see setMargins()).
+           If this attribute is *not* set, the endpoints of the scale will
+           be integer multiples of the step size.
+         */
+        Floating = 0x04,
+
+        //! Turn the scale upside down.
+        Inverted = 0x08
+    };
+
+    //! Layout attributes
+    typedef QFlags<Attribute> Attributes;
+
+    explicit QwtScaleEngine( uint base = 10 );
+    virtual ~QwtScaleEngine();
+
+    void setBase( uint base );
+    uint base() const;
+
+    void setAttribute( Attribute, bool on = true );
+    bool testAttribute( Attribute ) const;
+
+    void setAttributes( Attributes );
+    Attributes attributes() const;
+
+    void setReference( double reference );
+    double reference() const;
+
+    void setMargins( double lower, double upper );
+    double lowerMargin() const;
+    double upperMargin() const;
+
+    /*!
+      Align and divide an interval
+
+      \param maxNumSteps Max. number of steps
+      \param x1 First limit of the interval (In/Out)
+      \param x2 Second limit of the interval (In/Out)
+      \param stepSize Step size (Return value)
+    */
+    virtual void autoScale( int maxNumSteps,
+        double &x1, double &x2, double &stepSize ) const = 0;
+
+    /*!
+      \brief Calculate a scale division
+
+      \param x1 First interval limit
+      \param x2 Second interval limit
+      \param maxMajorSteps Maximum for the number of major steps
+      \param maxMinorSteps Maximum number of minor steps
+      \param stepSize Step size. If stepSize == 0.0, the scaleEngine
+                   calculates one.
+
+      \return Calculated scale division
+    */
+    virtual QwtScaleDiv divideScale( double x1, double x2,
+        int maxMajorSteps, int maxMinorSteps,
+        double stepSize = 0.0 ) const = 0;
+
+    void setTransformation( QwtTransform * );
+    QwtTransform *transformation() const;
+
+protected:
+    bool contains( const QwtInterval &, double val ) const;
+    QList<double> strip( const QList<double>&, const QwtInterval & ) const;
+
+    double divideInterval( double interval, int numSteps ) const;
+
+    QwtInterval buildInterval( double v ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+  \brief A scale engine for linear scales
+
+  The step size will fit into the pattern
+  \f$\left\{ 1,2,5\right\} \cdot 10^{n}\f$, where n is an integer.
+*/
+
+class QWT_EXPORT QwtLinearScaleEngine: public QwtScaleEngine
+{
+public:
+    QwtLinearScaleEngine( uint base = 10 );
+    virtual ~QwtLinearScaleEngine();
+
+    virtual void autoScale( int maxSteps,
+        double &x1, double &x2, double &stepSize ) const;
+
+    virtual QwtScaleDiv divideScale( double x1, double x2,
+        int numMajorSteps, int numMinorSteps,
+                                     double stepSize = 0.0 ) const;
+
+
+protected:
+    QwtInterval align( const QwtInterval&, double stepSize ) const;
+
+    void buildTicks(
+        const QwtInterval &, double stepSize, int maxMinSteps,
+        QList<double> ticks[QwtScaleDiv::NTickTypes] ) const;
+
+    QList<double> buildMajorTicks(
+        const QwtInterval &interval, double stepSize ) const;
+
+    void buildMinorTicks( const QList<double>& majorTicks,
+        int maxMinorSteps, double stepSize,
+        QList<double> &minorTicks, QList<double> &mediumTicks ) const;
+};
+
+/*!
+  \brief A scale engine for logarithmic scales
+
+  The step size is measured in *decades*
+  and the major step size will be adjusted to fit the pattern
+  \f$\left\{ 1,2,3,5\right\} \cdot 10^{n}\f$, where n is a natural number
+  including zero.
+
+  \warning the step size as well as the margins are measured in *decades*.
+*/
+
+class QWT_EXPORT QwtLogScaleEngine: public QwtScaleEngine
+{
+public:
+    QwtLogScaleEngine( uint base = 10 );
+    virtual ~QwtLogScaleEngine();
+
+    virtual void autoScale( int maxSteps,
+        double &x1, double &x2, double &stepSize ) const;
+
+    virtual QwtScaleDiv divideScale( double x1, double x2,
+        int numMajorSteps, int numMinorSteps,
+        double stepSize = 0.0 ) const;
+
+protected:
+    QwtInterval align( const QwtInterval&, double stepSize ) const;
+
+    void buildTicks(
+        const QwtInterval &, double stepSize, int maxMinSteps,
+        QList<double> ticks[QwtScaleDiv::NTickTypes] ) const;
+
+    QList<double> buildMajorTicks(
+        const QwtInterval &interval, double stepSize ) const;
+
+    void buildMinorTicks( const QList<double>& majorTicks,
+        int maxMinorSteps, double stepSize,
+        QList<double> &minorTicks, QList<double> &mediumTicks ) const;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtScaleEngine::Attributes )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_scale_map.h b/include/pli_vis/third_party/qwt/qwt_scale_map.h
new file mode 100644
index 0000000000000000000000000000000000000000..f46953e116f5e47d42a9a913568ad4ddcf20fb77
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_scale_map.h
@@ -0,0 +1,175 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_MAP_H
+#define QWT_SCALE_MAP_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_transform.h"
+#include <qrect.h>
+
+#ifndef QT_NO_DEBUG_STREAM
+#include <qdebug.h>
+#endif
+
+class QRectF;
+
+/*!
+   \brief A scale map
+
+   QwtScaleMap offers transformations from the coordinate system
+   of a scale into the linear coordinate system of a paint device 
+   and vice versa.
+*/
+class QWT_EXPORT QwtScaleMap
+{
+public:
+    QwtScaleMap();
+    QwtScaleMap( const QwtScaleMap& );
+
+    ~QwtScaleMap();
+
+    QwtScaleMap &operator=( const QwtScaleMap & );
+
+    void setTransformation( QwtTransform * );
+    const QwtTransform *transformation() const;
+
+    void setPaintInterval( double p1, double p2 );
+    void setScaleInterval( double s1, double s2 );
+
+    double transform( double s ) const;
+    double invTransform( double p ) const;
+
+    double p1() const;
+    double p2() const;
+
+    double s1() const;
+    double s2() const;
+
+    double pDist() const;
+    double sDist() const;
+
+    static QRectF transform( const QwtScaleMap &,
+        const QwtScaleMap &, const QRectF & );
+    static QRectF invTransform( const QwtScaleMap &,
+        const QwtScaleMap &, const QRectF & );
+
+    static QPointF transform( const QwtScaleMap &,
+        const QwtScaleMap &, const QPointF & );
+    static QPointF invTransform( const QwtScaleMap &,
+        const QwtScaleMap &, const QPointF & );
+
+    bool isInverting() const;
+
+private:
+    void updateFactor();
+
+    double d_s1, d_s2;     // scale interval boundaries
+    double d_p1, d_p2;     // paint device interval boundaries
+
+    double d_cnv;       // conversion factor
+    double d_ts1;
+
+    QwtTransform *d_transform;
+};
+
+/*!
+    \return First border of the scale interval
+*/
+inline double QwtScaleMap::s1() const
+{
+    return d_s1;
+}
+
+/*!
+    \return Second border of the scale interval
+*/
+inline double QwtScaleMap::s2() const
+{
+    return d_s2;
+}
+
+/*!
+    \return First border of the paint interval
+*/
+inline double QwtScaleMap::p1() const
+{
+    return d_p1;
+}
+
+/*!
+    \return Second border of the paint interval
+*/
+inline double QwtScaleMap::p2() const
+{
+    return d_p2;
+}
+
+/*!
+    \return qwtAbs(p2() - p1())
+*/
+inline double QwtScaleMap::pDist() const
+{
+    return qAbs( d_p2 - d_p1 );
+}
+
+/*!
+    \return qwtAbs(s2() - s1())
+*/
+inline double QwtScaleMap::sDist() const
+{
+    return qAbs( d_s2 - d_s1 );
+}
+
+/*!
+  Transform a point related to the scale interval into an point
+  related to the interval of the paint device
+
+  \param s Value relative to the coordinates of the scale
+  \return Transformed value
+
+  \sa invTransform()
+*/
+inline double QwtScaleMap::transform( double s ) const
+{
+    if ( d_transform )
+        s = d_transform->transform( s );
+
+    return d_p1 + ( s - d_ts1 ) * d_cnv;
+}
+
+/*!
+  Transform an paint device value into a value in the
+  interval of the scale.
+
+  \param p Value relative to the coordinates of the paint device
+  \return Transformed value
+
+  \sa transform()
+*/
+inline double QwtScaleMap::invTransform( double p ) const
+{
+    double s = d_ts1 + ( p - d_p1 ) / d_cnv;
+    if ( d_transform )
+        s = d_transform->invTransform( s );
+
+    return s;
+}
+
+//! \return True, when ( p1() < p2() ) != ( s1() < s2() )
+inline bool QwtScaleMap::isInverting() const
+{
+    return ( ( d_p1 < d_p2 ) != ( d_s1 < d_s2 ) );
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QWT_EXPORT QDebug operator<<( QDebug, const QwtScaleMap & );
+#endif
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_scale_widget.h b/include/pli_vis/third_party/qwt/qwt_scale_widget.h
new file mode 100644
index 0000000000000000000000000000000000000000..3abe9351dc9eb956e5a57d27f0f9d8a6a9357725
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_scale_widget.h
@@ -0,0 +1,136 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SCALE_WIDGET_H
+#define QWT_SCALE_WIDGET_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_scale_draw.h"
+#include <qwidget.h>
+#include <qfont.h>
+#include <qcolor.h>
+#include <qstring.h>
+
+class QPainter;
+class QwtTransform;
+class QwtScaleDiv;
+class QwtColorMap;
+
+/*!
+  \brief A Widget which contains a scale
+
+  This Widget can be used to decorate composite widgets with
+  a scale.
+*/
+
+class QWT_EXPORT QwtScaleWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    //! Layout flags of the title
+    enum LayoutFlag
+    {
+        /*!
+          The title of vertical scales is painted from top to bottom. 
+          Otherwise it is painted from bottom to top.
+         */
+        TitleInverted = 1
+    };
+
+    //! Layout flags of the title
+    typedef QFlags<LayoutFlag> LayoutFlags;
+
+    explicit QwtScaleWidget( QWidget *parent = NULL );
+    explicit QwtScaleWidget( QwtScaleDraw::Alignment, QWidget *parent = NULL );
+    virtual ~QwtScaleWidget();
+
+Q_SIGNALS:
+    //! Signal emitted, whenever the scale division changes
+    void scaleDivChanged();
+
+public:
+    void setTitle( const QString &title );
+    void setTitle( const QwtText &title );
+    QwtText title() const;
+
+    void setLayoutFlag( LayoutFlag, bool on );
+    bool testLayoutFlag( LayoutFlag ) const;
+
+    void setBorderDist( int start, int end );
+    int startBorderDist() const;
+    int endBorderDist() const;
+
+    void getBorderDistHint( int &start, int &end ) const;
+
+    void getMinBorderDist( int &start, int &end ) const;
+    void setMinBorderDist( int start, int end );
+
+    void setMargin( int );
+    int margin() const;
+
+    void setSpacing( int td );
+    int spacing() const;
+
+    void setScaleDiv( const QwtScaleDiv &sd );
+    void setTransformation( QwtTransform * );
+
+    void setScaleDraw( QwtScaleDraw * );
+    const QwtScaleDraw *scaleDraw() const;
+    QwtScaleDraw *scaleDraw();
+
+    void setLabelAlignment( Qt::Alignment );
+    void setLabelRotation( double rotation );
+
+    void setColorBarEnabled( bool );
+    bool isColorBarEnabled() const;
+
+    void setColorBarWidth( int );
+    int colorBarWidth() const;
+
+    void setColorMap( const QwtInterval &, QwtColorMap * );
+
+    QwtInterval colorBarInterval() const;
+    const QwtColorMap *colorMap() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    int titleHeightForWidth( int width ) const;
+    int dimForLength( int length, const QFont &scaleFont ) const;
+
+    void drawColorBar( QPainter *painter, const QRectF & ) const;
+    void drawTitle( QPainter *painter, QwtScaleDraw::Alignment,
+        const QRectF &rect ) const;
+
+    void setAlignment( QwtScaleDraw::Alignment );
+    QwtScaleDraw::Alignment alignment() const;
+
+    QRectF colorBarRect( const QRectF& ) const;
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+    virtual void resizeEvent( QResizeEvent * );
+
+    void draw( QPainter *p ) const;
+
+    void scaleChange();
+    void layoutScale( bool update = true );
+
+private:
+    void initScale( QwtScaleDraw::Alignment );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtScaleWidget::LayoutFlags )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_series_data.h b/include/pli_vis/third_party/qwt/qwt_series_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..be915285eb4024f1b2275b8943e2334b66629676
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_series_data.h
@@ -0,0 +1,355 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SERIES_DATA_H
+#define QWT_SERIES_DATA_H 1
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_samples.h"
+#include "qwt/qwt_point_3d.h"
+#include "qwt/qwt_point_polar.h"
+#include <qvector.h>
+#include <qrect.h>
+
+/*!
+   \brief Abstract interface for iterating over samples
+
+   Qwt offers several implementations of the QwtSeriesData API,
+   but in situations, where data of an application specific format
+   needs to be displayed, without having to copy it, it is recommended
+   to implement an individual data access.
+
+   A subclass of QwtSeriesData<QPointF> must implement: 
+
+   - size()\n 
+     Should return number of data points.
+
+   - sample()\n
+     Should return values x and y values of the sample at specific position
+     as QPointF object.
+
+   - boundingRect()\n 
+     Should return the bounding rectangle of the data series.
+     It is used for autoscaling and might help certain algorithms for displaying
+     the data. You can use qwtBoundingRect() for an implementation
+     but often it is possible to implement a more efficient algorithm 
+     depending on the characteristics of the series.
+     The member d_boundingRect is intended for caching the calculated rectangle.
+    
+*/
+template <typename T>
+class QwtSeriesData
+{
+public:
+    //! Constructor
+    QwtSeriesData();
+
+    //! Destructor
+    virtual ~QwtSeriesData();
+
+    //! \return Number of samples
+    virtual size_t size() const = 0;
+
+    /*!
+      Return a sample
+      \param i Index
+      \return Sample at position i
+     */
+    virtual T sample( size_t i ) const = 0;
+
+    /*!
+       Calculate the bounding rect of all samples
+
+       The bounding rect is necessary for autoscaling and can be used
+       for a couple of painting optimizations.
+
+       qwtBoundingRect(...) offers slow implementations iterating
+       over the samples. For large sets it is recommended to implement
+       something faster f.e. by caching the bounding rectangle.
+
+       \return Bounding rectangle
+     */
+    virtual QRectF boundingRect() const = 0;
+
+    /*!
+       Set a the "rect of interest"
+
+       QwtPlotSeriesItem defines the current area of the plot canvas
+       as "rectangle of interest" ( QwtPlotSeriesItem::updateScaleDiv() ).
+       It can be used to implement different levels of details.
+
+       The default implementation does nothing.
+   
+       \param rect Rectangle of interest
+    */
+    virtual void setRectOfInterest( const QRectF &rect );
+
+protected:
+    //! Can be used to cache a calculated bounding rectangle
+    mutable QRectF d_boundingRect;
+
+private:
+    QwtSeriesData<T> &operator=( const QwtSeriesData<T> & );
+};
+
+template <typename T>
+QwtSeriesData<T>::QwtSeriesData():
+    d_boundingRect( 0.0, 0.0, -1.0, -1.0 )
+{
+}
+
+template <typename T>
+QwtSeriesData<T>::~QwtSeriesData()
+{
+}
+
+template <typename T>
+void QwtSeriesData<T>::setRectOfInterest( const QRectF & )
+{
+}
+
+/*!
+  \brief Template class for data, that is organized as QVector
+
+  QVector uses implicit data sharing and can be
+  passed around as argument efficiently.
+*/
+template <typename T>
+class QwtArraySeriesData: public QwtSeriesData<T>
+{
+public:
+    //! Constructor
+    QwtArraySeriesData();
+
+    /*!
+       Constructor
+       \param samples Array of samples
+    */
+    QwtArraySeriesData( const QVector<T> &samples );
+
+    /*!
+      Assign an array of samples
+      \param samples Array of samples
+    */
+    void setSamples( const QVector<T> &samples );
+
+    //! \return Array of samples
+    const QVector<T> samples() const;
+
+    //! \return Number of samples
+    virtual size_t size() const;
+
+    /*!
+      \return Sample at a specific position
+
+      \param index Index
+      \return Sample at position index
+    */
+    virtual T sample( size_t index ) const;
+
+protected:
+    //! Vector of samples
+    QVector<T> d_samples;
+};
+
+template <typename T>
+QwtArraySeriesData<T>::QwtArraySeriesData()
+{
+}
+
+template <typename T>
+QwtArraySeriesData<T>::QwtArraySeriesData( const QVector<T> &samples ):
+    d_samples( samples )
+{
+}
+
+template <typename T>
+void QwtArraySeriesData<T>::setSamples( const QVector<T> &samples )
+{
+    QwtSeriesData<T>::d_boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
+    d_samples = samples;
+}
+
+template <typename T>
+const QVector<T> QwtArraySeriesData<T>::samples() const
+{
+    return d_samples;
+}
+
+template <typename T>
+size_t QwtArraySeriesData<T>::size() const
+{
+    return d_samples.size();
+}
+
+template <typename T>
+T QwtArraySeriesData<T>::sample( size_t i ) const
+{
+    return d_samples[ static_cast<int>( i ) ];
+}
+
+//! Interface for iterating over an array of points
+class QWT_EXPORT QwtPointSeriesData: public QwtArraySeriesData<QPointF>
+{
+public:
+    QwtPointSeriesData(
+        const QVector<QPointF> & = QVector<QPointF>() );
+
+    virtual QRectF boundingRect() const;
+};
+
+//! Interface for iterating over an array of 3D points
+class QWT_EXPORT QwtPoint3DSeriesData: public QwtArraySeriesData<QwtPoint3D>
+{
+public:
+    QwtPoint3DSeriesData(
+        const QVector<QwtPoint3D> & = QVector<QwtPoint3D>() );
+    virtual QRectF boundingRect() const;
+};
+
+//! Interface for iterating over an array of intervals
+class QWT_EXPORT QwtIntervalSeriesData: public QwtArraySeriesData<QwtIntervalSample>
+{
+public:
+    QwtIntervalSeriesData(
+        const QVector<QwtIntervalSample> & = QVector<QwtIntervalSample>() );
+
+    virtual QRectF boundingRect() const;
+};
+
+//! Interface for iterating over an array of samples
+class QWT_EXPORT QwtSetSeriesData: public QwtArraySeriesData<QwtSetSample>
+{
+public:
+    QwtSetSeriesData(
+        const QVector<QwtSetSample> & = QVector<QwtSetSample>() );
+
+    virtual QRectF boundingRect() const;
+};
+
+/*!
+    Interface for iterating over an array of OHLC samples
+*/
+class QWT_EXPORT QwtTradingChartData: public QwtArraySeriesData<QwtOHLCSample>
+{
+public:
+    QwtTradingChartData(
+        const QVector<QwtOHLCSample> & = QVector<QwtOHLCSample>() );
+
+    virtual QRectF boundingRect() const;
+};
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QPointF> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtPoint3D> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtPointPolar> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtIntervalSample> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtSetSample> &, int from = 0, int to = -1 );
+
+QWT_EXPORT QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtOHLCSample> &, int from = 0, int to = -1 );
+
+/*!
+    Binary search for a sorted series of samples
+
+    qwtUpperSampleIndex returns the index of sample that is the upper bound
+    of value. Is the the value smaller than the smallest value the return
+    value will be 0. Is the value greater or equal than the largest
+    value the return value will be -1.
+
+  \par Example
+    The following example shows finds a point of curve from an x
+    coordinate
+
+  \verbatim
+#include <qwt_series_data.h>
+#include <qwt_plot_curve.h>
+
+struct compareX
+{
+    inline bool operator()( const double x, const QPointF &pos ) const
+    {
+        return ( x < pos.x() );
+    }
+};
+
+QLineF curveLineAt( const QwtPlotCurve *curve, double x )
+{
+    int index = qwtUpperSampleIndex<QPointF>( 
+        *curve->data(), x, compareX() );
+            
+    if ( index == -1 && 
+        x == curve->sample( curve->dataSize() - 1 ).x() )
+    {   
+        // the last sample is excluded from qwtUpperSampleIndex
+        index = curve->dataSize() - 1;
+    }
+
+    QLineF line; // invalid
+    if ( index > 0 )
+    {
+        line.setP1( curve->sample( index - 1 ) );
+        line.setP2( curve->sample( index ) );
+    }
+
+    return line;
+}
+
+\endverbatim
+
+
+    \param series Series of samples
+    \param value Value
+    \param lessThan Compare operation
+
+    \note The samples must be sorted according to the order specified 
+          by the lessThan object
+
+of the range [begin, end) and returns the position of the one-past-the-last occurrence of value. If no such item is found, returns the position where the item should be inserted.
+ */
+template <typename T, typename LessThan>
+inline int qwtUpperSampleIndex( const QwtSeriesData<T> &series,
+    double value, LessThan lessThan  ) 
+{
+    const int indexMax = series.size() - 1;
+
+    if ( indexMax < 0 || !lessThan( value, series.sample( indexMax ) )  )
+        return -1;
+
+    int indexMin = 0;
+    int n = indexMax;
+
+    while ( n > 0 )
+    {
+        const int half = n >> 1;
+        const int indexMid = indexMin + half;
+
+        if ( lessThan( value, series.sample( indexMid ) ) )
+        {
+            n = half;
+        }
+        else
+        {
+            indexMin = indexMid + 1;
+            n -= half + 1;
+        }
+    }
+
+    return indexMin;
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_series_store.h b/include/pli_vis/third_party/qwt/qwt_series_store.h
new file mode 100644
index 0000000000000000000000000000000000000000..3dfa09e878fd16e47a4124b56410cef61041d128
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_series_store.h
@@ -0,0 +1,199 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SERIES_STORE_H
+#define QWT_SERIES_STORE_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_series_data.h"
+
+/*!
+  \brief Bridge between QwtSeriesStore and QwtPlotSeriesItem
+
+  QwtAbstractSeriesStore is an abstract interface only
+  to make it possible to isolate the template based methods ( QwtSeriesStore )
+  from the regular methods ( QwtPlotSeriesItem ) to make it possible
+  to derive from QwtPlotSeriesItem without any hassle with templates.
+*/
+class QwtAbstractSeriesStore
+{
+protected:
+    //! Destructor
+    virtual ~QwtAbstractSeriesStore() {}
+
+    //! dataChanged() indicates, that the series has been changed.
+    virtual void dataChanged() = 0;
+
+    /*!
+      Set a the "rectangle of interest" for the stored series
+      \sa QwtSeriesData<T>::setRectOfInterest()
+     */
+    virtual void setRectOfInterest( const QRectF & ) = 0;
+
+    //! \return Bounding rectangle of the stored series
+    virtual QRectF dataRect() const = 0;
+
+    //! \return Number of samples
+    virtual size_t dataSize() const = 0;
+};
+
+/*!
+  \brief Class storing a QwtSeriesData object
+
+  QwtSeriesStore and QwtPlotSeriesItem are intended as base classes for all
+  plot items iterating over a series of samples. Both classes share
+  a virtual base class ( QwtAbstractSeriesStore ) to bridge between them.
+
+  QwtSeriesStore offers the template based part for the plot item API, so
+  that QwtPlotSeriesItem can be derived without any hassle with templates.
+ */
+template <typename T>
+class QwtSeriesStore: public virtual QwtAbstractSeriesStore
+{
+public:
+    /*!
+      \brief Constructor
+      The store contains no series
+    */
+    explicit QwtSeriesStore<T>();
+
+    //! Destructor
+    ~QwtSeriesStore<T>();
+
+    /*!
+      Assign a series of samples
+
+      \param series Data
+      \warning The item takes ownership of the data object, deleting
+               it when its not used anymore.
+    */
+    void setData( QwtSeriesData<T> *series );
+
+    //! \return the the series data
+    QwtSeriesData<T> *data();
+
+    //! \return the the series data
+    const QwtSeriesData<T> *data() const;
+
+    /*!
+        \param index Index
+        \return Sample at position index
+    */
+    T sample( int index ) const;
+
+    /*!
+      \return Number of samples of the series
+      \sa setData(), QwtSeriesData<T>::size()
+    */
+    virtual size_t dataSize() const;
+
+    /*!
+      \return Bounding rectangle of the series
+              or an invalid rectangle, when no series is stored
+
+      \sa QwtSeriesData<T>::boundingRect()
+    */
+    virtual QRectF dataRect() const;
+
+    /*!
+      Set a the "rect of interest" for the series
+
+      \param rect Rectangle of interest
+      \sa QwtSeriesData<T>::setRectOfInterest()
+    */
+    virtual void setRectOfInterest( const QRectF &rect );
+
+    /*!
+      Replace a series without deleting the previous one
+
+      \param series New series
+      \return Previously assigned series
+     */
+    QwtSeriesData<T> *swapData( QwtSeriesData<T> *series );
+
+private:
+    QwtSeriesData<T> *d_series;
+};
+
+template <typename T>
+QwtSeriesStore<T>::QwtSeriesStore():
+    d_series( NULL )
+{
+}
+
+template <typename T>
+QwtSeriesStore<T>::~QwtSeriesStore()
+{
+    delete d_series;
+}
+
+template <typename T>
+inline QwtSeriesData<T> *QwtSeriesStore<T>::data()
+{
+    return d_series;
+}
+
+template <typename T>
+inline const QwtSeriesData<T> *QwtSeriesStore<T>::data() const
+{
+    return d_series;
+}
+
+template <typename T>
+inline T QwtSeriesStore<T>::sample( int index ) const
+{
+    return d_series ? d_series->sample( index ) : T();
+}
+
+template <typename T>
+void QwtSeriesStore<T>::setData( QwtSeriesData<T> *series )
+{
+    if ( d_series != series )
+    {
+        delete d_series;
+        d_series = series;
+        dataChanged();
+    }
+}
+
+template <typename T>
+size_t QwtSeriesStore<T>::dataSize() const
+{
+    if ( d_series == NULL )
+        return 0;
+
+    return d_series->size();
+}
+
+template <typename T>
+QRectF QwtSeriesStore<T>::dataRect() const
+{
+    if ( d_series == NULL )
+        return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid
+
+    return d_series->boundingRect();
+}
+
+template <typename T>
+void QwtSeriesStore<T>::setRectOfInterest( const QRectF &rect )
+{
+    if ( d_series )
+        d_series->setRectOfInterest( rect );
+}
+
+template <typename T>
+QwtSeriesData<T>* QwtSeriesStore<T>::swapData( QwtSeriesData<T> *series )
+{
+    QwtSeriesData<T> * swappedSeries = d_series;
+    d_series = series;
+
+    return swappedSeries;
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_slider.h b/include/pli_vis/third_party/qwt/qwt_slider.h
new file mode 100644
index 0000000000000000000000000000000000000000..7570d484f0d3a0e3519621055bf6009688e8ba20
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_slider.h
@@ -0,0 +1,130 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SLIDER_H
+#define QWT_SLIDER_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_abstract_slider.h"
+
+class QwtScaleDraw;
+
+/*!
+  \brief The Slider Widget
+
+  QwtSlider is a slider widget which operates on an interval
+  of type double. Its position is related to a scale showing
+  the current value.
+
+  The slider can be customized by having a through, a groove - or both.
+
+  \image html sliders.png
+*/
+
+class QWT_EXPORT QwtSlider: public QwtAbstractSlider
+{
+    Q_OBJECT
+
+    Q_ENUMS( ScalePosition BackgroundStyle )
+
+    Q_PROPERTY( Qt::Orientation orientation
+                READ orientation WRITE setOrientation )
+    Q_PROPERTY( ScalePosition scalePosition READ scalePosition
+        WRITE setScalePosition )
+
+    Q_PROPERTY( bool trough READ hasTrough WRITE setTrough )
+    Q_PROPERTY( bool groove READ hasGroove WRITE setGroove )
+
+    Q_PROPERTY( QSize handleSize READ handleSize WRITE setHandleSize )
+    Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth )
+    Q_PROPERTY( int spacing READ spacing WRITE setSpacing )
+
+public:
+
+    /*!
+      Position of the scale
+      \sa QwtSlider(), setScalePosition(), setOrientation()
+     */
+    enum ScalePosition
+    {
+        //! The slider has no scale
+        NoScale,
+
+        //! The scale is right of a vertical or below a horizontal slider
+        LeadingScale,
+
+        //! The scale is left of a vertical or above a horizontal slider
+        TrailingScale
+    };
+
+    explicit QwtSlider( QWidget *parent = NULL );
+    explicit QwtSlider( Qt::Orientation, QWidget *parent = NULL );
+
+    virtual ~QwtSlider();
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation() const;
+
+    void setScalePosition( ScalePosition );
+    ScalePosition scalePosition() const;
+
+    void setTrough( bool );
+    bool hasTrough() const;
+
+    void setGroove( bool );
+    bool hasGroove() const;
+
+    void setHandleSize( const QSize & );
+    QSize handleSize() const;
+
+    void setBorderWidth( int bw );
+    int borderWidth() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    void setScaleDraw( QwtScaleDraw * );
+    const QwtScaleDraw *scaleDraw() const;
+
+    void setUpdateInterval( int );
+    int updateInterval() const;
+
+protected:
+    virtual double scrolledTo( const QPoint & ) const;
+    virtual bool isScrollPosition( const QPoint & ) const;
+
+    virtual void drawSlider ( QPainter *, const QRect & ) const;
+    virtual void drawHandle( QPainter *, const QRect &, int pos ) const;
+
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void mouseReleaseEvent( QMouseEvent * );
+    virtual void resizeEvent( QResizeEvent * );
+    virtual void paintEvent ( QPaintEvent * );
+    virtual void changeEvent( QEvent * );
+    virtual void timerEvent( QTimerEvent * );
+
+    virtual void scaleChange();
+
+    QRect sliderRect() const;
+    QRect handleRect() const;
+
+private:
+    QwtScaleDraw *scaleDraw();
+
+    void layoutSlider( bool );
+    void initSlider( Qt::Orientation );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_spline.h b/include/pli_vis/third_party/qwt/qwt_spline.h
new file mode 100644
index 0000000000000000000000000000000000000000..e62e6092a3c790a225adc9026a11878961033e07
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_spline.h
@@ -0,0 +1,101 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SPLINE_H
+#define QWT_SPLINE_H
+
+#include "qwt/qwt_global.h"
+#include <qpolygon.h>
+#include <qvector.h>
+
+/*!
+  \brief A class for spline interpolation
+
+  The QwtSpline class is used for cubical spline interpolation.
+  Two types of splines, natural and periodic, are supported.
+
+  \par Usage:
+  <ol>
+  <li>First call setPoints() to determine the spline coefficients
+      for a tabulated function y(x).
+  <li>After the coefficients have been set up, the interpolated
+      function value for an argument x can be determined by calling
+      QwtSpline::value().
+  </ol>
+
+  \par Example:
+  \code
+#include <qwt_spline.h>
+
+QPolygonF interpolate(const QPolygonF& points, int numValues)
+{
+    QwtSpline spline;
+    if ( !spline.setPoints(points) )
+        return points;
+
+    QPolygonF interpolatedPoints(numValues);
+
+    const double delta =
+        (points[numPoints - 1].x() - points[0].x()) / (points.size() - 1);
+    for(i = 0; i < points.size(); i++)  / interpolate
+    {
+        const double x = points[0].x() + i * delta;
+        interpolatedPoints[i].setX(x);
+        interpolatedPoints[i].setY(spline.value(x));
+    }
+    return interpolatedPoints;
+}
+  \endcode
+*/
+
+class QWT_EXPORT QwtSpline
+{
+public:
+    //! Spline type
+    enum SplineType
+    {
+        //! A natural spline
+        Natural,
+
+        //! A periodic spline
+        Periodic
+    };
+
+    QwtSpline();
+    QwtSpline( const QwtSpline & );
+
+    ~QwtSpline();
+
+    QwtSpline &operator=( const QwtSpline & );
+
+    void setSplineType( SplineType );
+    SplineType splineType() const;
+
+    bool setPoints( const QPolygonF& points );
+    QPolygonF points() const;
+
+    void reset();
+
+    bool isValid() const;
+    double value( double x ) const;
+
+    const QVector<double> &coefficientsA() const;
+    const QVector<double> &coefficientsB() const;
+    const QVector<double> &coefficientsC() const;
+
+protected:
+    bool buildNaturalSpline( const QPolygonF & );
+    bool buildPeriodicSpline( const QPolygonF & );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_symbol.h b/include/pli_vis/third_party/qwt/qwt_symbol.h
new file mode 100644
index 0000000000000000000000000000000000000000..037c4a989208dd2b8dc1cbe7a2a74680a8183417
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_symbol.h
@@ -0,0 +1,258 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SYMBOL_H
+#define QWT_SYMBOL_H
+
+#include "qwt/qwt_global.h"
+#include <qpolygon.h>
+
+class QPainter;
+class QRect;
+class QSize;
+class QBrush;
+class QPen;
+class QColor;
+class QPointF;
+class QPolygonF;
+class QPainterPath;
+class QPixmap;
+class QByteArray;
+class QwtGraphic;
+
+//! A class for drawing symbols
+class QWT_EXPORT QwtSymbol
+{
+public:
+    /*!
+      Symbol Style
+      \sa setStyle(), style()
+     */
+    enum Style
+    {
+        //! No Style. The symbol cannot be drawn.
+        NoSymbol = -1,
+
+        //! Ellipse or circle
+        Ellipse,
+
+        //! Rectangle
+        Rect,
+
+        //!  Diamond
+        Diamond,
+
+        //! Triangle pointing upwards
+        Triangle,
+
+        //! Triangle pointing downwards
+        DTriangle,
+
+        //! Triangle pointing upwards
+        UTriangle,
+
+        //! Triangle pointing left
+        LTriangle,
+
+        //! Triangle pointing right
+        RTriangle,
+
+        //! Cross (+)
+        Cross,
+
+        //! Diagonal cross (X)
+        XCross,
+
+        //! Horizontal line
+        HLine,
+
+        //! Vertical line
+        VLine,
+
+        //! X combined with +
+        Star1,
+
+        //! Six-pointed star
+        Star2,
+
+        //! Hexagon
+        Hexagon,
+
+        /*!
+          The symbol is represented by a painter path, where the 
+          origin ( 0, 0 ) of the path coordinate system is mapped to
+          the position of the symbol.
+
+          \sa setPath(), path()
+         */
+        Path,
+
+        /*!
+          The symbol is represented by a pixmap. The pixmap is centered
+          or aligned to its pin point.
+
+          \sa setPinPoint()
+         */
+        Pixmap,
+
+        /*!
+          The symbol is represented by a graphic. The graphic is centered
+          or aligned to its pin point.
+
+          \sa setPinPoint()
+         */
+        Graphic,
+
+        /*!
+          The symbol is represented by a SVG graphic. The graphic is centered
+          or aligned to its pin point.
+
+          \sa setPinPoint()
+         */
+        SvgDocument,
+
+        /*!
+         Styles >= QwtSymbol::UserSymbol are reserved for derived
+         classes of QwtSymbol that overload drawSymbols() with
+         additional application specific symbol types.
+         */
+        UserStyle = 1000
+    };
+
+    /*!
+      Depending on the render engine and the complexity of the
+      symbol shape it might be faster to render the symbol
+      to a pixmap and to paint this pixmap.
+
+      F.e. the raster paint engine is a pure software renderer
+      where in cache mode a draw operation usually ends in 
+      raster operation with the the backing store, that are usually
+      faster, than the algorithms for rendering polygons.
+      But the opposite can be expected for graphic pipelines
+      that can make use of hardware acceleration.
+
+      The default setting is AutoCache
+
+      \sa setCachePolicy(), cachePolicy()
+
+      \note The policy has no effect, when the symbol is painted 
+            to a vector graphics format ( PDF, SVG ).
+      \warning Since Qt 4.8 raster is the default backend on X11
+     */
+
+    enum CachePolicy
+    {
+        //! Don't use a pixmap cache
+        NoCache,
+
+        //! Always use a pixmap cache
+        Cache,
+
+        /*! 
+           Use a cache when one of the following conditions is true:
+
+           - The symbol is rendered with the software 
+             renderer ( QPaintEngine::Raster )
+         */
+        AutoCache
+    };
+
+public:
+    QwtSymbol( Style = NoSymbol );
+    QwtSymbol( Style, const QBrush &, const QPen &, const QSize & );
+    QwtSymbol( const QPainterPath &, const QBrush &, const QPen & );
+
+    virtual ~QwtSymbol();
+
+    void setCachePolicy( CachePolicy );
+    CachePolicy cachePolicy() const;
+
+    void setSize( const QSize & );
+    void setSize( int width, int height = -1 );
+    const QSize& size() const;
+
+    void setPinPoint( const QPointF &pos, bool enable = true );
+    QPointF pinPoint() const;
+
+    void setPinPointEnabled( bool );
+    bool isPinPointEnabled() const;
+
+    virtual void setColor( const QColor & );
+
+    void setBrush( const QBrush& b );
+    const QBrush& brush() const;
+
+    void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine );
+    void setPen( const QPen & );
+    const QPen& pen() const;
+
+    void setStyle( Style );
+    Style style() const;
+
+    void setPath( const QPainterPath & );
+    const QPainterPath &path() const;
+
+    void setPixmap( const QPixmap & );
+    const QPixmap &pixmap() const;
+
+    void setGraphic( const QwtGraphic & );
+    const QwtGraphic &graphic() const;
+
+#ifndef QWT_NO_SVG
+    void setSvgDocument( const QByteArray & );
+#endif
+
+    void drawSymbol( QPainter *, const QRectF & ) const;
+    void drawSymbol( QPainter *, const QPointF & ) const;
+    void drawSymbols( QPainter *, const QPolygonF & ) const;
+    void drawSymbols( QPainter *,
+        const QPointF *, int numPoints ) const;
+
+    virtual QRect boundingRect() const;
+    void invalidateCache();
+
+protected:
+    virtual void renderSymbols( QPainter *,
+        const QPointF *, int numPoints ) const;
+
+private:
+    // Disabled copy constructor and operator=
+    QwtSymbol( const QwtSymbol & );
+    QwtSymbol &operator=( const QwtSymbol & );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+/*!
+  \brief Draw the symbol at a specified position
+
+  \param painter Painter
+  \param pos Position of the symbol in screen coordinates
+*/
+inline void QwtSymbol::drawSymbol(
+    QPainter *painter, const QPointF &pos ) const
+{
+    drawSymbols( painter, &pos, 1 );
+}
+
+/*!
+  \brief Draw symbols at the specified points
+
+  \param painter Painter
+  \param points Positions of the symbols in screen coordinates
+*/
+
+inline void QwtSymbol::drawSymbols(
+    QPainter *painter, const QPolygonF &points ) const
+{
+    drawSymbols( painter, points.data(), points.size() );
+}
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_system_clock.h b/include/pli_vis/third_party/qwt/qwt_system_clock.h
new file mode 100644
index 0000000000000000000000000000000000000000..b458392e926657cfb06256c072b99bccf41b031d
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_system_clock.h
@@ -0,0 +1,47 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_SYSTEM_CLOCK_H
+#define QWT_SYSTEM_CLOCK_H
+
+#include "qwt/qwt_global.h"
+
+/*!
+  \brief QwtSystemClock provides high resolution clock time functions.
+
+  Sometimes the resolution offered by QTime ( millisecond ) is not accurate
+  enough for implementing time measurements ( f.e. sampling ).
+  QwtSystemClock offers a subset of the QTime functionality using higher
+  resolution timers ( if possible ).
+
+  Precision and time intervals are multiples of milliseconds (ms).
+
+  \note The implementation uses high-resolution performance counter on Windows,
+        mach_absolute_time() on the Mac or POSIX timers on other systems. 
+        If none is available it falls back on QTimer.
+*/
+
+class QWT_EXPORT QwtSystemClock
+{
+public:
+    QwtSystemClock();
+    virtual ~QwtSystemClock();
+
+    bool isNull() const;
+
+    void start();
+    double restart();
+    double elapsed() const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_text.h b/include/pli_vis/third_party/qwt/qwt_text.h
new file mode 100644
index 0000000000000000000000000000000000000000..3907d63d03e2f8621f03d26431d72b2fb124bcdc
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_text.h
@@ -0,0 +1,223 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_TEXT_H
+#define QWT_TEXT_H
+
+#include "qwt/qwt_global.h"
+#include <qstring.h>
+#include <qsize.h>
+#include <qfont.h>
+#include <qmetatype.h>
+
+class QColor;
+class QPen;
+class QBrush;
+class QRectF;
+class QPainter;
+class QwtTextEngine;
+
+/*!
+  \brief A class representing a text
+
+  A QwtText is a text including a set of attributes how to render it.
+
+  - Format\n
+    A text might include control sequences (f.e tags) describing
+    how to render it. Each format (f.e MathML, TeX, Qt Rich Text)
+    has its own set of control sequences, that can be handles by
+    a special QwtTextEngine for this format.
+  - Background\n
+    A text might have a background, defined by a QPen and QBrush
+    to improve its visibility. The corners of the background might
+    be rounded.
+  - Font\n
+    A text might have an individual font.
+  - Color\n
+    A text might have an individual color.
+  - Render Flags\n
+    Flags from Qt::AlignmentFlag and Qt::TextFlag used like in
+    QPainter::drawText().
+
+  \sa QwtTextEngine, QwtTextLabel
+*/
+
+class QWT_EXPORT QwtText
+{
+public:
+
+    /*!
+      \brief Text format
+
+      The text format defines the QwtTextEngine, that is used to render
+      the text.
+
+      \sa QwtTextEngine, setTextEngine()
+    */
+
+    enum TextFormat
+    {
+        /*!
+          The text format is determined using QwtTextEngine::mightRender() for
+          all available text engines in increasing order > PlainText.
+          If none of the text engines can render the text is rendered
+          like QwtText::PlainText.
+         */
+        AutoText = 0,
+
+        //! Draw the text as it is, using a QwtPlainTextEngine.
+        PlainText,
+
+        //! Use the Scribe framework (Qt Rich Text) to render the text.
+        RichText,
+
+        /*!
+          Use a MathML (http://en.wikipedia.org/wiki/MathML) render engine
+          to display the text. The Qwt MathML extension offers such an engine
+          based on the MathML renderer of the Qt solutions package. 
+          To enable MathML support the following code needs to be added to the
+          application:
+\verbatim QwtText::setTextEngine(QwtText::MathMLText, new QwtMathMLTextEngine()); \endverbatim
+         */
+        MathMLText,
+
+        /*!
+          Use a TeX (http://en.wikipedia.org/wiki/TeX) render engine
+          to display the text ( not implemented yet ).
+         */
+        TeXText,
+
+        /*!
+          The number of text formats can be extended using setTextEngine.
+          Formats >= QwtText::OtherFormat are not used by Qwt.
+         */
+        OtherFormat = 100
+    };
+
+    /*!
+      \brief Paint Attributes
+
+      Font and color and background are optional attributes of a QwtText.
+      The paint attributes hold the information, if they are set.
+    */
+    enum PaintAttribute
+    {
+        //! The text has an individual font.
+        PaintUsingTextFont = 0x01,
+
+        //! The text has an individual color.
+        PaintUsingTextColor = 0x02,
+
+        //! The text has an individual background.
+        PaintBackground = 0x04
+    };
+
+    //! Paint attributes
+    typedef QFlags<PaintAttribute> PaintAttributes;
+
+    /*!
+      \brief Layout Attributes
+      The layout attributes affects some aspects of the layout of the text.
+    */
+    enum LayoutAttribute
+    {
+        /*!
+          Layout the text without its margins. This mode is useful if a
+          text needs to be aligned accurately, like the tick labels of a scale.
+          If QwtTextEngine::textMargins is not implemented for the format
+          of the text, MinimumLayout has no effect.
+         */
+        MinimumLayout = 0x01
+    };
+
+    //! Layout attributes
+    typedef QFlags<LayoutAttribute> LayoutAttributes;
+
+    QwtText( const QString & = QString::null,
+             TextFormat textFormat = AutoText );
+    QwtText( const QwtText & );
+    ~QwtText();
+
+    QwtText &operator=( const QwtText & );
+
+    bool operator==( const QwtText & ) const;
+    bool operator!=( const QwtText & ) const;
+
+    void setText( const QString &,
+        QwtText::TextFormat textFormat = AutoText );
+    QString text() const;
+
+    bool isNull() const;
+    bool isEmpty() const;
+
+    void setFont( const QFont & );
+    QFont font() const;
+
+    QFont usedFont( const QFont & ) const;
+
+    void setRenderFlags( int flags );
+    int renderFlags() const;
+
+    void setColor( const QColor & );
+    QColor color() const;
+
+    QColor usedColor( const QColor & ) const;
+
+    void setBorderRadius( double );
+    double borderRadius() const;
+
+    void setBorderPen( const QPen & );
+    QPen borderPen() const;
+
+    void setBackgroundBrush( const QBrush & );
+    QBrush backgroundBrush() const;
+
+    void setPaintAttribute( PaintAttribute, bool on = true );
+    bool testPaintAttribute( PaintAttribute ) const;
+
+    void setLayoutAttribute( LayoutAttribute, bool on = true );
+    bool testLayoutAttribute( LayoutAttribute ) const;
+
+    double heightForWidth( double width, const QFont & = QFont() ) const;
+    QSizeF textSize( const QFont & = QFont() ) const;
+
+    void draw( QPainter *painter, const QRectF &rect ) const;
+
+    static const QwtTextEngine *textEngine( 
+        const QString &text, QwtText::TextFormat = AutoText );
+
+    static const QwtTextEngine *textEngine( QwtText::TextFormat );
+    static void setTextEngine( QwtText::TextFormat, QwtTextEngine * );
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+
+    class LayoutCache;
+    LayoutCache *d_layoutCache;
+};
+
+//! \return text().isNull()
+inline bool QwtText::isNull() const
+{
+    return text().isNull();
+}
+
+//! \return text().isEmpty()
+inline bool QwtText::isEmpty() const
+{
+    return text().isEmpty();
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtText::PaintAttributes )
+Q_DECLARE_OPERATORS_FOR_FLAGS( QwtText::LayoutAttributes )
+
+Q_DECLARE_METATYPE( QwtText )
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_text_engine.h b/include/pli_vis/third_party/qwt/qwt_text_engine.h
new file mode 100644
index 0000000000000000000000000000000000000000..27825c5f6cd7e415596f563220fdd99aa824b10f
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_text_engine.h
@@ -0,0 +1,172 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_TEXT_ENGINE_H
+#define QWT_TEXT_ENGINE_H 1
+
+#include "qwt/qwt_global.h"
+#include <qsize.h>
+
+class QFont;
+class QRectF;
+class QString;
+class QPainter;
+
+/*!
+  \brief Abstract base class for rendering text strings
+
+  A text engine is responsible for rendering texts for a
+  specific text format. They are used by QwtText to render a text.
+
+  QwtPlainTextEngine and QwtRichTextEngine are part of the Qwt library.
+  The implementation of QwtMathMLTextEngine uses code from the 
+  Qt solution package. Because of license implications it is built into
+  a separate library.
+ 
+  \sa QwtText::setTextEngine()
+*/
+
+class QWT_EXPORT QwtTextEngine
+{
+public:
+    virtual ~QwtTextEngine();
+
+    /*!
+      Find the height for a given width
+
+      \param font Font of the text
+      \param flags Bitwise OR of the flags used like in QPainter::drawText
+      \param text Text to be rendered
+      \param width Width
+
+      \return Calculated height
+     */
+    virtual double heightForWidth( const QFont &font, int flags,
+        const QString &text, double width ) const = 0;
+
+    /*!
+      Returns the size, that is needed to render text
+
+      \param font Font of the text
+      \param flags Bitwise OR of the flags like in for QPainter::drawText
+      \param text Text to be rendered
+
+      \return Calculated size
+     */
+    virtual QSizeF textSize( const QFont &font, int flags,
+        const QString &text ) const = 0;
+
+    /*!
+      Test if a string can be rendered by this text engine
+
+      \param text Text to be tested
+      \return true, if it can be rendered
+     */
+    virtual bool mightRender( const QString &text ) const = 0;
+
+    /*!
+      Return margins around the texts
+
+      The textSize might include margins around the
+      text, like QFontMetrics::descent(). In situations
+      where texts need to be aligned in detail, knowing
+      these margins might improve the layout calculations.
+
+      \param font Font of the text
+      \param text Text to be rendered
+      \param left Return value for the left margin
+      \param right Return value for the right margin
+      \param top Return value for the top margin
+      \param bottom Return value for the bottom margin
+     */
+    virtual void textMargins( const QFont &font, const QString &text,
+        double &left, double &right, double &top, double &bottom ) const = 0;
+
+    /*!
+      Draw the text in a clipping rectangle
+
+      \param painter Painter
+      \param rect Clipping rectangle
+      \param flags Bitwise OR of the flags like in for QPainter::drawText()
+      \param text Text to be rendered
+     */
+    virtual void draw( QPainter *painter, const QRectF &rect,
+        int flags, const QString &text ) const = 0;
+
+protected:
+    QwtTextEngine();
+};
+
+
+/*!
+  \brief A text engine for plain texts
+
+  QwtPlainTextEngine renders texts using the basic Qt classes
+  QPainter and QFontMetrics.
+*/
+class QWT_EXPORT QwtPlainTextEngine: public QwtTextEngine
+{
+public:
+    QwtPlainTextEngine();
+    virtual ~QwtPlainTextEngine();
+
+    virtual double heightForWidth( const QFont &font, int flags,
+        const QString &text, double width ) const;
+
+    virtual QSizeF textSize( const QFont &font, int flags,
+        const QString &text ) const;
+
+    virtual void draw( QPainter *painter, const QRectF &rect,
+        int flags, const QString &text ) const;
+
+    virtual bool mightRender( const QString & ) const;
+
+    virtual void textMargins( const QFont &, const QString &,
+        double &left, double &right, double &top, double &bottom ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+
+#ifndef QT_NO_RICHTEXT
+
+/*!
+  \brief A text engine for Qt rich texts
+
+  QwtRichTextEngine renders Qt rich texts using the classes
+  of the Scribe framework of Qt.
+*/
+class QWT_EXPORT QwtRichTextEngine: public QwtTextEngine
+{
+public:
+    QwtRichTextEngine();
+
+    virtual double heightForWidth( const QFont &font, int flags,
+        const QString &text, double width ) const;
+
+    virtual QSizeF textSize( const QFont &font, int flags,
+        const QString &text ) const;
+
+    virtual void draw( QPainter *painter, const QRectF &rect,
+        int flags, const QString &text ) const;
+
+    virtual bool mightRender( const QString & ) const;
+
+    virtual void textMargins( const QFont &, const QString &,
+        double &left, double &right, double &top, double &bottom ) const;
+
+private:
+    QString taggedText( const QString &, int flags ) const;
+};
+
+#endif // !QT_NO_RICHTEXT
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_text_label.h b/include/pli_vis/third_party/qwt/qwt_text_label.h
new file mode 100644
index 0000000000000000000000000000000000000000..00b48afb4624d993843aeefb72404b46c065be8f
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_text_label.h
@@ -0,0 +1,77 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_TEXT_LABEL_H
+#define QWT_TEXT_LABEL_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_text.h"
+#include <qframe.h>
+
+class QString;
+class QPaintEvent;
+class QPainter;
+
+/*!
+   \brief A Widget which displays a QwtText
+*/
+
+class QWT_EXPORT QwtTextLabel : public QFrame
+{
+    Q_OBJECT
+
+    Q_PROPERTY( int indent READ indent WRITE setIndent )
+    Q_PROPERTY( int margin READ margin WRITE setMargin )
+    Q_PROPERTY( QString plainText READ plainText WRITE setPlainText )
+
+public:
+    explicit QwtTextLabel( QWidget *parent = NULL );
+    explicit QwtTextLabel( const QwtText &, QWidget *parent = NULL );
+    virtual ~QwtTextLabel();
+
+    void setPlainText( const QString & );
+    QString plainText() const;
+
+public Q_SLOTS:
+    void setText( const QString &,
+        QwtText::TextFormat textFormat = QwtText::AutoText );
+    virtual void setText( const QwtText & );
+
+    void clear();
+
+public:
+    const QwtText &text() const;
+
+    int indent() const;
+    void setIndent( int );
+
+    int margin() const;
+    void setMargin( int );
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+    virtual int heightForWidth( int ) const;
+
+    QRect textRect() const;
+
+    virtual void drawText( QPainter *, const QRectF & );
+
+protected:
+    virtual void paintEvent( QPaintEvent *e );
+    virtual void drawContents( QPainter * );
+
+private:
+    void init();
+    int defaultIndent() const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_thermo.h b/include/pli_vis/third_party/qwt/qwt_thermo.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4b1ee26d0aa459d7ce49213ed9536c1364e9947
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_thermo.h
@@ -0,0 +1,178 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_THERMO_H
+#define QWT_THERMO_H
+
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_abstract_scale.h"
+#include "qwt/qwt_interval.h"
+
+class QwtScaleDraw;
+class QwtColorMap;
+
+/*!
+  \brief The Thermometer Widget
+
+  QwtThermo is a widget which displays a value in an interval. It supports:
+  - a horizontal or vertical layout;
+  - a range;
+  - a scale;
+  - an alarm level.
+
+  \image html sysinfo.png
+
+  The fill colors might be calculated from an optional color map
+  If no color map has been assigned QwtThermo uses the 
+  following colors/brushes from the widget palette:
+
+  - QPalette::Base
+    Background of the pipe
+  - QPalette::ButtonText
+    Fill brush below the alarm level
+  - QPalette::Highlight
+    Fill brush for the values above the alarm level
+  - QPalette::WindowText
+    For the axis of the scale
+  - QPalette::Text
+    For the labels of the scale
+*/
+class QWT_EXPORT QwtThermo: public QwtAbstractScale
+{
+    Q_OBJECT
+
+    Q_ENUMS( ScalePosition )
+    Q_ENUMS( OriginMode )
+
+    Q_PROPERTY( Qt::Orientation orientation
+        READ orientation WRITE setOrientation )
+    Q_PROPERTY( ScalePosition scalePosition 
+        READ scalePosition WRITE setScalePosition )
+    Q_PROPERTY( OriginMode originMode READ originMode WRITE setOriginMode )
+
+    Q_PROPERTY( bool alarmEnabled READ alarmEnabled WRITE setAlarmEnabled )
+    Q_PROPERTY( double alarmLevel READ alarmLevel WRITE setAlarmLevel )
+    Q_PROPERTY( double origin READ origin WRITE setOrigin )
+    Q_PROPERTY( int spacing READ spacing WRITE setSpacing )
+    Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth )
+    Q_PROPERTY( int pipeWidth READ pipeWidth WRITE setPipeWidth )
+    Q_PROPERTY( double value READ value WRITE setValue )
+
+public:
+
+    /*!
+      Position of the scale
+      \sa setScalePosition(), setOrientation()
+     */
+    enum ScalePosition
+    {
+        //! The slider has no scale
+        NoScale,
+
+        //! The scale is right of a vertical or below of a horizontal slider
+        LeadingScale,
+
+        //! The scale is left of a vertical or above of a horizontal slider
+        TrailingScale
+    };
+
+    /*!
+      Origin mode. This property specifies where the beginning of the liquid
+      is placed.
+
+      \sa setOriginMode(), setOrigin()
+    */
+    enum OriginMode
+    {
+        //! The origin is the minimum of the scale
+        OriginMinimum,
+
+        //! The origin is the maximum of the scale
+        OriginMaximum,
+
+        //! The origin is specified using the origin() property
+        OriginCustom
+    };
+
+    explicit QwtThermo( QWidget *parent = NULL );
+    virtual ~QwtThermo();
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation() const;
+
+    void setScalePosition( ScalePosition );
+    ScalePosition scalePosition() const;
+
+    void setSpacing( int );
+    int spacing() const;
+
+    void setBorderWidth( int w );
+    int borderWidth() const;
+
+    void setOriginMode( OriginMode );
+    OriginMode originMode() const;
+
+    void setOrigin( double );
+    double origin() const;
+
+    void setFillBrush( const QBrush &b );
+    QBrush fillBrush() const;
+
+    void setAlarmBrush( const QBrush &b );
+    QBrush alarmBrush() const;
+
+    void setAlarmLevel( double v );
+    double alarmLevel() const;
+
+    void setAlarmEnabled( bool tf );
+    bool alarmEnabled() const;
+
+    void setColorMap( QwtColorMap * );
+    QwtColorMap *colorMap();
+    const QwtColorMap *colorMap() const;
+
+    void setPipeWidth( int w );
+    int pipeWidth() const;
+
+    void setRangeFlags( QwtInterval::BorderFlags );
+    QwtInterval::BorderFlags rangeFlags() const;
+
+    double value() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    void setScaleDraw( QwtScaleDraw * );
+    const QwtScaleDraw *scaleDraw() const;
+
+public Q_SLOTS:
+    virtual void setValue( double val );
+
+protected:
+    virtual void drawLiquid( QPainter *, const QRect & ) const;
+    virtual void scaleChange();
+
+    virtual void paintEvent( QPaintEvent * );
+    virtual void resizeEvent( QResizeEvent * );
+    virtual void changeEvent( QEvent * );
+
+    QwtScaleDraw *scaleDraw();
+
+    QRect pipeRect() const;
+    QRect fillRect( const QRect & ) const;
+    QRect alarmRect( const QRect & ) const;
+
+private:
+    void layoutThermo( bool );
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_transform.h b/include/pli_vis/third_party/qwt/qwt_transform.h
new file mode 100644
index 0000000000000000000000000000000000000000..7e271123c87c31c6d347c4ac9f468a31186b7e40
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_transform.h
@@ -0,0 +1,142 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_TRANSFORM_H
+#define QWT_TRANSFORM_H
+
+#include "qwt/qwt_global.h"
+
+/*!
+   \brief A transformation between coordinate systems
+
+   QwtTransform manipulates values, when being mapped between
+   the scale and the paint device coordinate system.
+
+   A transformation consists of 2 methods:
+
+   - transform
+   - invTransform
+
+   where one is is the inverse function of the other.
+
+   When p1, p2 are the boundaries of the paint device coordinates
+   and s1, s2 the boundaries of the scale, QwtScaleMap uses the
+   following calculations:
+
+   - p = p1 + ( p2 - p1 ) * ( T( s ) - T( s1 ) / ( T( s2 ) - T( s1 ) );
+   - s = invT ( T( s1 ) + ( T( s2 ) - T( s1 ) ) * ( p - p1 ) / ( p2 - p1 ) );
+*/
+class QWT_EXPORT QwtTransform
+{
+public:
+    QwtTransform();
+    virtual ~QwtTransform();
+
+    /*!
+       Modify value to be a valid value for the transformation.
+       The default implementation does nothing.
+     */
+    virtual double bounded( double value ) const;
+
+    /*!
+        Transformation function
+
+        \param value Value
+        \return Modified value
+
+        \sa invTransform()
+     */
+    virtual double transform( double value ) const = 0;
+
+    /*!
+        Inverse transformation function
+
+        \param value Value
+        \return Modified value
+
+        \sa transform()
+     */
+    virtual double invTransform( double value ) const = 0;
+
+    //! Virtualized copy operation
+    virtual QwtTransform *copy() const = 0;
+};
+
+/*!
+   \brief Null transformation
+
+   QwtNullTransform returns the values unmodified.
+   
+ */
+class QWT_EXPORT QwtNullTransform: public QwtTransform
+{
+public:
+    QwtNullTransform();
+    virtual ~QwtNullTransform();
+
+    virtual double transform( double value ) const;
+    virtual double invTransform( double value ) const;
+
+    virtual QwtTransform *copy() const;
+};
+/*!
+   \brief Logarithmic transformation
+
+   QwtLogTransform modifies the values using log() and exp().
+
+   \note In the calculations of QwtScaleMap the base of the log function
+         has no effect on the mapping. So QwtLogTransform can be used 
+         for log2(), log10() or any other logarithmic scale.
+ */
+class QWT_EXPORT QwtLogTransform: public QwtTransform
+{   
+public:
+    QwtLogTransform();
+    virtual ~QwtLogTransform();
+    
+    virtual double transform( double value ) const;
+    virtual double invTransform( double value ) const;
+
+    virtual double bounded( double value ) const;
+
+    virtual QwtTransform *copy() const;
+
+#if QT_VERSION >= 0x050400
+    static const double LogMin;
+    static const double LogMax;
+#else
+    QT_STATIC_CONST double LogMin;
+    QT_STATIC_CONST double LogMax;
+#endif
+};
+
+/*!
+   \brief A transformation using pow()
+
+   QwtPowerTransform preserves the sign of a value. 
+   F.e. a transformation with a factor of 2
+   transforms a value of -3 to -9 and v.v. Thus QwtPowerTransform
+   can be used for scales including negative values.
+ */
+class QWT_EXPORT QwtPowerTransform: public QwtTransform
+{
+public:
+    QwtPowerTransform( double exponent );
+    virtual ~QwtPowerTransform();
+
+    virtual double transform( double value ) const;
+    virtual double invTransform( double value ) const;
+
+    virtual QwtTransform *copy() const;
+
+private:
+    const double d_exponent;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_wheel.h b/include/pli_vis/third_party/qwt/qwt_wheel.h
new file mode 100644
index 0000000000000000000000000000000000000000..92f1e883de5e132a019a99deba2d37b2db2c1af6
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_wheel.h
@@ -0,0 +1,178 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_WHEEL_H
+#define QWT_WHEEL_H
+
+#include "qwt/qwt_global.h"
+#include <qwidget.h>
+
+/*!
+  \brief The Wheel Widget
+
+  The wheel widget can be used to change values over a very large range
+  in very small steps. Using the setMass() member, it can be configured
+  as a flying wheel.
+
+  The default range of the wheel is [0.0, 100.0]
+
+  \sa The radio example.
+*/
+class QWT_EXPORT QwtWheel: public QWidget
+{
+    Q_OBJECT
+
+    Q_PROPERTY( Qt::Orientation orientation
+                READ orientation WRITE setOrientation )
+
+    Q_PROPERTY( double value READ value WRITE setValue )
+    Q_PROPERTY( double minimum READ minimum WRITE setMinimum )
+    Q_PROPERTY( double maximum READ maximum WRITE setMaximum )
+
+    Q_PROPERTY( double singleStep READ singleStep WRITE setSingleStep )
+    Q_PROPERTY( int pageStepCount READ pageStepCount WRITE setPageStepCount )
+    Q_PROPERTY( bool stepAlignment READ stepAlignment WRITE setStepAlignment )
+
+    Q_PROPERTY( bool tracking READ isTracking WRITE setTracking )
+    Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping )
+    Q_PROPERTY( bool inverted READ isInverted WRITE setInverted )
+
+    Q_PROPERTY( double mass READ mass WRITE setMass )
+    Q_PROPERTY( int updateInterval READ updateInterval WRITE setUpdateInterval )
+
+    Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle )
+    Q_PROPERTY( double viewAngle READ viewAngle WRITE setViewAngle )
+    Q_PROPERTY( int tickCount READ tickCount WRITE setTickCount )
+    Q_PROPERTY( int wheelWidth READ wheelWidth WRITE setWheelWidth )
+    Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth )
+    Q_PROPERTY( int wheelBorderWidth READ wheelBorderWidth WRITE setWheelBorderWidth )
+
+public:
+    explicit QwtWheel( QWidget *parent = NULL );
+    virtual ~QwtWheel();
+
+    double value() const;
+
+    void setOrientation( Qt::Orientation );
+    Qt::Orientation orientation() const;
+
+    double totalAngle() const;
+    double viewAngle() const;
+
+    void setTickCount( int );
+    int tickCount() const;
+
+    void setWheelWidth( int );
+    int wheelWidth() const;
+
+    void setWheelBorderWidth( int );
+    int wheelBorderWidth() const;
+
+    void setBorderWidth( int );
+    int borderWidth() const;
+
+    void setInverted( bool tf );
+    bool isInverted() const;
+
+    void setWrapping( bool tf );
+    bool wrapping() const;
+
+    void setSingleStep( double );
+    double singleStep() const;
+
+    void setPageStepCount( int );
+    int pageStepCount() const;
+
+    void setStepAlignment( bool on );
+    bool stepAlignment() const;
+
+    void setRange( double vmin, double vmax );
+
+    void setMinimum( double min );
+    double minimum() const;
+
+    void setMaximum( double max );
+    double maximum() const;
+
+    void setUpdateInterval( int );
+    int updateInterval() const;
+
+    void setTracking( bool enable );
+    bool isTracking() const;
+
+    double mass() const;
+
+public Q_SLOTS:
+    void setValue( double );
+    void setTotalAngle ( double );
+    void setViewAngle( double );
+    void setMass( double );
+
+Q_SIGNALS:
+
+    /*!
+      \brief Notify a change of value.
+
+      When tracking is enabled this signal will be emitted every
+      time the value changes. 
+
+      \param value new value
+      \sa setTracking()
+    */
+    void valueChanged( double value );
+
+    /*!
+      This signal is emitted when the user presses the
+      the wheel with the mouse
+    */
+    void wheelPressed();
+
+    /*!
+      This signal is emitted when the user releases the mouse
+    */
+    void wheelReleased();
+
+    /*!
+      This signal is emitted when the user moves the
+      wheel with the mouse.
+
+      \param value new value
+    */
+    void wheelMoved( double value );
+
+protected:
+    virtual void paintEvent( QPaintEvent * );
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void mouseReleaseEvent( QMouseEvent * );
+    virtual void mouseMoveEvent( QMouseEvent * );
+    virtual void keyPressEvent( QKeyEvent * );
+    virtual void wheelEvent( QWheelEvent * );
+    virtual void timerEvent( QTimerEvent * );
+
+    void stopFlying();
+
+    QRect wheelRect() const;
+
+    virtual QSize sizeHint() const;
+    virtual QSize minimumSizeHint() const;
+
+    virtual void drawTicks( QPainter *, const QRectF & );
+    virtual void drawWheelBackground( QPainter *, const QRectF & );
+
+    virtual double valueAt( const QPoint & ) const;
+
+private:
+    double alignedValue( double ) const;
+    double boundedValue( double ) const;
+
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/qwt_widget_overlay.h b/include/pli_vis/third_party/qwt/qwt_widget_overlay.h
new file mode 100644
index 0000000000000000000000000000000000000000..d2abf0a412d81497e1e67e321dbbddb0b4d4df95
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/qwt_widget_overlay.h
@@ -0,0 +1,148 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#ifndef QWT_WIDGET_OVERLAY_H
+#define QWT_WIDGET_OVERLAY_H
+
+#include "qwt/qwt_global.h"
+#include <qwidget.h>
+#include <qregion.h>
+
+class QPainter;
+
+/*!
+   \brief An overlay for a widget
+
+   The main use case of an widget overlay is to avoid
+   heavy repaint operation of the widget below.
+
+   F.e. in combination with the plot canvas an overlay 
+   avoid replots as the content of the canvas can be restored from 
+   its backing store.
+
+   QwtWidgetOverlay is an abstract base class. Deriving classes are
+   supposed to reimplement the following methods:
+
+   - drawOverlay()
+   - maskHint()
+
+   Internally QwtPlotPicker uses overlays for displaying 
+   the rubber band and the tracker text.
+
+   \sa QwtPlotCanvas::BackingStore
+ */
+class QWT_EXPORT QwtWidgetOverlay: public QWidget
+{
+public:
+    /*!
+       \brief Mask mode
+
+       When using masks the widget below gets paint events for
+       the masked regions of the overlay only. Otherwise
+       Qt triggers full repaints. On less powerful hardware
+       ( f.e embedded systems ) - or when using the raster paint 
+       engine on a remote desktop - bit blitting is a noticeable
+       operation, that needs to be avoided.
+       
+       If and how to mask depends on how expensive the calculation 
+       of the mask is and how many pixels can be excluded by the mask.
+
+       The default setting is MaskHint.
+
+       \sa setMaskMode(), maskMode()
+     */
+    enum MaskMode
+    {
+        //! Don't use a mask.
+        NoMask,
+
+        /*!
+           \brief Use maskHint() as mask
+
+           For many situations a fast approximation is good enough 
+           and it is not necessary to build a more detailed mask
+           ( f.e the bounding rectangle of a text ).
+         */
+        MaskHint,
+
+        /*!
+           \brief Calculate a mask by checking the alpha values
+
+           Sometimes it is not possible to give a fast approximation
+           and the mask needs to be calculated by drawing the overlay
+           and testing the result.
+          
+           When a valid maskHint() is available
+           only pixels inside this approximation are checked.
+         */
+        AlphaMask
+    };
+
+    /*!
+       \brief Render mode
+
+       For calculating the alpha mask the overlay has already
+       been painted to a temporary QImage. Instead of rendering
+       the overlay twice this buffer can be copied for drawing
+       the overlay.
+
+       On graphic systems using the raster paint engine ( QWS, Windows )
+       it means usually copying some memory only. On X11 it results in an
+       expensive operation building a pixmap and for simple overlays
+       it might not be recommended.
+
+       \note The render mode has no effect, when maskMode() != AlphaMask.
+     */
+    enum RenderMode
+    {
+        //! Copy the buffer, when using the raster paint engine.
+        AutoRenderMode,
+
+        //! Always copy the buffer
+        CopyAlphaMask,
+
+        //! Never copy the buffer
+        DrawOverlay
+    };
+
+    QwtWidgetOverlay( QWidget* );
+    virtual ~QwtWidgetOverlay();
+
+    void setMaskMode( MaskMode );
+    MaskMode maskMode() const;
+
+    void setRenderMode( RenderMode );
+    RenderMode renderMode() const;
+
+    void updateOverlay();
+
+    virtual bool eventFilter( QObject *, QEvent *);
+
+protected:
+    virtual void paintEvent( QPaintEvent* event );
+    virtual void resizeEvent( QResizeEvent* event );
+
+    virtual QRegion maskHint() const;
+
+    /*!
+       Draw the widget overlay
+       \param painter Painter
+     */
+    virtual void drawOverlay( QPainter *painter ) const = 0;
+
+private:
+    void updateMask();
+    void draw( QPainter * ) const;
+
+private:
+    class PrivateData;
+    PrivateData *d_data;
+};
+
+#endif
diff --git a/include/pli_vis/third_party/qwt/src.pri b/include/pli_vis/third_party/qwt/src.pri
new file mode 100644
index 0000000000000000000000000000000000000000..65d79a397eb5c0661e2acfd3261a37b623802a01
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/src.pri
@@ -0,0 +1,247 @@
+################################################################
+# Qwt Widget Library
+# Copyright (C) 1997   Josef Wilgen
+# Copyright (C) 2002   Uwe Rathmann
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the Qwt License, Version 1.0
+################################################################
+
+
+HEADERS += \
+    qwt.h \
+    qwt_abstract_scale_draw.h \
+    qwt_clipper.h \
+    qwt_color_map.h \
+    qwt_compat.h \
+    qwt_column_symbol.h \
+    qwt_date.h \
+    qwt_date_scale_draw.h \
+    qwt_date_scale_engine.h \
+    qwt_dyngrid_layout.h \
+    qwt_global.h \
+    qwt_graphic.h \
+    qwt_interval.h \
+    qwt_interval_symbol.h \
+    qwt_math.h \
+    qwt_magnifier.h \
+    qwt_null_paintdevice.h \
+    qwt_painter.h \
+    qwt_painter_command.h \
+    qwt_panner.h \
+    qwt_picker.h \
+    qwt_picker_machine.h \
+    qwt_pixel_matrix.h \
+    qwt_point_3d.h \
+    qwt_point_polar.h \
+    qwt_round_scale_draw.h \
+    qwt_scale_div.h \
+    qwt_scale_draw.h \
+    qwt_scale_engine.h \
+    qwt_scale_map.h \
+    qwt_spline.h \
+    qwt_symbol.h \
+    qwt_system_clock.h \
+    qwt_text_engine.h \
+    qwt_text_label.h \
+    qwt_text.h \
+    qwt_transform.h \
+    qwt_widget_overlay.h
+
+SOURCES += \
+    qwt_abstract_scale_draw.cpp \
+    qwt_clipper.cpp \
+    qwt_color_map.cpp \
+    qwt_column_symbol.cpp \
+    qwt_date.cpp \
+    qwt_date_scale_draw.cpp \
+    qwt_date_scale_engine.cpp \
+    qwt_dyngrid_layout.cpp \
+    qwt_event_pattern.cpp \
+    qwt_graphic.cpp \
+    qwt_interval.cpp \
+    qwt_interval_symbol.cpp \
+    qwt_math.cpp \
+    qwt_magnifier.cpp \
+    qwt_null_paintdevice.cpp \
+    qwt_painter.cpp \
+    qwt_painter_command.cpp \
+    qwt_panner.cpp \
+    qwt_picker.cpp \
+    qwt_picker_machine.cpp \
+    qwt_pixel_matrix.cpp \
+    qwt_point_3d.cpp \
+    qwt_point_polar.cpp \
+    qwt_round_scale_draw.cpp \
+    qwt_scale_div.cpp \
+    qwt_scale_draw.cpp \
+    qwt_scale_map.cpp \
+    qwt_spline.cpp \
+    qwt_scale_engine.cpp \
+    qwt_symbol.cpp \
+    qwt_system_clock.cpp \
+    qwt_text_engine.cpp \
+    qwt_text_label.cpp \
+    qwt_text.cpp \
+    qwt_transform.cpp \
+    qwt_widget_overlay.cpp
+
+ 
+contains(QWT_CONFIG, QwtPlot) {
+
+    HEADERS += \
+        qwt_curve_fitter.h \
+        qwt_event_pattern.h \
+        qwt_abstract_legend.h \
+        qwt_legend.h \
+        qwt_legend_data.h \
+        qwt_legend_label.h \
+        qwt_plot.h \
+        qwt_plot_renderer.h \
+        qwt_plot_curve.h \
+        qwt_plot_dict.h \
+        qwt_plot_directpainter.h \
+        qwt_plot_grid.h \
+        qwt_plot_histogram.h \
+        qwt_plot_item.h \
+        qwt_plot_abstract_barchart.h \
+        qwt_plot_barchart.h \
+        qwt_plot_multi_barchart.h \
+        qwt_plot_intervalcurve.h \
+        qwt_plot_tradingcurve.h \
+        qwt_plot_layout.h \
+        qwt_plot_marker.h \
+        qwt_plot_zoneitem.h \
+        qwt_plot_textlabel.h \
+        qwt_plot_rasteritem.h \
+        qwt_plot_spectrogram.h \
+        qwt_plot_spectrocurve.h \
+        qwt_plot_scaleitem.h \
+        qwt_plot_legenditem.h \
+        qwt_plot_seriesitem.h \
+        qwt_plot_shapeitem.h \
+        qwt_plot_canvas.h \
+        qwt_plot_panner.h \
+        qwt_plot_picker.h \
+        qwt_plot_zoomer.h \
+        qwt_plot_magnifier.h \
+        qwt_plot_rescaler.h \
+        qwt_point_mapper.h \
+        qwt_raster_data.h \
+        qwt_matrix_raster_data.h \
+        qwt_sampling_thread.h \
+        qwt_samples.h \
+        qwt_series_data.h \
+        qwt_series_store.h \
+        qwt_point_data.h \
+        qwt_scale_widget.h 
+
+    SOURCES += \
+        qwt_curve_fitter.cpp \
+        qwt_abstract_legend.cpp \
+        qwt_legend.cpp \
+        qwt_legend_data.cpp \
+        qwt_legend_label.cpp \
+        qwt_plot.cpp \
+        qwt_plot_renderer.cpp \
+        qwt_plot_xml.cpp \
+        qwt_plot_axis.cpp \
+        qwt_plot_curve.cpp \
+        qwt_plot_dict.cpp \
+        qwt_plot_directpainter.cpp \
+        qwt_plot_grid.cpp \
+        qwt_plot_histogram.cpp \
+        qwt_plot_item.cpp \
+        qwt_plot_abstract_barchart.cpp \
+        qwt_plot_barchart.cpp \
+        qwt_plot_multi_barchart.cpp \
+        qwt_plot_intervalcurve.cpp \
+        qwt_plot_zoneitem.cpp \
+        qwt_plot_tradingcurve.cpp \
+        qwt_plot_spectrogram.cpp \
+        qwt_plot_spectrocurve.cpp \
+        qwt_plot_scaleitem.cpp \
+        qwt_plot_legenditem.cpp \
+        qwt_plot_seriesitem.cpp \
+        qwt_plot_shapeitem.cpp \
+        qwt_plot_marker.cpp \
+        qwt_plot_textlabel.cpp \
+        qwt_plot_layout.cpp \
+        qwt_plot_canvas.cpp \
+        qwt_plot_panner.cpp \
+        qwt_plot_rasteritem.cpp \
+        qwt_plot_picker.cpp \
+        qwt_plot_zoomer.cpp \
+        qwt_plot_magnifier.cpp \
+        qwt_plot_rescaler.cpp \
+        qwt_point_mapper.cpp \
+        qwt_raster_data.cpp \
+        qwt_matrix_raster_data.cpp \
+        qwt_sampling_thread.cpp \
+        qwt_series_data.cpp \
+        qwt_point_data.cpp \
+        qwt_scale_widget.cpp 
+}
+
+greaterThan(QT_MAJOR_VERSION, 4) {
+
+    QT += printsupport
+    QT += concurrent
+} 
+
+contains(QWT_CONFIG, QwtSvg) {
+
+    QT += svg
+
+    HEADERS += qwt_plot_svgitem.h
+    SOURCES += qwt_plot_svgitem.cpp 
+}
+else {
+
+    DEFINES += QWT_NO_SVG
+}
+
+contains(QWT_CONFIG, QwtOpenGL) {
+
+    QT += opengl
+
+    HEADERS += qwt_plot_glcanvas.h
+    SOURCES += qwt_plot_glcanvas.cpp
+}
+else {
+
+    DEFINES += QWT_NO_OPENGL
+}
+
+contains(QWT_CONFIG, QwtWidgets) {
+
+    HEADERS += \
+        qwt_abstract_slider.h \
+        qwt_abstract_scale.h \
+        qwt_arrow_button.h \
+        qwt_analog_clock.h \
+        qwt_compass.h \
+        qwt_compass_rose.h \
+        qwt_counter.h \
+        qwt_dial.h \
+        qwt_dial_needle.h \
+        qwt_knob.h \
+        qwt_slider.h \
+        qwt_thermo.h \
+        qwt_wheel.h
+    
+    SOURCES += \
+        qwt_abstract_slider.cpp \
+        qwt_abstract_scale.cpp \
+        qwt_arrow_button.cpp \
+        qwt_analog_clock.cpp \
+        qwt_compass.cpp \
+        qwt_compass_rose.cpp \
+        qwt_counter.cpp \
+        qwt_dial.cpp \
+        qwt_dial_needle.cpp \
+        qwt_knob.cpp \
+        qwt_slider.cpp \
+        qwt_thermo.cpp \
+        qwt_wheel.cpp
+}
diff --git a/include/pli_vis/third_party/qwt/src.pro b/include/pli_vis/third_party/qwt/src.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a352b9921dbcc372090f7a7a538867773d0a0742
--- /dev/null
+++ b/include/pli_vis/third_party/qwt/src.pro
@@ -0,0 +1,103 @@
+################################################################
+# Qwt Widget Library
+# Copyright (C) 1997   Josef Wilgen
+# Copyright (C) 2002   Uwe Rathmann
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the Qwt License, Version 1.0
+################################################################
+
+# qmake project file for building the qwt libraries
+
+QWT_ROOT = $${PWD}/..
+include( $${QWT_ROOT}/qwtconfig.pri )
+include( $${QWT_ROOT}/qwtbuild.pri )
+include( $${QWT_ROOT}/qwtfunctions.pri )
+
+QWT_OUT_ROOT = $${OUT_PWD}/..
+
+TEMPLATE          = lib
+TARGET            = $$qwtLibraryTarget(qwt)
+
+DESTDIR           = $${QWT_OUT_ROOT}/lib
+
+contains(QWT_CONFIG, QwtDll) {
+
+    CONFIG += dll
+    win32|symbian: DEFINES += QT_DLL QWT_DLL QWT_MAKEDLL
+}
+else {
+    CONFIG += staticlib
+} 
+
+contains(QWT_CONFIG, QwtFramework) {
+
+    CONFIG += lib_bundle
+}
+
+include ( $${PWD}/src.pri )
+
+# Install directives
+
+target.path    = $${QWT_INSTALL_LIBS}
+INSTALLS       = target 
+
+CONFIG(lib_bundle) {
+
+    FRAMEWORK_HEADERS.version = Versions
+    FRAMEWORK_HEADERS.files = $${HEADERS}
+    FRAMEWORK_HEADERS.path = Headers
+    QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS
+}
+else {
+
+    headers.files  = $${HEADERS}
+    headers.path   = $${QWT_INSTALL_HEADERS}
+    INSTALLS += headers
+}
+
+contains(QWT_CONFIG, QwtPkgConfig) {
+
+    CONFIG     += create_pc create_prl no_install_prl
+
+    QMAKE_PKGCONFIG_NAME = Qwt$${QWT_VER_MAJ}
+    QMAKE_PKGCONFIG_DESCRIPTION = Qt Widgets for Technical Applications
+
+    QMAKE_PKGCONFIG_LIBDIR = $${QWT_INSTALL_LIBS}
+    QMAKE_PKGCONFIG_INCDIR = $${QWT_INSTALL_HEADERS}
+
+    QMAKE_PKGCONFIG_DESTDIR = pkgconfig
+
+    greaterThan(QT_MAJOR_VERSION, 4) {
+
+        QMAKE_PKGCONFIG_FILE = Qt$${QT_MAJOR_VERSION}$${QMAKE_PKGCONFIG_NAME}
+        QMAKE_PKGCONFIG_REQUIRES = Qt5Widgets Qt5Concurrent Qt5PrintSupport
+
+        contains(QWT_CONFIG, QwtSvg) {
+            QMAKE_PKGCONFIG_REQUIRES += Qt5Svg
+        }
+
+        contains(QWT_CONFIG, QwtOpenGL) {
+            QMAKE_PKGCONFIG_REQUIRES += Qt5OpenGL
+        }
+
+        QMAKE_DISTCLEAN += $${DESTDIR}/$${QMAKE_PKGCONFIG_DESTDIR}/$${QMAKE_PKGCONFIG_FILE}.pc
+    }
+    else {
+
+        # there is no QMAKE_PKGCONFIG_FILE fo Qt4
+        QMAKE_PKGCONFIG_REQUIRES = QtGui 
+
+        contains(QWT_CONFIG, QwtSvg) {
+            QMAKE_PKGCONFIG_REQUIRES += QtSvg
+        }
+
+        contains(QWT_CONFIG, QwtOpenGL) {
+            QMAKE_PKGCONFIG_REQUIRES += QtOpenGL
+        }
+
+        QMAKE_DISTCLEAN += $${DESTDIR}/$${QMAKE_PKGCONFIG_DESTDIR}/$${TARGET}.pc
+    }
+
+    QMAKE_DISTCLEAN += $${DESTDIR}/libqwt.prl
+}
diff --git a/include/third_party/qxt/QxtGlobal.h b/include/pli_vis/third_party/qxt/QxtGlobal.h
similarity index 100%
rename from include/third_party/qxt/QxtGlobal.h
rename to include/pli_vis/third_party/qxt/QxtGlobal.h
diff --git a/include/third_party/qxt/QxtLetterBoxWidget.h b/include/pli_vis/third_party/qxt/QxtLetterBoxWidget.h
similarity index 100%
rename from include/third_party/qxt/QxtLetterBoxWidget.h
rename to include/pli_vis/third_party/qxt/QxtLetterBoxWidget.h
diff --git a/include/third_party/qxt/QxtLetterBoxWidgetP.h b/include/pli_vis/third_party/qxt/QxtLetterBoxWidgetP.h
similarity index 100%
rename from include/third_party/qxt/QxtLetterBoxWidgetP.h
rename to include/pli_vis/third_party/qxt/QxtLetterBoxWidgetP.h
diff --git a/include/third_party/qxt/QxtSpanSlider.h b/include/pli_vis/third_party/qxt/QxtSpanSlider.h
similarity index 100%
rename from include/third_party/qxt/QxtSpanSlider.h
rename to include/pli_vis/third_party/qxt/QxtSpanSlider.h
diff --git a/include/third_party/qxt/QxtSpanSliderP.h b/include/pli_vis/third_party/qxt/QxtSpanSliderP.h
similarity index 100%
rename from include/third_party/qxt/QxtSpanSliderP.h
rename to include/pli_vis/third_party/qxt/QxtSpanSliderP.h
diff --git a/include/third_party/spdlog/async_logger.h b/include/pli_vis/third_party/spdlog/async_logger.h
similarity index 100%
rename from include/third_party/spdlog/async_logger.h
rename to include/pli_vis/third_party/spdlog/async_logger.h
diff --git a/include/third_party/spdlog/common.h b/include/pli_vis/third_party/spdlog/common.h
similarity index 100%
rename from include/third_party/spdlog/common.h
rename to include/pli_vis/third_party/spdlog/common.h
diff --git a/include/third_party/spdlog/details/async_log_helper.h b/include/pli_vis/third_party/spdlog/details/async_log_helper.h
similarity index 100%
rename from include/third_party/spdlog/details/async_log_helper.h
rename to include/pli_vis/third_party/spdlog/details/async_log_helper.h
diff --git a/include/third_party/spdlog/details/async_logger_impl.h b/include/pli_vis/third_party/spdlog/details/async_logger_impl.h
similarity index 100%
rename from include/third_party/spdlog/details/async_logger_impl.h
rename to include/pli_vis/third_party/spdlog/details/async_logger_impl.h
diff --git a/include/third_party/spdlog/details/file_helper.h b/include/pli_vis/third_party/spdlog/details/file_helper.h
similarity index 100%
rename from include/third_party/spdlog/details/file_helper.h
rename to include/pli_vis/third_party/spdlog/details/file_helper.h
diff --git a/include/third_party/spdlog/details/log_msg.h b/include/pli_vis/third_party/spdlog/details/log_msg.h
similarity index 100%
rename from include/third_party/spdlog/details/log_msg.h
rename to include/pli_vis/third_party/spdlog/details/log_msg.h
diff --git a/include/third_party/spdlog/details/logger_impl.h b/include/pli_vis/third_party/spdlog/details/logger_impl.h
similarity index 100%
rename from include/third_party/spdlog/details/logger_impl.h
rename to include/pli_vis/third_party/spdlog/details/logger_impl.h
diff --git a/include/third_party/spdlog/details/mpmc_bounded_q.h b/include/pli_vis/third_party/spdlog/details/mpmc_bounded_q.h
similarity index 100%
rename from include/third_party/spdlog/details/mpmc_bounded_q.h
rename to include/pli_vis/third_party/spdlog/details/mpmc_bounded_q.h
diff --git a/include/third_party/spdlog/details/null_mutex.h b/include/pli_vis/third_party/spdlog/details/null_mutex.h
similarity index 100%
rename from include/third_party/spdlog/details/null_mutex.h
rename to include/pli_vis/third_party/spdlog/details/null_mutex.h
diff --git a/include/third_party/spdlog/details/os.h b/include/pli_vis/third_party/spdlog/details/os.h
similarity index 100%
rename from include/third_party/spdlog/details/os.h
rename to include/pli_vis/third_party/spdlog/details/os.h
diff --git a/include/third_party/spdlog/details/pattern_formatter_impl.h b/include/pli_vis/third_party/spdlog/details/pattern_formatter_impl.h
similarity index 100%
rename from include/third_party/spdlog/details/pattern_formatter_impl.h
rename to include/pli_vis/third_party/spdlog/details/pattern_formatter_impl.h
diff --git a/include/third_party/spdlog/details/registry.h b/include/pli_vis/third_party/spdlog/details/registry.h
similarity index 100%
rename from include/third_party/spdlog/details/registry.h
rename to include/pli_vis/third_party/spdlog/details/registry.h
diff --git a/include/third_party/spdlog/details/spdlog_impl.h b/include/pli_vis/third_party/spdlog/details/spdlog_impl.h
similarity index 100%
rename from include/third_party/spdlog/details/spdlog_impl.h
rename to include/pli_vis/third_party/spdlog/details/spdlog_impl.h
diff --git a/include/third_party/spdlog/fmt/bundled/format.cc b/include/pli_vis/third_party/spdlog/fmt/bundled/format.cc
similarity index 100%
rename from include/third_party/spdlog/fmt/bundled/format.cc
rename to include/pli_vis/third_party/spdlog/fmt/bundled/format.cc
diff --git a/include/third_party/spdlog/fmt/bundled/format.h b/include/pli_vis/third_party/spdlog/fmt/bundled/format.h
similarity index 100%
rename from include/third_party/spdlog/fmt/bundled/format.h
rename to include/pli_vis/third_party/spdlog/fmt/bundled/format.h
diff --git a/include/third_party/spdlog/fmt/bundled/ostream.cc b/include/pli_vis/third_party/spdlog/fmt/bundled/ostream.cc
similarity index 100%
rename from include/third_party/spdlog/fmt/bundled/ostream.cc
rename to include/pli_vis/third_party/spdlog/fmt/bundled/ostream.cc
diff --git a/include/third_party/spdlog/fmt/bundled/ostream.h b/include/pli_vis/third_party/spdlog/fmt/bundled/ostream.h
similarity index 100%
rename from include/third_party/spdlog/fmt/bundled/ostream.h
rename to include/pli_vis/third_party/spdlog/fmt/bundled/ostream.h
diff --git a/include/third_party/spdlog/fmt/bundled/printf.h b/include/pli_vis/third_party/spdlog/fmt/bundled/printf.h
similarity index 100%
rename from include/third_party/spdlog/fmt/bundled/printf.h
rename to include/pli_vis/third_party/spdlog/fmt/bundled/printf.h
diff --git a/include/third_party/spdlog/fmt/fmt.h b/include/pli_vis/third_party/spdlog/fmt/fmt.h
similarity index 100%
rename from include/third_party/spdlog/fmt/fmt.h
rename to include/pli_vis/third_party/spdlog/fmt/fmt.h
diff --git a/include/third_party/spdlog/fmt/ostr.h b/include/pli_vis/third_party/spdlog/fmt/ostr.h
similarity index 100%
rename from include/third_party/spdlog/fmt/ostr.h
rename to include/pli_vis/third_party/spdlog/fmt/ostr.h
diff --git a/include/third_party/spdlog/formatter.h b/include/pli_vis/third_party/spdlog/formatter.h
similarity index 100%
rename from include/third_party/spdlog/formatter.h
rename to include/pli_vis/third_party/spdlog/formatter.h
diff --git a/include/third_party/spdlog/logger.h b/include/pli_vis/third_party/spdlog/logger.h
similarity index 100%
rename from include/third_party/spdlog/logger.h
rename to include/pli_vis/third_party/spdlog/logger.h
diff --git a/include/third_party/spdlog/sinks/android_sink.h b/include/pli_vis/third_party/spdlog/sinks/android_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/android_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/android_sink.h
diff --git a/include/third_party/spdlog/sinks/ansicolor_sink.h b/include/pli_vis/third_party/spdlog/sinks/ansicolor_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/ansicolor_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/ansicolor_sink.h
diff --git a/include/third_party/spdlog/sinks/base_sink.h b/include/pli_vis/third_party/spdlog/sinks/base_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/base_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/base_sink.h
diff --git a/include/third_party/spdlog/sinks/dist_sink.h b/include/pli_vis/third_party/spdlog/sinks/dist_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/dist_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/dist_sink.h
diff --git a/include/third_party/spdlog/sinks/file_sinks.h b/include/pli_vis/third_party/spdlog/sinks/file_sinks.h
similarity index 100%
rename from include/third_party/spdlog/sinks/file_sinks.h
rename to include/pli_vis/third_party/spdlog/sinks/file_sinks.h
diff --git a/include/third_party/spdlog/sinks/msvc_sink.h b/include/pli_vis/third_party/spdlog/sinks/msvc_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/msvc_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/msvc_sink.h
diff --git a/include/third_party/spdlog/sinks/null_sink.h b/include/pli_vis/third_party/spdlog/sinks/null_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/null_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/null_sink.h
diff --git a/include/third_party/spdlog/sinks/ostream_sink.h b/include/pli_vis/third_party/spdlog/sinks/ostream_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/ostream_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/ostream_sink.h
diff --git a/include/third_party/spdlog/sinks/sink.h b/include/pli_vis/third_party/spdlog/sinks/sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/sink.h
rename to include/pli_vis/third_party/spdlog/sinks/sink.h
diff --git a/include/third_party/spdlog/sinks/stdout_sinks.h b/include/pli_vis/third_party/spdlog/sinks/stdout_sinks.h
similarity index 100%
rename from include/third_party/spdlog/sinks/stdout_sinks.h
rename to include/pli_vis/third_party/spdlog/sinks/stdout_sinks.h
diff --git a/include/third_party/spdlog/sinks/syslog_sink.h b/include/pli_vis/third_party/spdlog/sinks/syslog_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/syslog_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/syslog_sink.h
diff --git a/include/third_party/spdlog/sinks/wincolor_sink.h b/include/pli_vis/third_party/spdlog/sinks/wincolor_sink.h
similarity index 100%
rename from include/third_party/spdlog/sinks/wincolor_sink.h
rename to include/pli_vis/third_party/spdlog/sinks/wincolor_sink.h
diff --git a/include/third_party/spdlog/spdlog.h b/include/pli_vis/third_party/spdlog/spdlog.h
similarity index 100%
rename from include/third_party/spdlog/spdlog.h
rename to include/pli_vis/third_party/spdlog/spdlog.h
diff --git a/include/third_party/spdlog/tweakme.h b/include/pli_vis/third_party/spdlog/tweakme.h
similarity index 100%
rename from include/third_party/spdlog/tweakme.h
rename to include/pli_vis/third_party/spdlog/tweakme.h
diff --git a/include/pli_vis/third_party/tangent-base/analytic_orbit_interpolator.hpp b/include/pli_vis/third_party/tangent-base/analytic_orbit_interpolator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d0f42f39f641e02789501bf5d0ed2fe00876754
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/analytic_orbit_interpolator.hpp
@@ -0,0 +1,53 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENTBASE_ANALYTICORBITINTERPOL_H
+#define TANGENTBASE_ANALYTICORBITINTERPOL_H
+
+#include "base_operations.hpp"
+#include "base_types.hpp"
+#include "cartesian_grid.hpp"
+
+namespace tangent {
+
+class AnalyticOrbitInterpolator final {
+public:
+  AnalyticOrbitInterpolator() = default;
+  AnalyticOrbitInterpolator(const CartesianGrid *data) {}
+  ~AnalyticOrbitInterpolator() = default;
+
+  bool IsInside(const point_t &point) const { return true; }
+
+  vector_t Interpolate(const point_t &pt) const {
+    float_t oneDivLen = 1.0f / Length(pt);
+    vector_t normVec = {pt[0] * oneDivLen, pt[1] * oneDivLen,
+                        pt[2] * oneDivLen};
+    return vector_t({-normVec[1], normVec[0], 0.0f});
+  }
+
+private:
+};
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/base_operations.hpp b/include/pli_vis/third_party/tangent-base/base_operations.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c22ae2cdd4a0d96cc7d8fe654eab09ef44bc9d7
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/base_operations.hpp
@@ -0,0 +1,67 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENT_BASE_BASE_OPERATIONS_HPP
+#define TANGENT_BASE_BASE_OPERATIONS_HPP
+
+#include "base_types.hpp"
+#include <cmath>
+
+namespace tangent {
+
+inline vector_t Interpolate1D(const vector_t &v1, const vector_t &v2,
+                              const float_t &weight) {
+  return vector_t{{(1.0f - weight) * v1[0] + weight * v2[0],
+                   (1.0f - weight) * v1[1] + weight * v2[1],
+                   (1.0f - weight) * v1[2] + weight * v2[2]}};
+}
+
+inline vector_t AddWeightedVector(const vector_t &v, const float_t &f,
+                                  const vector_t &u) {
+  return vector_t{{v[0] + f * u[0], v[1] + f * u[1], v[2] + f * u[2]}};
+}
+
+inline point_t AddWeightedVectorToPoint(const point_t &p, const float_t &f,
+                                        const vector_t &v) {
+  return point_t{{p[0] + f * v[0], p[1] + f * v[1], p[2] + f * v[2], p[3]}};
+}
+
+inline vector_t ScaleVector(const vector_t &v, const float_t &f) {
+  return vector_t{{v[0] * f, v[1] * f, v[2] * f}};
+}
+
+template <std::size_t array_size, std::size_t num_components>
+inline float_t Length(const std::array<float_t, array_size> &a) {
+  float_t l = 0;
+  for (std::size_t i = 0; i < num_components; ++i)
+    l += a[i] * a[i];
+  return std::sqrt(l);
+}
+
+inline float_t Length(const point_t &p) { return Length<4, 3>(p); }
+
+inline float_t Length(const vector_t &v) { return Length<3, 3>(v); }
+}
+
+#endif // Include guard.
diff --git a/include/pli_vis/third_party/tangent-base/base_types.hpp b/include/pli_vis/third_party/tangent-base/base_types.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..24ca3ce96e390d05e6d6a0cb6606982ad79faf06
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/base_types.hpp
@@ -0,0 +1,61 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENTBASE_TYPES_H
+#define TANGENTBASE_TYPES_H
+
+#include <array>
+#include <cstdint>
+
+namespace tangent {
+using float_t = float;
+using point_id_t = std::size_t;
+using stage_id_t = std::uint32_t;
+
+using point_t = std::array<float_t, 4>;
+using vector_t = std::array<float_t, 3>;
+
+using weight_t = std::array<float_t, 3>;
+
+using grid_dim_t = std::array<std::size_t, 3>;
+using grid_coords_t = std::array<std::size_t, 3>;
+
+/**
+ * axis aligned bounding box in the format {min_x, max_x, min_y, max_y, min_z,
+ * max_z}
+ */
+using box_t = std::array<float_t, 6>;
+
+/**
+ * keep track of the eight point ids that designate a voxel's vertices
+ */
+using cell_pointids_t = std::array<std::size_t, 8>;
+
+/**
+ * keep track of the 8 vector values attached to a voxel's vertices
+ */
+using cell_vectors_t = std::array<vector_t, 8>;
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/basic_trilinear_interpolator.hpp b/include/pli_vis/third_party/tangent-base/basic_trilinear_interpolator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..43f1280327cd9f719f56bdbd0ea0f7747d9d5c85
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/basic_trilinear_interpolator.hpp
@@ -0,0 +1,104 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENTBASE_BASICTRILINEARINTERPOLATOR_H
+#define TANGENTBASE_BASICTRILINEARINTERPOLATOR_H
+
+#include "base_operations.hpp"
+#include "base_types.hpp"
+#include "cartesian_grid.hpp"
+#include "cartesian_locator.hpp"
+#include <cassert>
+
+namespace tangent {
+
+/**
+ * Implementation of a very basic trilinear interpolator which performs spatial
+ * interpolation and uses no optimizations whatsoever.
+ */
+class BasicTrilinearInterpolator final {
+public:
+  /**
+   * Create an interpolator based on the Cartesian grid passed in data
+   */
+  BasicTrilinearInterpolator(const CartesianGrid *data)
+      : data_{data}, locator_{data} {
+    assert(data_ != nullptr);
+  }
+  ~BasicTrilinearInterpolator() = default;
+
+  void SetData(const CartesianGrid *data) {
+    assert(data != nullptr);
+    locator_.SetData(data);
+    data_ = data;
+  }
+
+  /**
+   * Check whether the given position is inside the underlying data's bounding
+   * box
+   */
+  bool IsInside(const point_t &point) const {
+    return locator_.IsInsideGrid(point);
+  }
+
+  /**
+   * Perform trilinear interpolation by querying the underling Cartesian grid.
+   */
+  vector_t Interpolate(const point_t &pt) const {
+    assert(locator_.IsInsideGrid(pt));
+
+    auto cellPoints = locator_.GetCellPointIds(locator_.GetGridCoords(pt));
+    cell_vectors_t vectorVals = this->GetVectorValues(cellPoints);
+    weight_t weights = locator_.GetParametricCoords(pt);
+
+    vectorVals = this->FoldAlongAxis<4>(vectorVals, weights[0]);
+    vectorVals = this->FoldAlongAxis<2>(vectorVals, weights[1]);
+    return this->FoldAlongAxis<1>(vectorVals, weights[2])[0];
+  }
+
+protected:
+  cell_vectors_t GetVectorValues(const cell_pointids_t &cellPoints) const {
+    cell_vectors_t vectorVals;
+    for (std::size_t p = 0; p < 8; ++p)
+      vectorVals[p] = data_->GetVectorValue(cellPoints[p]);
+    return vectorVals;
+  }
+
+  template <std::size_t NUM_DIMS>
+  cell_vectors_t FoldAlongAxis(const std::array<vector_t, 8> &values,
+                               const float_t weight) const {
+    cell_vectors_t outVals;
+    for (std::size_t i = 0; i < NUM_DIMS; ++i)
+      outVals[i] =
+          tangent::Interpolate1D(values[2 * i], values[2 * i + 1], weight);
+    return outVals;
+  }
+
+private:
+  const CartesianGrid *data_;
+  CartesianLocator locator_;
+};
+};
+
+#endif
diff --git a/include/pli_vis/third_party/tangent-base/cartesian_grid.hpp b/include/pli_vis/third_party/tangent-base/cartesian_grid.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d02be531ceef938c83a87d43d05197d5737fa2d
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/cartesian_grid.hpp
@@ -0,0 +1,117 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENTBASE_CARTESIANGRID_H
+#define TANGENTBASE_CARTESIANGRID_H
+
+#include <array>
+#include <cassert>
+#include <vector>
+
+#include <tangent-base/base_types.hpp>
+
+namespace tangent {
+
+/**
+ * straightforward implementation of a vector field represented on a Cartesian
+ * grid. Note that this only implements the data handling itself, i.e. no
+ * higher-level functionality, e.g. interpolation, is defined here.
+ */
+class CartesianGrid final {
+public:
+  /**
+   * create a cartesian grid based on dims, cell spacing, and an (optional)
+   * origin which defaults to 0.
+   */
+  explicit CartesianGrid(const grid_dim_t &dims, const vector_t &spacing,
+                         const point_t &origin = {{0.0f, 0.0f, 0.0f, 0.0f}})
+      : dims_{dims}, spacing_{spacing}, origin_{origin} {
+    for (std::size_t d = 0; d < 3; ++d) {
+      box_[2 * d] = origin_[d];
+      box_[2 * d + 1] = origin_[d] + (dims_[d] - 1) * spacing_[d];
+    }
+    data_.resize(dims[0] * dims[1] * dims[2]);
+  }
+  /**
+   * create a cartesian grid based on dims and a bounding box. Spacing
+   * will automatically be computed according to these specs.
+   */
+  explicit CartesianGrid(const grid_dim_t &dims, const box_t box)
+      : dims_(dims), origin_({{box[0], box[2], box[4], 0.0f}}), box_(box) {
+    for (std::size_t d = 0; d < 3; ++d)
+      spacing_[d] = (box[2 * d + 1] - box[2 * d]) / (dims[d] - 1);
+    data_.resize(dims[0] * dims[1] * dims[2]);
+  }
+
+  ~CartesianGrid() = default;
+
+  /**
+   * flood fill the data with the given value
+   */
+  void Assign(const vector_t &value) {
+    data_.assign(dims_[0] * dims_[1] * dims_[2], value);
+  }
+
+  void Assign(std::vector<vector_t> &&input_vals) {
+    assert(input_vals.size() == dims_[0] * dims_[1] * dims_[2]);
+    data_ = input_vals;
+  }
+
+  std::size_t GetNumberOfPoints() const { return data_.size(); }
+
+  grid_dim_t GetDims() const { return dims_; }
+
+  point_t GetOrigin() const { return origin_; }
+
+  vector_t GetSpacing() const { return spacing_; }
+
+  box_t GetBounds() const { return box_; }
+
+  vector_t GetVectorValue(const point_id_t ptId) const {
+    assert(ptId < data_.size());
+    return data_[ptId];
+  }
+
+  /**
+   * Get pointer into the underlying data representation. It is safe to assume
+   * that vector data will be handled as contiguous chunk of interleaved data
+   * tuples of the form [u,v,w,u,v,w,...]
+   */
+  vector_t *GetVectorPointer(const point_id_t ptId) {
+    assert(ptId < data_.size());
+    return &(data_[ptId]);
+  }
+
+protected:
+private:
+  grid_dim_t dims_;
+  vector_t spacing_;
+  point_t origin_;
+  box_t box_;
+
+  std::vector<tangent::vector_t> data_;
+};
+};
+
+#endif
diff --git a/include/pli_vis/third_party/tangent-base/cartesian_locator.hpp b/include/pli_vis/third_party/tangent-base/cartesian_locator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0cf1cb707718f59689d39264c53f6174923941c6
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/cartesian_locator.hpp
@@ -0,0 +1,143 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your opointion) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+
+#include "cartesian_grid.hpp"
+
+#include <cassert>
+
+namespace tangent {
+class CartesianLocator {
+public:
+  CartesianLocator(const CartesianGrid *grid)
+      : gridBox_{grid->GetBounds()}, gridDims_{grid->GetDims()},
+        numGridPoints_{gridDims_[0] * gridDims_[1] * gridDims_[2]},
+        gridOrigin_{grid->GetOrigin()}, gridSpacing_{grid->GetSpacing()} {}
+  ~CartesianLocator() = default;
+
+  void SetData(const CartesianGrid *grid) {
+    gridBox_ = grid->GetBounds();
+    gridDims_ = grid->GetDims();
+    numGridPoints_ = gridDims_[0] * gridDims_[1] * gridDims_[2];
+    gridOrigin_ = grid->GetOrigin();
+    gridSpacing_ = grid->GetSpacing();
+  }
+
+  /**
+  * Check whether or not the given point (in world coords) is inside the box
+  * covered by the underlying grid. Note that the lower boundary is considered
+  * part of the grid whereas the upper boundary is not. Hence,
+  * isPointInsideGrid(origin)== true, but
+  * isPointInsideGrid({box[1], box[3], box[5]} == false.
+  */
+  bool IsInsideGrid(const point_t &point) const {
+    return (point[0] >= gridBox_[0] && point[0] < gridBox_[1]) &&
+           (point[1] >= gridBox_[2] && point[1] < gridBox_[3]) &&
+           (point[2] >= gridBox_[4] && point[2] < gridBox_[5]);
+  }
+
+  /**
+   * Get the grid coords of the base point (i.e. the lower, left, back vertex
+   * id) of the voxel containing pt iff pt is inside the grid. Note that the
+   * result is undefined if pt is not inside the grid's BB. Use isInsideGrid if
+   * in doubt.
+   */
+  grid_coords_t GetGridCoords(const point_t &point) const {
+    assert(this->IsInsideGrid(point));
+    return grid_coords_t({static_cast<point_id_t>(std::floor(
+                              (point[0] - gridOrigin_[0]) / gridSpacing_[0])),
+                          static_cast<point_id_t>(std::floor(
+                              (point[1] - gridOrigin_[1]) / gridSpacing_[1])),
+                          static_cast<point_id_t>(std::floor(
+                              (point[2] - gridOrigin_[2]) / gridSpacing_[2]))});
+  }
+
+  /**
+   * Get the grid coords for a given, valid point id of a grid vertex.
+   */
+  grid_coords_t GetGridCoords(const point_id_t ptId) const {
+    assert(ptId < numGridPoints_);
+    return tangent::grid_coords_t({(ptId % gridDims_[0]),
+                                   (ptId / gridDims_[0]) % gridDims_[1],
+                                   (ptId / gridDims_[0]) / gridDims_[1]});
+  }
+
+  /**
+   * Get the parametric coordinates of the given point inside the voxel
+   * which contains it. Note that the result is undefined if pt is not inside
+   * the grid's BB. Use isInside if in doubt.
+   */
+  weight_t GetParametricCoords(const point_t &point) const {
+    point_t basePt = this->GetPointCoords(this->GetGridCoords(point));
+    return weight_t({(point[0] - basePt[0]) / gridSpacing_[0],
+                     (point[1] - basePt[1]) / gridSpacing_[1],
+                     (point[2] - basePt[2]) / gridSpacing_[2]});
+  }
+
+  /**
+   * Return the 3D position of the grid point designated by the given grid
+   * coords. Note that this will return invalid points if gridCoords is outside
+   * the grid's dims in any dimension.
+   */
+  point_t GetPointCoords(const grid_coords_t &gridCoords) const {
+    assert(gridCoords[0] < gridDims_[0] && gridCoords[1] < gridDims_[1] &&
+           gridCoords[2] < gridDims_[2]);
+    return point_t({gridOrigin_[0] + gridCoords[0] * gridSpacing_[0],
+                    gridOrigin_[1] + gridCoords[1] * gridSpacing_[1],
+                    gridOrigin_[2] + gridCoords[2] * gridSpacing_[2]});
+  }
+
+  /**
+   * Convert grid coords to point id
+   */
+  point_id_t GetPointId(const grid_coords_t &gridCoords) const {
+    assert(gridCoords[0] < gridDims_[0] && gridCoords[1] < gridDims_[1] &&
+           gridCoords[2] < gridDims_[2]);
+    return gridCoords[0] +
+           gridDims_[0] * (gridCoords[1] + gridDims_[1] * gridCoords[2]);
+  }
+
+  /**
+   * Given grid coords of a valid grid point that has a voxel to its right,
+   * upper, front, retrieve the grid point ids which define that voxel Note that
+   * the behavior is undefined if pt lies outside the grid's bounding box.
+   */
+  cell_pointids_t GetCellPointIds(const grid_coords_t &gridCoords) const {
+    point_id_t ptId = this->GetPointId(gridCoords);
+    return cell_pointids_t({ptId, ptId + 1, ptId + gridDims_[0],
+                            ptId + gridDims_[0] + 1,
+                            ptId + gridDims_[0] * gridDims_[1],
+                            ptId + gridDims_[0] * gridDims_[1] + 1,
+                            ptId + (gridDims_[0] + 1) * gridDims_[1],
+                            ptId + (gridDims_[0] + 1) * gridDims_[1] + 1});
+  }
+
+private:
+  box_t gridBox_;
+  grid_dim_t gridDims_;
+  unsigned long long numGridPoints_;
+  point_t gridOrigin_;
+  vector_t gridSpacing_;
+};
+};
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/default_tracers.hpp b/include/pli_vis/third_party/tangent-base/default_tracers.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0205cf5fa44bf552e78b95ec310fdcba64d6533a
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/default_tracers.hpp
@@ -0,0 +1,61 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef DEFAULT_TRACERS_H
+#define DEFAULT_TRACERS_H
+
+#include "cartesian_grid.hpp"
+#include "basic_trilinear_interpolator.hpp"
+#include "runge_kutta_4_integrator.hpp"
+#include "trace_recorder.hpp"
+#include "dummy_recorder.hpp"
+#include "simple_tracer.hpp"
+#include "omp_pos_tracer.hpp"
+
+namespace tangent {
+  struct TrilinearInterpolationTrait {
+    using Data = CartesianGrid;
+    using Interpolator = BasicTrilinearInterpolator;
+  };
+
+  struct CartesianPointAdvectionTrait {
+    using Data = CartesianGrid;
+    using Integrator = RungeKutta4Integrator<TrilinearInterpolationTrait>;
+    using Recorder = DummyRecorder;
+  };
+
+  struct CartesianStreamlineTrait {
+    using Data = CartesianGrid;
+    using Integrator = RungeKutta4Integrator<TrilinearInterpolationTrait>;
+    using Recorder = TraceRecorder;
+  };
+
+  using SimpleCartGridPointMappingTracer = SimpleTracer<CartesianPointAdvectionTrait>;
+  using SimpleCartGridStreamlineTracer = SimpleTracer<CartesianStreamlineTrait>;
+
+  using OmpCartGridPointMappingTracer = OmpPOSTracer<CartesianPointAdvectionTrait>;
+  using OmpCartGridStreamlineTracer = OmpPOSTracer<CartesianStreamlineTrait>;
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/dummy_recorder.hpp b/include/pli_vis/third_party/tangent-base/dummy_recorder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..482ac40868f2413aa206b047011db843dfa3107f
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/dummy_recorder.hpp
@@ -0,0 +1,53 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENT_BASE_DUMMY_RECORDER_HPP
+#define TANGENT_BASE_DUMMY_RECORDER_HPP
+
+#include "base_types.hpp"
+
+namespace tangent {
+/**
+ * This recorder can be used as a blank stand in for a real recorder in case
+ * that recording particle positions is not required for the task at hand. Not
+ * recording anything "on the go" might significantly increase tracing
+ * performance, because there will essentially be no write-backs to main memory.
+ */
+class DummyRecorder final {
+public:
+  DummyRecorder() = default;
+  ~DummyRecorder() = default;
+
+  void InitRecording(const std::size_t num_traces,
+                     const std::size_t expected_trace_length){};
+
+  void InitTrace(const std::size_t trace_id, const point_t &seed) { return; }
+
+  void AppendTracePoint(const std::size_t trace_id, const point_t &point) {
+    return;
+  }
+};
+}
+
+#endif // Include guard.
diff --git a/include/pli_vis/third_party/tangent-base/measurements/counter_measurement.hpp b/include/pli_vis/third_party/tangent-base/measurements/counter_measurement.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ac820f727fd4c3c2f6099bb8852b23d4fd2989f
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/measurements/counter_measurement.hpp
@@ -0,0 +1,63 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef COUNTER_MEASUREMENT_H
+#define COUNTER_MEASUREMENT_H
+
+#include "measurement_base.hpp"
+#include <cstdint>
+#include <string>
+
+namespace tangent {
+/**
+ * This is a simple measurement class to count occurrences of events and
+ * integrate them into a measurement output.
+ * BEWARE: The count method is NOT THREAD SAFE! So make sure you use a
+ * dedicated counter for each thread if running in a threaded environment!
+ */
+class CounterMeasurement final : public MeasurementBase {
+public:
+  CounterMeasurement(const std::string &title)
+      : MeasurementBase{title}, counter_{0} {}
+  ~CounterMeasurement() = default;
+
+  void Count() { ++counter_; }
+
+  std::uint64_t GetCount() const { return counter_; }
+  void SetCount(const std::uint64_t count) { counter_ = count; }
+
+  std::ostream &WriteData(std::ostream &out_stream) const override {
+    out_stream << "counter : ";
+    out_stream << this->GetLabel() << " : ";
+    out_stream << counter_ << " : ";
+    out_stream << "count" << std::endl;
+    return out_stream;
+  }
+
+private:
+  std::uint64_t counter_;
+};
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/measurements/cummulative_time_measurement.hpp b/include/pli_vis/third_party/tangent-base/measurements/cummulative_time_measurement.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..36a3c8dab578603b8e3de3017c8a040f10275ad2
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/measurements/cummulative_time_measurement.hpp
@@ -0,0 +1,87 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef AVERAGE_DURATION_MEASUREMENT_H
+#define AVERAGE_DURATION_MEASUREMENT_H
+
+#include "counter_measurement.hpp"
+#include "time_referenced_measurement.hpp"
+#include <chrono>
+#include <cstdint>
+#include <typeinfo>
+
+namespace tangent {
+template <typename TimeUnit = std::chrono::milliseconds>
+/**
+ * This measurement takes to cumulative time of some event, i.e. the sum of
+ * times over multiple episodes.
+ * BEWARE: This measurement is NOT THREAD SAFE! So make sure you use a
+ * dedicated instance for each thread if running in a threaded environment!
+ */
+class CummulativeTimeMeasurement final
+    : public TimeReferencedMeasurement<TimeUnit> {
+public:
+  CummulativeTimeMeasurement(const std::string &title)
+      : TimeReferencedMeasurement<TimeUnit>{title}, counter_{""},
+        total_time_taken_{0} {}
+  ~CummulativeTimeMeasurement() = default;
+
+  void StartEpisode() { start_ = std::chrono::high_resolution_clock::now(); }
+
+  void StopEpisode() {
+    total_time_taken_ +=
+        (std::chrono::high_resolution_clock::now() - start_).count();
+    counter_.Count();
+  }
+
+  void Reset() { total_time_taken_ = 0; }
+
+  std::uint64_t GetCummulativeTime() const { return total_time_taken_; }
+  std::uint64_t GetNumberOfEpisodes() const { return counter_.GetCount(); }
+  float GetAverageEpisodeDuration() const {
+    return static_cast<float>(total_time_taken_) /
+           static_cast<float>(counter_.GetCount());
+  }
+
+  std::ostream &WriteData(std::ostream &out_stream) const override {
+    out_stream << "cummulative_time : ";
+    out_stream << this->GetLabel() << " : ";
+    out_stream << total_time_taken_ << " : ";
+    out_stream << this->GetTimeUnitString() << std::endl;
+    out_stream << "cummulative_count : ";
+    out_stream << this->GetLabel() << " : ";
+    out_stream << counter_.GetCount() << " : ";
+    out_stream << "count" << std::endl;
+    return out_stream;
+  }
+
+protected:
+private:
+  std::chrono::time_point<std::chrono::high_resolution_clock> start_;
+  std::uint64_t total_time_taken_;
+  CounterMeasurement counter_;
+};
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/measurements/frequency_measurement.hpp b/include/pli_vis/third_party/tangent-base/measurements/frequency_measurement.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dd7cbc175b2a6f9e9e16aa24f2caece14bf9182f
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/measurements/frequency_measurement.hpp
@@ -0,0 +1,73 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef FREQUENCY_MEASUREMENT_H
+#define FREQUENCY_MEASUREMENT_H
+
+#include "counter_measurement.hpp"
+#include "runtime_measurement.hpp"
+#include "time_referenced_measurement.hpp"
+
+#include <string>
+
+namespace tangent {
+/**
+ * This class allows clients to track frequencies, i.e. number of events per
+ * unit time.
+ * BEWARE: This measurement is NOT THREAD SAFE! So make sure you use a
+ * dedicated instance for each thread if running in a threaded environment!
+ */
+template <typename TimeUnit = std::chrono::milliseconds>
+class FrequencyMeasurement final : public TimeReferencedMeasurement<TimeUnit> {
+public:
+  FrequencyMeasurement(const std::string title,
+                       const RuntimeMeasurement<TimeUnit> &time,
+                       const CounterMeasurement &count)
+      : TimeReferencedMeasurement<TimeUnit>{title}, time_{time}, count_{count} {
+  }
+  ~FrequencyMeasurement() = default;
+
+  float GetFrequency() const {
+    return static_cast<float>(count_.GetCount()) / time_.GetTimeTaken();
+  }
+
+  std::ostream &WriteData(std::ostream &out_stream) const override {
+    out_stream << "frequency_time : ";
+    out_stream << this->GetLabel() << " : ";
+    out_stream << time_.GetTimeTaken() << " : ";
+    out_stream << this->GetTimeUnitString() << std::endl;
+    out_stream << "frequency_count : ";
+    out_stream << this->GetLabel() << " : ";
+    out_stream << count_.GetCount() << " : ";
+    out_stream << "count" << std::endl;
+    return out_stream;
+  }
+
+private:
+  const RuntimeMeasurement<TimeUnit> &time_;
+  const CounterMeasurement &count_;
+};
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/measurements/measurement_base.hpp b/include/pli_vis/third_party/tangent-base/measurements/measurement_base.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..591d072ae432a47f0909e9c50249c6009d6164ca
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/measurements/measurement_base.hpp
@@ -0,0 +1,49 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef MEASUREMENT_BASE_H
+#define MEASUREMENT_BASE_H
+
+#include <chrono>
+#include <iostream>
+#include <string>
+
+namespace tangent {
+
+class MeasurementBase {
+public:
+  MeasurementBase(const std::string &label) : label_{label} {}
+  virtual ~MeasurementBase() = default;
+
+  const std::string GetLabel() const { return label_; }
+
+  virtual std::ostream &WriteData(std::ostream &out_stream) const = 0;
+
+protected:
+private:
+  std::string label_;
+};
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/measurements/performance_experiment.hpp b/include/pli_vis/third_party/tangent-base/measurements/performance_experiment.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..56f1ab750c785a84725fc274c9bcd4ead60990b8
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/measurements/performance_experiment.hpp
@@ -0,0 +1,157 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENT_BASE_RUNTIME_EXPERIMENT_HPP
+#define TANGENT_BASE_RUNTIME_EXPERIMENT_HPP
+
+#include "measurement_base.hpp"
+#include "runtime_measurement.hpp"
+
+#include <chrono>
+#include <cstdint>
+#include <functional>
+#include <iostream>
+#include <string>
+
+namespace tangent {
+/**
+ * Configure your custom performance experiment by providing callbacks to the
+ * three essential phases setup, run, and teardown. In order to account for a
+ * wide variety of measurement scenarios, the time units in which the
+ * measurement will be taken, can be configured via a template parameter.
+ *
+ * The runtime of all three phases will be measured per default. Additional
+ * measurements can be added as needed. However, it is the client's
+ * responsibility to properly inject these measurements into the overall setup,
+ * i.e. see to them measurements being called from within the measured code. In
+ * order to count the number of integrations, for example, a point recorder can
+ * be used (see IntegrationCountingRecorder).
+ *
+ * Along with executing the three phases outline above, this class provides
+ * structured output for the overall experiment.
+ */
+template <typename TimeUnit = std::chrono::milliseconds>
+class PerformanceExperiment {
+public:
+  using RuntimeMeasurementType = RuntimeMeasurement<TimeUnit>;
+
+  PerformanceExperiment(const std::string &title,
+                        const std::function<void()> &setup,
+                        const std::function<void()> &run,
+                        const std::function<void()> &teardown)
+      : title_{title}, setup_callback_{setup}, run_callback_{run},
+        teardown_callback_{teardown},
+        setup_timer_{std::make_unique<RuntimeMeasurementType>("setup_time")},
+        run_timer_{std::make_unique<RuntimeMeasurementType>("run_time")},
+        teardown_timer_{
+            std::make_unique<RuntimeMeasurementType>("teardown_time")} {
+    measurements_.reserve(10);
+  }
+  ~PerformanceExperiment() = default;
+
+  void AddMeasurement(MeasurementBase *measurement) {
+    measurements_.push_back(measurement);
+  }
+
+  void Run() {
+    this->ExecuteStep(setup_callback_, setup_timer_.get());
+    this->ExecuteStep(run_callback_, run_timer_.get());
+    this->ExecuteStep(teardown_callback_, teardown_timer_.get());
+  }
+
+  std::uint64_t GetSetupTime() const { return setup_timer_->GetTimeTaken(); }
+
+  std::uint64_t GetRuntime() const { return run_timer_->GetTimeTaken(); }
+
+  std::uint64_t GetTeardownTime() const {
+    return teardown_timer_->GetTimeTaken();
+  }
+
+  template <typename MeasurementType>
+  MeasurementType *GetMeasurementByTitle(const std::string &title) const {
+    for (auto m : measurements_) {
+      if (m->GetLabel() == title)
+        return dynamic_cast<MeasurementType *>(m);
+    }
+    return nullptr;
+  }
+
+  std::vector<MeasurementBase *> GetMeasurements() const {
+    std::vector<MeasurementBase *> result;
+    return measurements_;
+  }
+
+  std::ostream &WriteData(std::ostream &out_stream) const {
+    this->WriteHeader(out_stream);
+    this->WriteDefaultTimes(out_stream);
+    this->WriteMeasurements(out_stream);
+    return out_stream;
+  }
+
+protected:
+  void ExecuteStep(const std::function<void()> &step,
+                   RuntimeMeasurementType *timer) {
+    timer->Start();
+    step();
+    timer->Stop();
+  }
+
+  void WriteHeader(std::ostream &out_stream) const {
+    out_stream << "# tangent experiment data" << std::endl;
+    out_stream << "# EXPRIMENT: " << title_ << std::endl;
+    out_stream << "# Format: " << std::endl;
+    out_stream << "# measurement type : measurement label : result : unit"
+               << std::endl;
+    out_stream << std::endl;
+  }
+
+  void WriteDefaultTimes(std::ostream &out_stream) const {
+    setup_timer_->WriteData(out_stream);
+    run_timer_->WriteData(out_stream);
+    teardown_timer_->WriteData(out_stream);
+  }
+
+  void WriteMeasurements(std::ostream &out_stream) const {
+    for (const auto &m : measurements_)
+      m->WriteData(out_stream);
+    out_stream << std::endl;
+  }
+
+private:
+  const std::string title_;
+
+  std::function<void()> setup_callback_;
+  std::function<void()> run_callback_;
+  std::function<void()> teardown_callback_;
+
+  using RuntimeMeasurePtr = std::unique_ptr<RuntimeMeasurementType>;
+  RuntimeMeasurePtr setup_timer_;
+  RuntimeMeasurePtr run_timer_;
+  RuntimeMeasurePtr teardown_timer_;
+
+  std::vector<MeasurementBase *> measurements_;
+};
+}
+
+#endif // Include guard.
diff --git a/include/pli_vis/third_party/tangent-base/measurements/runtime_measurement.hpp b/include/pli_vis/third_party/tangent-base/measurements/runtime_measurement.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b567bf55d13251c721ff761ba857e4db5348d7e
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/measurements/runtime_measurement.hpp
@@ -0,0 +1,70 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef RUNTIME_MEASUREMENT_H
+#define RUNTIME_MEASUREMENT_H
+
+#include "time_referenced_measurement.hpp"
+#include <chrono>
+#include <cstdint>
+#include <string>
+
+namespace tangent {
+/**
+ * This class defines simple stop watch semantics, i.e. it measures the time
+ * between start and stop.
+ * BEWARE: This measurement is NOT THREAD SAFE! So make sure you use a
+ * dedicated instance for each thread if running in a threaded environment!
+ */
+template <typename TimeUnit = std::chrono::milliseconds>
+class RuntimeMeasurement final : public TimeReferencedMeasurement<TimeUnit> {
+public:
+  RuntimeMeasurement(const std::string &title)
+      : TimeReferencedMeasurement<TimeUnit>{title} {}
+  ~RuntimeMeasurement() = default;
+
+  void Start() { start_ = std::chrono::high_resolution_clock::now(); }
+
+  void Stop() { end_ = std::chrono::high_resolution_clock::now(); }
+
+  std::uint64_t GetTimeTaken() const {
+    return std::chrono::duration_cast<TimeUnit>(end_ - start_).count();
+  }
+
+  std::ostream &WriteData(std::ostream &out_stream) const override {
+    out_stream << "runtime : ";
+    out_stream << this->GetLabel() << " : ";
+    out_stream << this->GetTimeTaken() << " : ";
+    out_stream << this->GetTimeUnitString() << std::endl;
+    return out_stream;
+  }
+
+protected:
+private:
+  std::chrono::time_point<std::chrono::high_resolution_clock> start_;
+  std::chrono::time_point<std::chrono::high_resolution_clock> end_;
+};
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/measurements/time_referenced_measurement.hpp b/include/pli_vis/third_party/tangent-base/measurements/time_referenced_measurement.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..330619828a9f850cd82b96cb6a5b80e81ae3e83a
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/measurements/time_referenced_measurement.hpp
@@ -0,0 +1,69 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TIME_REFERENCED_MEASUREMENT_H
+#define TIME_REFERENCED_MEASUREMENT_H
+
+#include "time_referenced_measurement.hpp"
+
+#include <chrono>
+#include <string>
+#include <typeinfo>
+
+namespace tangent {
+/**
+ * Base class for all measurements using time units. Defines a common output
+ * format for unit strings.
+ */
+template <typename TimeUnit>
+class TimeReferencedMeasurement : public MeasurementBase {
+public:
+  TimeReferencedMeasurement(const std::string &label)
+      : MeasurementBase{label} {}
+  virtual ~TimeReferencedMeasurement() = default;
+
+protected:
+  std::string GetTimeUnitString() const {
+    if (typeid(TimeUnit) == typeid(std::chrono::nanoseconds))
+      return "ns";
+    if (typeid(TimeUnit) == typeid(std::chrono::microseconds))
+      return "mus";
+    if (typeid(TimeUnit) == typeid(std::chrono::milliseconds))
+      return "ms";
+    if (typeid(TimeUnit) == typeid(std::chrono::seconds))
+      return "s";
+    if (typeid(TimeUnit) == typeid(std::chrono::minutes))
+      return "m";
+    if (typeid(TimeUnit) == typeid(std::chrono::hours))
+      return "ns";
+    if (typeid(TimeUnit) == typeid(std::chrono::nanoseconds))
+      return "ns";
+    return "undefined";
+  }
+
+private:
+};
+};
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/omp_pos_tracer.hpp b/include/pli_vis/third_party/tangent-base/omp_pos_tracer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7386778936a27d328b63482f1df0d716109ee5e0
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/omp_pos_tracer.hpp
@@ -0,0 +1,71 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef OMP_POS_TRACER_H
+#define OMP_POS_TRACER_H
+
+#include "base_types.hpp"
+#include "tracer_base.hpp"
+
+#include <vector>
+
+namespace tangent {
+/**
+ * OmpPOSTracer uses OpenMP for a parallel particle integration based on the
+ * "parallelize-over-seed" (POS) strategy.
+ */
+template <typename TracerTrait>
+class OmpPOSTracer final : public TracerBase<TracerTrait> {
+public:
+  using Data = typename TracerTrait::Data;
+  using Integrator = typename TracerTrait::Integrator;
+  using IntegrationStep = typename Integrator::IntegrationStep;
+  using Recorder = typename TracerTrait::Recorder;
+
+  OmpPOSTracer(Recorder *recorder) : TracerBase<TracerTrait>(recorder){};
+  ~OmpPOSTracer() = default;
+
+  std::vector<point_t> TraceSeeds(const std::vector<point_t> &seeds) {
+    const std::int64_t num_seeds = static_cast<std::int64_t>(seeds.size());
+    auto output_points = this->InitOutput(num_seeds);
+
+#pragma omp parallel shared(output_points)
+    {
+      Integrator integrator(this->GetData());
+#pragma omp for schedule(guided)
+      for (std::int64_t seed_id = 0; seed_id < num_seeds; ++seed_id) {
+        auto result = this->AdvectParticle(
+            seed_id, integrator, this->InitStep(seed_id, seeds[seed_id]));
+        output_points[seed_id] = result.end_point;
+      }
+    }
+    return output_points;
+  }
+
+protected:
+private:
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/particle_population.hpp b/include/pli_vis/third_party/tangent-base/particle_population.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..90ec15b89f462cefbb5d4289c60af0532d017706
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/particle_population.hpp
@@ -0,0 +1,59 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENT_BASE_PARTICLE_POPULATION_HPP
+#define TANGENT_BASE_PARTICLE_POPULATION_HPP
+
+#include "base_types.hpp"
+#include <vector>
+
+namespace tangent {
+/**
+ * Straightforward data structure to keep track of particle traces. Each trace
+ * is represented as a std::vector of points.
+ */
+class ParticlePopulation final {
+public:
+  using Trace = std::vector<point_t>;
+
+  ParticlePopulation() = default;
+  ParticlePopulation(const std::size_t num_particles,
+                     const std::size_t expected_size = 100) {
+    traces_.resize(num_particles);
+    for (auto &trace : traces_)
+      trace.reserve(expected_size);
+  }
+  ~ParticlePopulation() = default;
+
+  std::size_t GetNumberOfTraces() const { return traces_.size(); }
+
+  Trace &operator[](const std::size_t trace_id) { return traces_[trace_id]; }
+
+protected:
+private:
+  std::vector<Trace> traces_;
+};
+}
+
+#endif // Include guard.
diff --git a/include/pli_vis/third_party/tangent-base/raw_binary_reader.hpp b/include/pli_vis/third_party/tangent-base/raw_binary_reader.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fd05befb0ff4f3411499875f1bdb4d6d44686d86
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/raw_binary_reader.hpp
@@ -0,0 +1,86 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef RAW_BINARY_READER_H
+#define RAW_BINARY_READER_H
+
+#include "base_types.hpp"
+#include "cartesian_grid.hpp"
+
+#include <cassert>
+#include <cstdint>
+#include <fstream>
+#include <memory>
+#include <vector>
+
+namespace tangent {
+class RawBinaryReader final {
+public:
+  RawBinaryReader() = default;
+  ~RawBinaryReader() = default;
+
+  std::unique_ptr<CartesianGrid>
+  ReadFile(const std::string &file_name,
+           const vector_t &spacing = {1.0f, 1.0f, 1.0f},
+           const point_t &origin = {0.0f, 0.0f, 0.0f, 0.0f}) const {
+    std::ifstream input_file{file_name.c_str(),
+                             std::ifstream::in | std::ifstream::binary};
+    if (!input_file.is_open())
+      return nullptr;
+    grid_dim_t dims{this->ReadGridDims(input_file)};
+    if (input_file.fail())
+      return nullptr;
+    auto out_data = std::make_unique<CartesianGrid>(dims, spacing, origin);
+    if (!this->ReadVectorData(input_file, out_data.get()))
+      return nullptr;
+    return out_data;
+  }
+
+private:
+  tangent::grid_dim_t ReadGridDims(std::ifstream &file) const {
+    std::uint32_t input_dims[3];
+    file.read(reinterpret_cast<char *>(input_dims), 3 * sizeof(std::uint32_t));
+    return grid_dim_t{input_dims[0], input_dims[1], input_dims[2]};
+  }
+
+  bool ReadVectorData(std::ifstream &input_file, CartesianGrid *data) const {
+    auto dims = data->GetDims();
+    std::size_t num_vectors = dims[0] * dims[1] * dims[2];
+    std::size_t num_bytes = num_vectors * 3 * sizeof(float);
+    // NOTE: This only works if the 32-bit data in the file matches the 32-bit
+    // runtime representation of tangent!
+    assert(sizeof(float) == sizeof(tangent::float_t));
+    std::vector<tangent::vector_t> input_data(num_vectors);
+    input_file.read(reinterpret_cast<char *>(&input_data[0]), num_bytes);
+    if (ReadFailed(input_file) || !ReadFileToTheEnd(input_file))
+      return false;
+    data->Assign(std::move(input_data));
+    return true;
+  }
+
+  bool ReadFailed(std::ifstream &f) const { return f.fail(); }
+  bool ReadFileToTheEnd(std::ifstream &f) const { return f.peek() == EOF; }
+};
+}
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/third_party/tangent-base/runge_kutta_4_integrator.hpp b/include/pli_vis/third_party/tangent-base/runge_kutta_4_integrator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f2c396e00dae5cea291c60dbefc61ed4d8d474b
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/runge_kutta_4_integrator.hpp
@@ -0,0 +1,166 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENTBASE_RUNGE_KUTTA_4_INTEGRATOR_HPP
+#define TANGENTBASE_RUNGE_KUTTA_4_INTEGRATOR_HPP
+
+#include "base_types.hpp"
+#include "base_operations.hpp"
+
+namespace tangent {
+
+/**
+ * Vanilla Runge Kutta 4 scheme w/o step size adaption (for now).
+ */
+template <typename InterpolatorTrait> class RungeKutta4Integrator final {
+public:
+  using Interpolator = typename InterpolatorTrait::Interpolator;
+  using Data = typename InterpolatorTrait::Data;
+
+  enum StepState {
+    STEP_STAGE_0 = 0,
+    STEP_STAGE_1 = 1,
+    STEP_STAGE_2 = 2,
+    STEP_STAGE_3 = 3,
+    STEP_FINISHED = 4
+  };
+
+  /**
+   * Helper class to facilitate pre-empted integration across multiple data
+   * sets. All necessary information from a step is stored in an instance of
+   * IntegrationStep. Hence, should the step fail at any stage during the
+   * integration - i.e. in case the  current sample pos lies outside the current
+   * data - it can be resumed later on, specifically after having provided the
+   * correct data set. This ensures that an integration across multiple blocks
+   * performs exactly the same computations as an integration within a single
+   * block.
+   */
+  struct IntegrationStep final {
+    IntegrationStep(const tangent::point_t &start_pt,
+                    const tangent::float_t step)
+        : start_point{start_pt}, end_point{start_pt}, current_k{{0.0f, 0.0f,
+                                                                 0.0f}},
+          k_sum{{0.0f, 0.0f, 0.0f}}, time_step{step}, next_stage{STEP_STAGE_0} {
+    }
+    ~IntegrationStep() = default;
+
+    void RestartAt(point_t &start) {
+      start_point = start;
+      k_sum = {0.0f, 0.0f, 0.0f};
+      next_stage = STEP_STAGE_0;
+    }
+
+    bool HasFinished() const { return next_stage == STEP_FINISHED; }
+
+    tangent::point_t start_point;
+    tangent::point_t end_point;
+    tangent::vector_t current_k;
+    tangent::vector_t k_sum;
+    tangent::float_t time_step;
+    stage_id_t next_stage;
+  };
+
+  RungeKutta4Integrator(const Data *data)
+      : interpolator_{data}, stage_factors_({0.0f, 0.5f, 0.5f, 1.0f}),
+        result_factors_({1.0f, 2.0f, 2.0f, 1.0f}) {}
+  ~RungeKutta4Integrator() = default;
+
+  void SetData(const Data *data) { interpolator_.SetData(data); }
+
+  /**
+   * Perform a single  RK4 step. In case this is successful, step_data will
+   * contain a valid end_point and its next_stage attribute will be set to
+   * STEP_FINISHED. In case something goes wrong, intermediate values will be
+   * stored in step_data, in order to be able to resume integration.
+   * Specifically, step_data.next_stage will indicate the next integration stage
+   * that has to be performed by the integrator.
+   *
+   * If passed a previously non-finished step, integration will automatically
+   * try to resume at the next integration stage. Please note that in this will
+   * perform additive computations, which critically rely in the fact that the
+   * input step_data did in fact result from a previous, prematurely terminated
+   * integration step.
+   *
+   * For use in iterative integration, it is the client's responsibility to
+   * correctly init step_data after each iteration. The STEP_FINISHED state is
+   * specifically included in order to separate a failed first stage from a
+   * fully completed step.
+   */
+  void ComputeStep(IntegrationStep &step_data) const {
+    point_t current_point;
+    vector_t current_vector;
+    for (stage_id_t stage = step_data.next_stage; stage < STEP_FINISHED;
+         ++stage) {
+      current_point = this->GetStateSamplePosition(stage, step_data);
+      if (!this->Interpolate(current_point, current_vector))
+        return;
+      this->UpdateStepData(stage, current_vector, step_data);
+    }
+    this->FinalizeStep(step_data);
+  }
+
+protected:
+  point_t GetStateSamplePosition(stage_id_t stage,
+                                 IntegrationStep &step_data) const {
+    point_t sample_position = tangent::AddWeightedVectorToPoint(
+        step_data.start_point, stage_factors_[stage], step_data.current_k);
+    sample_position[3] += stage_factors_[stage] * step_data.time_step;
+    return sample_position;
+  }
+
+  bool Interpolate(const point_t &sample_point, vector_t &vector) const {
+    if (!interpolator_.IsInside(sample_point))
+      return false;
+    vector = interpolator_.Interpolate(sample_point);
+    return true;
+  }
+
+  void UpdateStepData(stage_id_t stage, const vector_t &current_vector,
+                      IntegrationStep &step_data) const {
+    step_data.current_k =
+        tangent::ScaleVector(current_vector, step_data.time_step);
+    /*
+    Note: first sum up the k values only. Do not incrementally sum up the end
+    position. The latter options, while more compact to write, is numerically
+    less stable.
+    */
+    step_data.k_sum = tangent::AddWeightedVector(
+        step_data.k_sum, result_factors_[stage], step_data.current_k);
+    step_data.next_stage = stage + 1;
+  }
+
+  void FinalizeStep(IntegrationStep &step_data) const {
+    step_data.end_point = tangent::AddWeightedVectorToPoint(
+        step_data.start_point, (1.0f / 6.0f), step_data.k_sum);
+    step_data.end_point[3] = step_data.start_point[3] + step_data.time_step;
+  }
+
+private:
+  Interpolator interpolator_;
+  std::array<float_t, 4> stage_factors_;
+  std::array<float_t, 4> result_factors_;
+};
+}
+
+#endif // Include guard.
diff --git a/include/pli_vis/third_party/tangent-base/simple_tracer.hpp b/include/pli_vis/third_party/tangent-base/simple_tracer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e078f8d6a2fe22ecd537633ed1ffc31b3262ce23
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/simple_tracer.hpp
@@ -0,0 +1,60 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENT_BASE_SIMPLE_TRACER_HPP
+#define TANGENT_BASE_SIMPLE_TRACER_HPP
+
+#include "tracer_base.hpp"
+#include "base_types.hpp"
+#include <vector>
+
+namespace tangent {
+template <typename TracerTrait>
+class SimpleTracer final : public TracerBase<TracerTrait> {
+public:
+  using Data = typename TracerTrait::Data;
+  using Integrator = typename TracerTrait::Integrator;
+  using IntegrationStep = typename Integrator::IntegrationStep;
+  using Recorder = typename TracerTrait::Recorder;
+
+  SimpleTracer(Recorder *recorder) : TracerBase<TracerTrait>(recorder) {}
+  ~SimpleTracer() override = default;
+
+  std::vector<point_t> TraceSeeds(const std::vector<point_t> &seeds) {
+    const auto num_seeds = seeds.size();
+    auto output_points = this->InitOutput(num_seeds);
+    Integrator integrator(this->GetData());
+    for (std::size_t seed_id = 0; seed_id < num_seeds; ++seed_id) {
+      auto result = this->AdvectParticle(
+          seed_id, integrator, this->InitStep(seed_id, seeds[seed_id]));
+      output_points[seed_id] = result.end_point;
+    }
+    return output_points;
+  }
+
+protected:
+};
+}
+
+#endif // Include guard.
diff --git a/include/pli_vis/third_party/tangent-base/trace_recorder.hpp b/include/pli_vis/third_party/tangent-base/trace_recorder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e7079b83a486ded872c2bf2df80dcb29ba8b756
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/trace_recorder.hpp
@@ -0,0 +1,68 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright (c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TANGENT_BASE_TRACE_RECORDER_HPP
+#define TANGENT_BASE_TRACE_RECORDER_HPP
+
+#include "base_types.hpp"
+#include "particle_population.hpp"
+#include <cassert>
+
+namespace tangent {
+/**
+ * A TraceRecorder is used to record trace points in a straightforward vector of
+ * vectors layout. The underlying data type is a tangent::ParticlePopulation.
+ * Each point that is issued to the recorder will just be appended to the
+ * respective particle trace's representation.
+ */
+class TraceRecorder final {
+public:
+  TraceRecorder() = default;
+  ~TraceRecorder() = default;
+
+  void InitRecording(const std::size_t num_traces,
+                     const std::size_t expected_trace_length) {
+    particles_ = ParticlePopulation{num_traces, expected_trace_length};
+  }
+
+  void InitTrace(const std::size_t trace_id, const point_t &seed) {
+    assert(trace_id < particles_.GetNumberOfTraces());
+    auto &trace = particles_[trace_id];
+    trace.clear();
+    trace.push_back(seed);
+  }
+
+  void AppendTracePoint(const std::size_t trace_id, const point_t &point) {
+    assert(trace_id < particles_.GetNumberOfTraces());
+    particles_[trace_id].push_back(point);
+  }
+
+  ParticlePopulation &GetPopulation() { return particles_; }
+
+private:
+  ParticlePopulation particles_;
+};
+}
+
+#endif // Include guard.
diff --git a/include/pli_vis/third_party/tangent-base/tracer_base.hpp b/include/pli_vis/third_party/tangent-base/tracer_base.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0fc3035edec57688a8721235c4f840b92b177b0f
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-base/tracer_base.hpp
@@ -0,0 +1,101 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TRACER_BASE_H
+#define TRACER_BASE_H
+
+#include "base_types.hpp"
+#include "base_operations.hpp"
+
+#include <cassert>
+#include <vector>
+
+namespace tangent {
+
+/**
+ * TracerBase provides common infrastructure for a number of derived tracers.
+ */
+template <typename TracerTrait> class TracerBase {
+public:
+  using Data = typename TracerTrait::Data;
+  using Integrator = typename TracerTrait::Integrator;
+  using IntegrationStep = typename Integrator::IntegrationStep;
+  using Recorder = typename TracerTrait::Recorder;
+
+  TracerBase(Recorder *recorder)
+      : data_{nullptr},
+        recorder_(recorder), default_step_{0.01f}, num_iterations_{100} {
+    assert(recorder_ != nullptr);
+  }
+  virtual ~TracerBase() = default;
+
+  void SetData(const Data *data) { data_ = data; }
+  const Data *GetData() const { return data_; }
+
+  void SetIntegrationStep(const float_t step) { default_step_ = step; }
+  float_t GetIntegrationStep() const { return default_step_; }
+
+  void SetNumberOfIterations(const std::size_t n) { num_iterations_ = n; }
+  std::size_t GetNumberOfIterations() const { return num_iterations_; }
+
+protected:
+  Recorder *GetRecorder() const { return recorder_; }
+
+  std::vector<point_t> InitOutput(const std::size_t num_seeds) {
+    recorder_->InitRecording(num_seeds, num_iterations_ / 4);
+    std::vector<point_t> output_points;
+    output_points.resize(num_seeds);
+    return output_points;
+  }
+
+  IntegrationStep InitStep(std::size_t trace_id, const point_t &seed) {
+    recorder_->InitTrace(trace_id, seed);
+    return IntegrationStep{seed, default_step_};
+  }
+
+  IntegrationStep AdvectParticle(std::size_t trace_id,
+                                 const Integrator &integrator,
+                                 IntegrationStep &&step) {
+    for (std::size_t iteration = 0; iteration < num_iterations_; ++iteration) {
+      integrator.ComputeStep(step);
+      if (step.HasFinished()) {
+        recorder_->AppendTracePoint(trace_id, step.end_point);
+        step.RestartAt(step.end_point);
+      } else {
+        step.end_point = step.start_point;
+        break;
+      }
+    }
+    return step;
+  }
+
+private:
+  const Data *data_;
+  float_t default_step_;
+  std::size_t num_iterations_;
+  Recorder *recorder_;
+};
+};
+
+#endif
diff --git a/include/pli_vis/third_party/tangent-tbb/tbb_default_tracers.hpp b/include/pli_vis/third_party/tangent-tbb/tbb_default_tracers.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4f480d5507d5daf1e03935151a8953e781deecd3
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-tbb/tbb_default_tracers.hpp
@@ -0,0 +1,39 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TBB_TRACER_DEFAULTS_H
+#define TBB_TRACER_DEFAULTS_H
+
+#include <tangent-base/default_tracers.hpp>
+#include "tbb_pos_tracer.hpp"
+
+#include <cassert>
+#include <vector>
+
+namespace tangent {
+  using TBBCartGridPointMappingTracer = TBBPOSTracer<CartesianPointAdvectionTrait>;
+  using TBBCartGridStreamlineTracer = TBBPOSTracer<CartesianStreamlineTrait>;
+}
+
+#endif
diff --git a/include/pli_vis/third_party/tangent-tbb/tbb_pos_tracer.hpp b/include/pli_vis/third_party/tangent-tbb/tbb_pos_tracer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..be6fb0195b10292088f9af50a610e6b1f445ee9d
--- /dev/null
+++ b/include/pli_vis/third_party/tangent-tbb/tbb_pos_tracer.hpp
@@ -0,0 +1,86 @@
+//-------------------------------------------------------------------------------
+// tangent
+//
+// Copyright(c) 2017 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualisation Group.
+//-------------------------------------------------------------------------------
+//                                 License
+//
+// This framework is free software : you can redistribute it and / or modify
+// it under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// In the future, we may decide to add a commercial license
+// at our own discretion without further notice.
+//
+// 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.If not, see < http://www.gnu.org/licenses/>.
+//-------------------------------------------------------------------------------
+#ifndef TBB_POS_TRACER_H
+#define TBB_POS_TRACER_H
+
+#include <tangent-base/base_types.hpp>
+#include <tangent-base/tracer_base.hpp>
+
+#include <vector>
+
+#include <tbb/tbb.h>
+
+namespace tangent {
+/**
+ * TBBPOSTracer uses Intel Threading Building Blocks (TBB) for a parallel
+ * particle integration based on the "parallelize-over-seed" (POS) strategy.
+ */
+template <typename TracerTrait>
+class TBBPOSTracer final : public TracerBase<TracerTrait> {
+public:
+  using Data = typename TracerTrait::Data;
+  using Integrator = typename TracerTrait::Integrator;
+  using IntegrationStep = typename Integrator::IntegrationStep;
+  using Recorder = typename TracerTrait::Recorder;
+
+  TBBPOSTracer(Recorder *recorder) : TracerBase<TracerTrait>(recorder){};
+  ~TBBPOSTracer() = default;
+
+  std::vector<point_t> TraceSeeds(const std::vector<point_t> &seeds) {
+    const std::int64_t num_seeds = static_cast<std::int64_t>(seeds.size());
+    auto output_points = this->InitOutput(num_seeds);
+    TraceFunctor advect_seeds{this, seeds, output_points};
+    tbb::parallel_for(tbb::blocked_range<int>(0, static_cast<int>(num_seeds)),
+                      advect_seeds);
+    return output_points;
+  }
+
+protected:
+  struct TraceFunctor {
+    TBBPOSTracer<TracerTrait> *tracer_;
+    const std::vector<point_t> &seeds_;
+    std::vector<point_t> &output_points_;
+
+    TraceFunctor(TBBPOSTracer<TracerTrait> *tracer,
+                 const std::vector<point_t> &seeds,
+                 std::vector<point_t> &output_points)
+        : tracer_{tracer}, seeds_{seeds}, output_points_{output_points} {}
+
+    void operator()(const tbb::blocked_range<int> &range) const {
+      Integrator integrator(tracer_->GetData());
+      for (int seed_id = range.begin(); seed_id != range.end(); ++seed_id) {
+        auto result = tracer_->AdvectParticle(
+            seed_id, integrator, tracer_->InitStep(seed_id, seeds_[seed_id]));
+        output_points_[seed_id] = result.end_point;
+      }
+    }
+  };
+  friend TraceFunctor;
+
+private:
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/ui/application.hpp b/include/pli_vis/ui/application.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7d7c285933e77bd2b45b176785045b595176b361
--- /dev/null
+++ b/include/pli_vis/ui/application.hpp
@@ -0,0 +1,46 @@
+#ifndef PLI_VIS_WINDOW_HPP_
+#define PLI_VIS_WINDOW_HPP_
+
+#include <vector>
+
+#include <QLabel>
+#include <QMainWindow>
+#include <QProgressBar>
+
+#include <pli_vis/aspects/loggable.hpp>
+#include <pli_vis/ui/widgets/wait_spinner.hpp>
+#include <pli_vis/ui/plugin_base.hpp>
+#include <ui_window.h>
+
+namespace pli
+{
+class application : public QMainWindow, public Ui_window, public loggable<application>
+{
+public:
+   application();
+  ~application();
+
+  void set_is_loading(const bool is_loading) const;
+
+  template<typename plugin_type>
+  plugin_type* get_plugin()
+  {
+    for (auto plugin : plugins_)
+      if (typeid(*plugin) == typeid(plugin_type))
+        return reinterpret_cast<plugin_type*>(plugin);
+    return nullptr;
+  }
+
+private:
+  void bind_actions         ();
+  void create_gpu_status_bar();
+
+  std::vector<plugin_base*> plugins_;
+  pli::wait_spinner* wait_spinner_;
+
+  QLabel*       gpu_status_label_ = nullptr;
+  QProgressBar* gpu_status_bar_   = nullptr;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/plugin.hpp b/include/pli_vis/ui/plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..046d417aea9daeab2af8521ac9cd4720586a1898
--- /dev/null
+++ b/include/pli_vis/ui/plugin.hpp
@@ -0,0 +1,32 @@
+#ifndef PLI_VIS_PLUGIN_HPP_
+#define PLI_VIS_PLUGIN_HPP_
+
+#include <pli_vis/aspects/loggable.hpp>
+#include <pli_vis/ui/plugin_base.hpp>
+
+namespace pli
+{
+template<typename derived, typename ui_type>
+class plugin : public plugin_base, public loggable<derived>, public ui_type
+{
+public:
+  explicit plugin(QWidget* parent = nullptr) : plugin_base(parent) 
+  {
+    setupUi(this);
+  }
+  virtual ~plugin() = default;
+
+  void set_owner(application* owner) override
+  {
+    owner_ = owner;
+  }
+  void awake    () override {}
+  void start    () override {}
+  void destroy  () override {}
+
+protected:
+  application* owner_ = nullptr;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/plugin_base.hpp b/include/pli_vis/ui/plugin_base.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..42ae6e97f5e01bab198c67778494e34f3411b798
--- /dev/null
+++ b/include/pli_vis/ui/plugin_base.hpp
@@ -0,0 +1,23 @@
+#ifndef PLI_VIS_PLUGIN_BASE_HPP_
+#define PLI_VIS_PLUGIN_BASE_HPP_
+
+#include <QWidget>
+
+namespace pli
+{
+class application;
+
+class plugin_base : public QWidget
+{
+public:
+  explicit plugin_base(QWidget* parent = nullptr) : QWidget(parent) { }
+  virtual ~plugin_base() = default;
+
+  virtual void set_owner(application* owner) = 0;
+  virtual void awake    ()                   = 0;
+  virtual void start    ()                   = 0;
+  virtual void destroy  ()                   = 0;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/plugins/color_plugin.hpp b/include/pli_vis/ui/plugins/color_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2e95f902c219df42d00b5cf68e3d6b0e7a20b69d
--- /dev/null
+++ b/include/pli_vis/ui/plugins/color_plugin.hpp
@@ -0,0 +1,25 @@
+#ifndef PLI_VIS_COLOR_PLUGIN_HPP_
+#define PLI_VIS_COLOR_PLUGIN_HPP_
+
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_color_toolbox.h>
+
+namespace pli
+{
+class color_plugin : public plugin<color_plugin, Ui_color_toolbox>
+{
+  Q_OBJECT
+
+signals :
+  void on_change(int mode, float k, bool inverted);
+
+public:
+  explicit color_plugin(QWidget* parent = nullptr);
+
+  int   mode    () const;
+  float k       () const;
+  bool  inverted() const;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/plugins/color_toolbox.ui b/include/pli_vis/ui/plugins/color_toolbox.ui
new file mode 100644
index 0000000000000000000000000000000000000000..8baeec2db4a2a2849a684ad36cda70c20c609af9
--- /dev/null
+++ b/include/pli_vis/ui/plugins/color_toolbox.ui
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>color_toolbox</class>
+ <widget class="QWidget" name="color_toolbox">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>264</width>
+    <height>233</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Color Toolbox</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_2">
+   <item row="0" column="0" colspan="3">
+    <widget class="QGroupBox" name="group_box">
+     <property name="title">
+      <string>Color Space</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QRadioButton" name="radio_button_hsl_1">
+        <property name="text">
+         <string>HSL - θKφ</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QRadioButton" name="radio_button_hsv_1">
+        <property name="text">
+         <string>HSV - θKφ</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <widget class="QRadioButton" name="radio_button_rgb">
+        <property name="text">
+         <string>RGB - XZY</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QRadioButton" name="radio_button_hsl_2">
+        <property name="text">
+         <string>HSL - θφK</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1" colspan="2">
+       <widget class="QRadioButton" name="radio_button_hsv_2">
+        <property name="text">
+         <string>HSV - θφK</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_k">
+     <property name="text">
+      <string>K</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QSlider" name="slider_k">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>10000</number>
+     </property>
+     <property name="singleStep">
+      <number>1</number>
+     </property>
+     <property name="value">
+      <number>5000</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="tickPosition">
+      <enum>QSlider::NoTicks</enum>
+     </property>
+     <property name="tickInterval">
+      <number>1</number>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="2">
+    <widget class="QLineEdit" name="line_edit_k">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0.5</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_invert_phi">
+     <property name="text">
+      <string>Invert φ </string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QCheckBox" name="checkbox_invert_k">
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>86</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>radio_button_hsl_1</tabstop>
+  <tabstop>radio_button_hsl_2</tabstop>
+  <tabstop>radio_button_hsv_1</tabstop>
+  <tabstop>radio_button_hsv_2</tabstop>
+  <tabstop>radio_button_rgb</tabstop>
+  <tabstop>slider_k</tabstop>
+  <tabstop>line_edit_k</tabstop>
+  <tabstop>checkbox_invert_k</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/include/pli_vis/ui/plugins/data_plugin.hpp b/include/pli_vis/ui/plugins/data_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7abe881b2606597aa2e1c08c0920635c3a4748bc
--- /dev/null
+++ b/include/pli_vis/ui/plugins/data_plugin.hpp
@@ -0,0 +1,105 @@
+#ifndef PLI_VIS_DATA_PLUGIN_HPP_
+#define PLI_VIS_DATA_PLUGIN_HPP_
+
+#include <future>
+
+#include <vector_types.h>
+
+#include <pli_vis/io/io.hpp>
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_data_toolbox.h>
+
+namespace pli
+{
+class data_plugin : public plugin<data_plugin, Ui_data_toolbox>
+{
+  Q_OBJECT
+
+public:
+  explicit data_plugin(QWidget* parent = nullptr);
+
+  const std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>& transmittance_bounds() const
+  {
+    return transmittance_bounds_;
+  }
+  const std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>& retardation_bounds  () const
+  {
+    return retardation_bounds_;
+  }
+  const std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>& direction_bounds    () const
+  {
+    return direction_bounds_;
+  }
+  const std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>& inclination_bounds  () const
+  {
+    return inclination_bounds_;
+  }
+  const std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>>& mask_bounds         () const
+  {
+    return mask_bounds_;
+  }
+  const std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>>& unit_vector_bounds  () const
+  {
+    return unit_vector_bounds_;
+  }
+
+  const boost::multi_array<float, 3>& transmittance() const
+  {
+    return *transmittance_;
+  }
+  const boost::multi_array<float, 3>& retardation  () const
+  {
+    return *retardation_;
+  }
+  const boost::multi_array<float, 3>& direction    () const
+  {
+    return *direction_;
+  }
+  const boost::multi_array<float, 3>& inclination  () const
+  {
+    return *inclination_;
+  }
+  const boost::multi_array<float, 3>& mask         () const
+  {
+    return *mask_;
+  }
+  const boost::multi_array<float, 4>& unit_vector  () const
+  {
+    return *unit_vector_;
+  }
+  
+  std::array<std::size_t, 3> selection_offset() const;
+  std::array<std::size_t, 3> selection_size  () const;
+  std::array<std::size_t, 3> selection_stride() const;
+
+  boost::multi_array<unsigned char, 2> generate_preview_image  (std::size_t x_resolution = 2048 );
+  boost::multi_array<unsigned char, 2> generate_selection_image(std::size_t x_resolution = 2048 );
+  boost::multi_array<float3, 3>        generate_vectors        (bool        cartesian    = false);
+  
+signals:
+  void on_load();
+
+private:
+  void start() override;
+  void setup();
+
+  io                io_    ;
+  std::future<void> future_;
+  
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> transmittance_bounds_;
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> retardation_bounds_  ;
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> direction_bounds_    ; 
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> inclination_bounds_  ;  
+  std::pair<std::array<std::size_t, 3>, std::array<std::size_t, 3>> mask_bounds_         ;    
+  std::pair<std::array<std::size_t, 4>, std::array<std::size_t, 4>> unit_vector_bounds_  ;
+
+  std::unique_ptr<boost::multi_array<float, 3>> transmittance_;
+  std::unique_ptr<boost::multi_array<float, 3>> retardation_  ;
+  std::unique_ptr<boost::multi_array<float, 3>> direction_    ;
+  std::unique_ptr<boost::multi_array<float, 3>> inclination_  ;
+  std::unique_ptr<boost::multi_array<float, 3>> mask_         ;
+  std::unique_ptr<boost::multi_array<float, 4>> unit_vector_  ;
+};
+}
+
+#endif
diff --git a/include/ui/plugins/selector_toolbox.ui b/include/pli_vis/ui/plugins/data_toolbox.ui
similarity index 93%
rename from include/ui/plugins/selector_toolbox.ui
rename to include/pli_vis/ui/plugins/data_toolbox.ui
index a24849417e268434b066ae157a136368c081f761..d0bd1f21fdf286ad2acbd1d180c80561b8ed68d1 100644
--- a/include/ui/plugins/selector_toolbox.ui
+++ b/include/pli_vis/ui/plugins/data_toolbox.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
- <class>selector_toolbox</class>
- <widget class="QWidget" name="selector_toolbox">
+ <class>data_toolbox</class>
+ <widget class="QWidget" name="data_toolbox">
   <property name="enabled">
    <bool>true</bool>
   </property>
@@ -9,8 +9,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>240</width>
-    <height>263</height>
+    <width>367</width>
+    <height>530</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -20,10 +20,28 @@
    </sizepolicy>
   </property>
   <property name="windowTitle">
-   <string>Selector Toolbox</string>
+   <string>Data Toolbox</string>
   </property>
   <layout class="QGridLayout" name="gridLayout_2">
    <item row="0" column="0" colspan="5">
+    <layout class="QHBoxLayout" name="horizontal_layout">
+     <item>
+      <widget class="QPushButton" name="button_open_slice">
+       <property name="text">
+        <string>Open Per Slice Dataset</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="button_open_volume">
+       <property name="text">
+        <string>Open Volume Dataset</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="0" colspan="5">
     <widget class="QxtLetterBoxWidget" name="letterbox">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@@ -47,8 +65,8 @@
       <property name="spacing">
        <number>0</number>
       </property>
-      <item row="0" column="0">
-       <widget class="pli::overview_image" name="image">
+      <item row="1" column="0">
+       <widget class="pli::roi_selector" name="image">
         <property name="sizePolicy">
          <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
           <horstretch>0</horstretch>
@@ -490,7 +508,7 @@
      </layout>
     </widget>
    </item>
-   <item row="1" column="1">
+   <item row="2" column="1">
     <widget class="QLabel" name="label_offset">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@@ -500,8 +518,8 @@
      </property>
      <property name="font">
       <font>
-       <weight>75</weight>
-       <bold>true</bold>
+       <weight>50</weight>
+       <bold>false</bold>
       </font>
      </property>
      <property name="text">
@@ -509,7 +527,7 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="2">
+   <item row="2" column="2">
     <widget class="QLabel" name="label_size">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@@ -519,8 +537,8 @@
      </property>
      <property name="font">
       <font>
-       <weight>75</weight>
-       <bold>true</bold>
+       <weight>50</weight>
+       <bold>false</bold>
       </font>
      </property>
      <property name="text">
@@ -528,7 +546,7 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="3">
+   <item row="2" column="3">
     <widget class="QLabel" name="label_stride">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@@ -538,8 +556,8 @@
      </property>
      <property name="font">
       <font>
-       <weight>75</weight>
-       <bold>true</bold>
+       <weight>50</weight>
+       <bold>false</bold>
       </font>
      </property>
      <property name="text">
@@ -547,7 +565,7 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="0">
+   <item row="3" column="0">
     <widget class="QLabel" name="label_x">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@@ -558,8 +576,8 @@
      <property name="font">
       <font>
        <pointsize>8</pointsize>
-       <weight>75</weight>
-       <bold>true</bold>
+       <weight>50</weight>
+       <bold>false</bold>
       </font>
      </property>
      <property name="text">
@@ -570,7 +588,7 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="1">
+   <item row="3" column="1">
     <widget class="QLineEdit" name="line_edit_offset_x">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -604,7 +622,7 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="2">
+   <item row="3" column="2">
     <widget class="QLineEdit" name="line_edit_size_x">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -638,7 +656,7 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="3">
+   <item row="3" column="3">
     <widget class="QLineEdit" name="line_edit_stride_x">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -672,7 +690,7 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="4">
+   <item row="3" column="4">
     <widget class="QxtSpanSlider" name="slider_x">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -697,7 +715,7 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="0">
+   <item row="4" column="0">
     <widget class="QLabel" name="label_y">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@@ -708,8 +726,8 @@
      <property name="font">
       <font>
        <pointsize>8</pointsize>
-       <weight>75</weight>
-       <bold>true</bold>
+       <weight>50</weight>
+       <bold>false</bold>
       </font>
      </property>
      <property name="text">
@@ -720,7 +738,7 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="1">
+   <item row="4" column="1">
     <widget class="QLineEdit" name="line_edit_offset_y">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -754,7 +772,7 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="2">
+   <item row="4" column="2">
     <widget class="QLineEdit" name="line_edit_size_y">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -788,7 +806,7 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="3">
+   <item row="4" column="3">
     <widget class="QLineEdit" name="line_edit_stride_y">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -822,7 +840,7 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="4">
+   <item row="4" column="4">
     <widget class="QxtSpanSlider" name="slider_y">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -847,7 +865,7 @@
      </property>
     </widget>
    </item>
-   <item row="4" column="0">
+   <item row="5" column="0">
     <widget class="QLabel" name="label_z">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@@ -858,8 +876,8 @@
      <property name="font">
       <font>
        <pointsize>8</pointsize>
-       <weight>75</weight>
-       <bold>true</bold>
+       <weight>50</weight>
+       <bold>false</bold>
       </font>
      </property>
      <property name="text">
@@ -870,7 +888,7 @@
      </property>
     </widget>
    </item>
-   <item row="4" column="1">
+   <item row="5" column="1">
     <widget class="QLineEdit" name="line_edit_offset_z">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -904,7 +922,7 @@
      </property>
     </widget>
    </item>
-   <item row="4" column="2">
+   <item row="5" column="2">
     <widget class="QLineEdit" name="line_edit_size_z">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -938,7 +956,7 @@
      </property>
     </widget>
    </item>
-   <item row="4" column="3">
+   <item row="5" column="3">
     <widget class="QLineEdit" name="line_edit_stride_z">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -972,7 +990,7 @@
      </property>
     </widget>
    </item>
-   <item row="4" column="4">
+   <item row="5" column="4">
     <widget class="QxtSpanSlider" name="slider_z">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -997,14 +1015,14 @@
      </property>
     </widget>
    </item>
-   <item row="5" column="0" colspan="5">
+   <item row="6" column="0" colspan="5">
     <widget class="QPushButton" name="button_update">
      <property name="text">
       <string>Update Viewer</string>
      </property>
     </widget>
    </item>
-   <item row="6" column="0" colspan="5">
+   <item row="7" column="0" colspan="5">
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
@@ -1044,20 +1062,37 @@
   <customwidget>
    <class>QxtSpanSlider</class>
    <extends>QSlider</extends>
-   <header location="global">third_party/qxt/QxtSpanSlider.h</header>
+   <header location="global">pli_vis/third_party/qxt/QxtSpanSlider.h</header>
   </customwidget>
   <customwidget>
    <class>QxtLetterBoxWidget</class>
    <extends>QFrame</extends>
-   <header location="global">third_party/qxt/QxtLetterBoxWidget.h</header>
+   <header location="global">pli_vis/third_party/qxt/QxtLetterBoxWidget.h</header>
    <container>1</container>
   </customwidget>
   <customwidget>
-   <class>pli::overview_image</class>
+   <class>pli::roi_selector</class>
    <extends>QLabel</extends>
-   <header location="global">ui/overview_image.hpp</header>
+   <header location="global">pli_vis/ui/widgets/roi_selector.hpp</header>
   </customwidget>
  </customwidgets>
+ <tabstops>
+  <tabstop>button_open_slice</tabstop>
+  <tabstop>button_open_volume</tabstop>
+  <tabstop>line_edit_offset_x</tabstop>
+  <tabstop>line_edit_offset_y</tabstop>
+  <tabstop>line_edit_offset_z</tabstop>
+  <tabstop>line_edit_size_x</tabstop>
+  <tabstop>line_edit_size_y</tabstop>
+  <tabstop>line_edit_size_z</tabstop>
+  <tabstop>line_edit_stride_x</tabstop>
+  <tabstop>line_edit_stride_y</tabstop>
+  <tabstop>line_edit_stride_z</tabstop>
+  <tabstop>slider_x</tabstop>
+  <tabstop>slider_y</tabstop>
+  <tabstop>slider_z</tabstop>
+  <tabstop>button_update</tabstop>
+ </tabstops>
  <resources/>
  <connections/>
 </ui>
diff --git a/include/pli_vis/ui/plugins/fom_plugin.hpp b/include/pli_vis/ui/plugins/fom_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d441172f1e80cbbe26b03d2ad59d8ede7eb2350
--- /dev/null
+++ b/include/pli_vis/ui/plugins/fom_plugin.hpp
@@ -0,0 +1,25 @@
+#ifndef PLI_VIS_FOM_PLUGIN_HPP_
+#define PLI_VIS_FOM_PLUGIN_HPP_
+
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_fom_toolbox.h>
+
+namespace pli
+{
+class vector_field;
+
+class fom_plugin : public plugin<fom_plugin, Ui_fom_toolbox>
+{
+public:
+  explicit fom_plugin(QWidget* parent = nullptr);
+
+  void start() override;
+
+private:
+  void upload();
+
+  vector_field* vector_field_;
+};
+}
+
+#endif
diff --git a/include/ui/plugins/fom_toolbox.ui b/include/pli_vis/ui/plugins/fom_toolbox.ui
similarity index 52%
rename from include/ui/plugins/fom_toolbox.ui
rename to include/pli_vis/ui/plugins/fom_toolbox.ui
index 2066ac69f6754a2227f8c04234e5e919b931c575..04603ea3e919216e42e6844ea3fc97b5df5b4572 100644
--- a/include/ui/plugins/fom_toolbox.ui
+++ b/include/pli_vis/ui/plugins/fom_toolbox.ui
@@ -6,15 +6,48 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>280</width>
-    <height>70</height>
+    <width>290</width>
+    <height>126</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Fiber Orientation Map Toolbox</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
-   <item row="1" column="1" colspan="2">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_enabled">
+     <property name="text">
+      <string>Enabled</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QCheckBox" name="checkbox_enabled">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_scale">
+     <property name="text">
+      <string>Fiber Scale</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
     <widget class="QSlider" name="slider_fiber_scale">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -42,7 +75,7 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="3">
+   <item row="1" column="2">
     <widget class="QLineEdit" name="line_edit_fiber_scale">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -55,15 +88,15 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="0">
-    <widget class="QLabel" name="label_scale">
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_view_dependent">
      <property name="text">
-      <string>Fiber Scale</string>
+      <string>View Dependent</string>
      </property>
     </widget>
    </item>
-   <item row="0" column="1" colspan="4">
-    <widget class="QCheckBox" name="checkbox_enabled">
+   <item row="2" column="1">
+    <widget class="QCheckBox" name="checkbox_view_dependent">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>0</horstretch>
@@ -77,18 +110,71 @@
       <string/>
      </property>
      <property name="checked">
-      <bool>true</bool>
+      <bool>false</bool>
      </property>
     </widget>
    </item>
-   <item row="0" column="0">
-    <widget class="QLabel" name="label_enabled">
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_rate_of_decay">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
      <property name="text">
-      <string>Enabled</string>
+      <string>Rate of Decay</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QSlider" name="slider_rate_of_decay">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="minimum">
+      <number>1</number>
+     </property>
+     <property name="maximum">
+      <number>100</number>
+     </property>
+     <property name="value">
+      <number>1</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="2">
+    <widget class="QLineEdit" name="line_edit_rate_of_decay">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="placeholderText">
+      <string>1</string>
      </property>
     </widget>
    </item>
-   <item row="2" column="0" colspan="5">
+   <item row="4" column="0" colspan="3">
     <spacer name="spacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
diff --git a/include/pli_vis/ui/plugins/global_tractography_plugin.hpp b/include/pli_vis/ui/plugins/global_tractography_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ddad5f5cd49835d36839a2e7a5136bf5b8ba7f54
--- /dev/null
+++ b/include/pli_vis/ui/plugins/global_tractography_plugin.hpp
@@ -0,0 +1,27 @@
+#ifndef PLI_VIS_GLOBAL_TRACTOGRAPHY_PLUGIN_HPP_
+#define PLI_VIS_GLOBAL_TRACTOGRAPHY_PLUGIN_HPP_
+
+#include <future>
+
+#include <pli_vis/ui/plugin.hpp>
+#include <pli_vis/visualization/algorithms/streamline_renderer.hpp>
+#include <ui_global_tractography_toolbox.h>
+
+namespace pli
+{
+class global_tractography_plugin : public plugin<global_tractography_plugin, Ui_global_tractography_toolbox>
+{
+public:
+  explicit global_tractography_plugin(QWidget* parent = nullptr);
+
+  void start() override;
+  
+protected:
+  void trace();
+
+  std::future<void>    future_;
+  streamline_renderer* streamline_renderer_ = nullptr;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/ui/plugins/global_tractography_toolbox.ui b/include/pli_vis/ui/plugins/global_tractography_toolbox.ui
new file mode 100644
index 0000000000000000000000000000000000000000..a617302823a94f0b2f39a4f7bfffb62fc5c751be
--- /dev/null
+++ b/include/pli_vis/ui/plugins/global_tractography_toolbox.ui
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>global_tractography_toolbox</class>
+ <widget class="QWidget" name="global_tractography_toolbox">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>345</width>
+    <height>81</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Global Tractography Toolbox</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_enabled">
+     <property name="text">
+      <string>Enabled</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QCheckBox" name="checkbox_enabled">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" colspan="2">
+    <widget class="QPushButton" name="button_trace_selection">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>Trace Selection</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>318</width>
+       <height>12</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/include/pli_vis/ui/plugins/interactor_plugin.hpp b/include/pli_vis/ui/plugins/interactor_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f9db5ef389580ba108b665e99eaa185428b9515e
--- /dev/null
+++ b/include/pli_vis/ui/plugins/interactor_plugin.hpp
@@ -0,0 +1,18 @@
+#ifndef PLI_VIS_INTERACTOR_PLUGIN_HPP_
+#define PLI_VIS_INTERACTOR_PLUGIN_HPP_
+
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_interactor_toolbox.h>
+
+namespace pli
+{
+class interactor_plugin : public plugin<interactor_plugin, Ui_interactor_toolbox>
+{
+public:
+  explicit interactor_plugin(QWidget* parent = nullptr);
+
+  void start() override;
+};
+}
+
+#endif
diff --git a/include/ui/plugins/interactor_toolbox.ui b/include/pli_vis/ui/plugins/interactor_toolbox.ui
similarity index 63%
rename from include/ui/plugins/interactor_toolbox.ui
rename to include/pli_vis/ui/plugins/interactor_toolbox.ui
index 733dd168470a336be56b3b0f66af5a07923d5011..2ab80dc6f99a3bfd56ebb5375beee8945defb729 100644
--- a/include/ui/plugins/interactor_toolbox.ui
+++ b/include/pli_vis/ui/plugins/interactor_toolbox.ui
@@ -6,15 +6,130 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>280</width>
-    <height>110</height>
+    <width>253</width>
+    <height>188</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Interactor Toolbox</string>
   </property>
-  <layout class="QGridLayout" name="grid_layout">
-   <item row="0" column="2">
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_controls">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>Controls</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1" colspan="2">
+    <widget class="QSplitter" name="splitter_2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <widget class="QRadioButton" name="radio_button_vtklike">
+      <property name="text">
+       <string>VTK-like</string>
+      </property>
+      <property name="checked">
+       <bool>true</bool>
+      </property>
+     </widget>
+     <widget class="QRadioButton" name="radio_button_orbit">
+      <property name="text">
+       <string>Orbit</string>
+      </property>
+     </widget>
+     <widget class="QRadioButton" name="radio_button_wasd">
+      <property name="text">
+       <string>WASD</string>
+      </property>
+     </widget>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_projection">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>Projection</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1" colspan="2">
+    <widget class="QSplitter" name="splitter">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <widget class="QRadioButton" name="radio_button_perspective">
+      <property name="text">
+       <string>Perspective</string>
+      </property>
+      <property name="checked">
+       <bool>true</bool>
+      </property>
+     </widget>
+     <widget class="QRadioButton" name="radio_button_orthographic">
+      <property name="text">
+       <string>Orthographic</string>
+      </property>
+      <property name="checked">
+       <bool>false</bool>
+      </property>
+     </widget>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_move_speed">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>Move Speed</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QSlider" name="slider_move_speed">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>10000</number>
+     </property>
+     <property name="value">
+      <number>10000</number>
+     </property>
+     <property name="sliderPosition">
+      <number>10000</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="2">
     <widget class="QLineEdit" name="line_edit_move_speed">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -33,7 +148,7 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="0">
+   <item row="3" column="0">
     <widget class="QLabel" name="label_look_speed">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -46,7 +161,7 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="1">
+   <item row="3" column="1">
     <widget class="QSlider" name="slider_look_speed">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -64,14 +179,14 @@
       <number>10000</number>
      </property>
      <property name="value">
-      <number>1000</number>
+      <number>2500</number>
      </property>
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
     </widget>
    </item>
-   <item row="1" column="2">
+   <item row="3" column="2">
     <widget class="QLineEdit" name="line_edit_look_speed">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -90,7 +205,7 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="0" colspan="3">
+   <item row="4" column="0" colspan="3">
     <widget class="QPushButton" name="button_reset_camera">
      <property name="enabled">
       <bool>true</bool>
@@ -106,7 +221,7 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="0" colspan="3">
+   <item row="5" column="1">
     <spacer name="spacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
@@ -119,47 +234,6 @@
      </property>
     </spacer>
    </item>
-   <item row="0" column="0">
-    <widget class="QLabel" name="label_move_speed">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-       <horstretch>0</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="text">
-      <string>Move Speed</string>
-     </property>
-    </widget>
-   </item>
-   <item row="0" column="1">
-    <widget class="QSlider" name="slider_move_speed">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-       <horstretch>4</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="minimumSize">
-      <size>
-       <width>0</width>
-       <height>0</height>
-      </size>
-     </property>
-     <property name="maximum">
-      <number>10000</number>
-     </property>
-     <property name="value">
-      <number>10</number>
-     </property>
-     <property name="sliderPosition">
-      <number>10</number>
-     </property>
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-    </widget>
-   </item>
   </layout>
  </widget>
  <tabstops>
diff --git a/include/pli_vis/ui/plugins/local_tractography_plugin.hpp b/include/pli_vis/ui/plugins/local_tractography_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b82a37718d6f5b7797900a656823e6dc3b85b52
--- /dev/null
+++ b/include/pli_vis/ui/plugins/local_tractography_plugin.hpp
@@ -0,0 +1,33 @@
+#ifndef PLI_VIS_LOCAL_TRACTOGRAPHY_PLUGIN_HPP_
+#define PLI_VIS_LOCAL_TRACTOGRAPHY_PLUGIN_HPP_
+
+#include <future>
+
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_local_tractography_toolbox.h>
+
+namespace pli
+{
+class local_tractography_plugin : public plugin<local_tractography_plugin, Ui_local_tractography_toolbox>
+{
+public:
+  explicit local_tractography_plugin(QWidget* parent = nullptr);
+
+  void start() override;
+
+private:
+  void trace();
+
+  std::array<std::size_t, 3> seed_offset() const;
+  std::array<std::size_t, 3> seed_size  () const;
+  std::array<std::size_t, 3> seed_stride() const;
+
+  renderable*       streamline_renderer_;
+  std::future<void> future_             ;
+  bool              line_ao_            = false;
+  bool              gpu_tracing_        = true ;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/plugins/local_tractography_toolbox.ui b/include/pli_vis/ui/plugins/local_tractography_toolbox.ui
new file mode 100644
index 0000000000000000000000000000000000000000..7920201e6ff8a8f57bcd54b31ef77ef9c1864ca6
--- /dev/null
+++ b/include/pli_vis/ui/plugins/local_tractography_toolbox.ui
@@ -0,0 +1,1298 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>local_tractography_toolbox</class>
+ <widget class="QWidget" name="local_tractography_toolbox">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>337</width>
+    <height>480</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Local Tractography Toolbox</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_enabled">
+     <property name="text">
+      <string>Enabled</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QCheckBox" name="checkbox_enabled">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" colspan="5">
+    <widget class="QxtLetterBoxWidget" name="letterbox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>4</verstretch>
+      </sizepolicy>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <property name="leftMargin">
+       <number>0</number>
+      </property>
+      <property name="topMargin">
+       <number>0</number>
+      </property>
+      <property name="rightMargin">
+       <number>0</number>
+      </property>
+      <property name="bottomMargin">
+       <number>0</number>
+      </property>
+      <property name="spacing">
+       <number>0</number>
+      </property>
+      <item row="1" column="0">
+       <widget class="pli::roi_selector" name="image">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="minimumSize">
+         <size>
+          <width>0</width>
+          <height>0</height>
+         </size>
+        </property>
+        <property name="palette">
+         <palette>
+          <active>
+           <colorrole role="WindowText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Button">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Light">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Midlight">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Dark">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Mid">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Text">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="BrightText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ButtonText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Base">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Window">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Shadow">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="AlternateBase">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ToolTipBase">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>220</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ToolTipText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+          </active>
+          <inactive>
+           <colorrole role="WindowText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Button">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Light">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Midlight">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Dark">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Mid">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Text">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="BrightText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ButtonText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Base">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Window">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Shadow">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="AlternateBase">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ToolTipBase">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>220</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ToolTipText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+          </inactive>
+          <disabled>
+           <colorrole role="WindowText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Button">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Light">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Midlight">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Dark">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Mid">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Text">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="BrightText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>255</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ButtonText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Base">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Window">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="Shadow">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="AlternateBase">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ToolTipBase">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>255</red>
+              <green>255</green>
+              <blue>220</blue>
+             </color>
+            </brush>
+           </colorrole>
+           <colorrole role="ToolTipText">
+            <brush brushstyle="SolidPattern">
+             <color alpha="255">
+              <red>0</red>
+              <green>0</green>
+              <blue>0</blue>
+             </color>
+            </brush>
+           </colorrole>
+          </disabled>
+         </palette>
+        </property>
+        <property name="autoFillBackground">
+         <bool>true</bool>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+        <property name="scaledContents">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="5">
+    <layout class="QGridLayout" name="gridLayout_4">
+     <item row="0" column="1">
+      <widget class="QLabel" name="label_offset">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Offset</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QLabel" name="label_size">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Size</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="3">
+      <widget class="QLabel" name="label_stride">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Stride</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_x">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>8</pointsize>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>X</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLineEdit" name="line_edit_offset_x">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Offset</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="2">
+      <widget class="QLineEdit" name="line_edit_size_x">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Size</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="3">
+      <widget class="QLineEdit" name="line_edit_stride_x">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Size</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>1</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="4">
+      <widget class="QxtSpanSlider" name="slider_x">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>1</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="maximum">
+        <number>0</number>
+       </property>
+       <property name="value">
+        <number>0</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_y">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>8</pointsize>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Y</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="line_edit_offset_y">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Offset</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="2">
+      <widget class="QLineEdit" name="line_edit_size_y">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Size</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="3">
+      <widget class="QLineEdit" name="line_edit_stride_y">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Size</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>1</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="4">
+      <widget class="QxtSpanSlider" name="slider_y">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>1</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="maximum">
+        <number>0</number>
+       </property>
+       <property name="value">
+        <number>0</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="label_z">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>8</pointsize>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Z</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QLineEdit" name="line_edit_offset_z">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Offset</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="2">
+      <widget class="QLineEdit" name="line_edit_size_z">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Size</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="3">
+      <widget class="QLineEdit" name="line_edit_stride_z">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>42</width>
+         <height>21</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Size</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+       <property name="placeholderText">
+        <string>1</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="4">
+      <widget class="QxtSpanSlider" name="slider_z">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>1</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="maximum">
+        <number>0</number>
+       </property>
+       <property name="value">
+        <number>0</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QLabel" name="label_integration_step">
+     <property name="text">
+      <string>Integration Step</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="2" colspan="2">
+    <widget class="QSlider" name="slider_integration_step">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="minimum">
+      <number>1</number>
+     </property>
+     <property name="maximum">
+      <number>10000</number>
+     </property>
+     <property name="value">
+      <number>10000</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="4">
+    <widget class="QLineEdit" name="line_edit_integration_step">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="placeholderText">
+      <string>1</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="label_iterations">
+     <property name="text">
+      <string>Iterations</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="2" colspan="2">
+    <widget class="QSlider" name="slider_iterations">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="minimum">
+      <number>1</number>
+     </property>
+     <property name="maximum">
+      <number>10000</number>
+     </property>
+     <property name="value">
+      <number>10</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="4">
+    <widget class="QLineEdit" name="line_edit_iterations">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="placeholderText">
+      <string>10</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="3" colspan="2">
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+       <horstretch>0</horstretch>
+       <verstretch>1</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Shading</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_3">
+      <property name="spacing">
+       <number>0</number>
+      </property>
+      <property name="leftMargin">
+       <number>6</number>
+      </property>
+      <property name="topMargin">
+       <number>0</number>
+      </property>
+      <property name="rightMargin">
+       <number>0</number>
+      </property>
+      <property name="bottomMargin">
+       <number>0</number>
+      </property>
+      <item>
+       <widget class="QRadioButton" name="radio_button_regular">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="text">
+         <string>Regular</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="radio_button_lineao">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="text">
+         <string>Line AO</string>
+        </property>
+        <property name="checked">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="6" column="0" colspan="5">
+    <widget class="QPushButton" name="button_trace_selection">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>Trace Selection</string>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="0">
+    <spacer name="spacer_vertical">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>4</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="5" column="0" colspan="3">
+    <widget class="QGroupBox" name="groupBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+       <horstretch>0</horstretch>
+       <verstretch>1</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Device</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_2">
+      <property name="spacing">
+       <number>0</number>
+      </property>
+      <property name="leftMargin">
+       <number>6</number>
+      </property>
+      <property name="topMargin">
+       <number>0</number>
+      </property>
+      <property name="rightMargin">
+       <number>0</number>
+      </property>
+      <property name="bottomMargin">
+       <number>0</number>
+      </property>
+      <item>
+       <widget class="QRadioButton" name="radio_button_gpu">
+        <property name="text">
+         <string>GPU</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="radio_button_cpu">
+        <property name="text">
+         <string>CPU</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QxtSpanSlider</class>
+   <extends>QSlider</extends>
+   <header location="global">pli_vis/third_party/qxt/QxtSpanSlider.h</header>
+  </customwidget>
+  <customwidget>
+   <class>QxtLetterBoxWidget</class>
+   <extends>QFrame</extends>
+   <header location="global">pli_vis/third_party/qxt/QxtLetterBoxWidget.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>pli::roi_selector</class>
+   <extends>QLabel</extends>
+   <header location="global">pli_vis/ui/widgets/roi_selector.hpp</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>checkbox_enabled</tabstop>
+  <tabstop>line_edit_offset_x</tabstop>
+  <tabstop>line_edit_offset_y</tabstop>
+  <tabstop>line_edit_offset_z</tabstop>
+  <tabstop>line_edit_size_x</tabstop>
+  <tabstop>line_edit_size_y</tabstop>
+  <tabstop>line_edit_size_z</tabstop>
+  <tabstop>line_edit_stride_x</tabstop>
+  <tabstop>line_edit_stride_y</tabstop>
+  <tabstop>line_edit_stride_z</tabstop>
+  <tabstop>slider_x</tabstop>
+  <tabstop>slider_y</tabstop>
+  <tabstop>slider_z</tabstop>
+  <tabstop>slider_integration_step</tabstop>
+  <tabstop>line_edit_integration_step</tabstop>
+  <tabstop>slider_iterations</tabstop>
+  <tabstop>line_edit_iterations</tabstop>
+  <tabstop>radio_button_gpu</tabstop>
+  <tabstop>radio_button_cpu</tabstop>
+  <tabstop>radio_button_regular</tabstop>
+  <tabstop>radio_button_lineao</tabstop>
+  <tabstop>button_trace_selection</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/include/pli_vis/ui/plugins/odf_plugin.hpp b/include/pli_vis/ui/plugins/odf_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8fc8f5ce63a95ede890619c861da552e8c36c99d
--- /dev/null
+++ b/include/pli_vis/ui/plugins/odf_plugin.hpp
@@ -0,0 +1,41 @@
+#ifndef PLI_VIS_ODF_PLUGIN_HPP_
+#define PLI_VIS_ODF_PLUGIN_HPP_
+
+#include <future>
+
+#include <cusolverDn.h>
+#include <boost/multi_array.hpp>
+
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_odf_toolbox.h>
+
+namespace pli
+{
+class odf_field;
+
+class odf_plugin : public plugin<odf_plugin, Ui_odf_toolbox>
+{
+public:
+  explicit odf_plugin(QWidget* parent = nullptr);
+
+  void start  () override;
+  void destroy() override;
+
+private:
+  void calculate         ();
+  void extract_peaks     ();
+  void set_visible_layers() const;
+
+  float                         threshold_multiplier_ = 0.01F;
+  std::size_t                   maxima_count_         = 3;
+  boost::multi_array<float , 4> coefficients_;
+  boost::multi_array<float3, 4> maxima_      ;
+  odf_field*                    odf_field_   ;                              
+  std::future<void>             future_      ;                                          
+  cusolverDnHandle_t            cusolver_    ;
+  cublasHandle_t                cublas_      ;
+
+};
+}
+
+#endif
diff --git a/include/ui/plugins/fdm_toolbox.ui b/include/pli_vis/ui/plugins/odf_toolbox.ui
similarity index 76%
rename from include/ui/plugins/fdm_toolbox.ui
rename to include/pli_vis/ui/plugins/odf_toolbox.ui
index 83954aa7eb756b2c50559b9021613e7d7d845092..144e64a8a9982f3ab807342d9519b34ee3ee987b 100644
--- a/include/ui/plugins/fdm_toolbox.ui
+++ b/include/pli_vis/ui/plugins/odf_toolbox.ui
@@ -1,19 +1,26 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
- <class>fdm_toolbox</class>
- <widget class="QWidget" name="fdm_toolbox">
+ <class>odf_toolbox</class>
+ <widget class="QWidget" name="odf_toolbox">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>337</width>
-    <height>395</height>
+    <width>295</width>
+    <height>428</height>
    </rect>
   </property>
   <property name="windowTitle">
-   <string>Fiber Distribution Map Toolbox</string>
+   <string>Orientation Distribution Function Toolbox</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_enabled">
+     <property name="text">
+      <string>Enabled</string>
+     </property>
+    </widget>
+   </item>
    <item row="0" column="1">
     <widget class="QCheckBox" name="checkbox_enabled">
      <property name="layoutDirection">
@@ -27,8 +34,38 @@
      </property>
     </widget>
    </item>
-   <item row="5" column="1" colspan="2">
-    <widget class="QSlider" name="slider_histogram_theta">
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_even_only">
+     <property name="text">
+      <string>Symmetric</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QCheckBox" name="checkbox_even_only">
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_vector_block_x">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Supervoxel X extent</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1" colspan="2">
+    <widget class="QSlider" name="slider_vector_block_x">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>4</horstretch>
@@ -42,18 +79,27 @@
       </size>
      </property>
      <property name="maximum">
-      <number>360</number>
+      <number>256</number>
+     </property>
+     <property name="singleStep">
+      <number>1</number>
      </property>
      <property name="value">
-      <number>100</number>
+      <number>64</number>
      </property>
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
+     <property name="tickPosition">
+      <enum>QSlider::NoTicks</enum>
+     </property>
+     <property name="tickInterval">
+      <number>1</number>
+     </property>
     </widget>
    </item>
-   <item row="5" column="3">
-    <widget class="QLineEdit" name="line_edit_histogram_theta">
+   <item row="2" column="3">
+    <widget class="QLineEdit" name="line_edit_vector_block_x">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>1</horstretch>
@@ -71,38 +117,18 @@
      </property>
     </widget>
    </item>
-   <item row="6" column="0">
-    <widget class="QLabel" name="label_histogram_phi">
-     <property name="enabled">
-      <bool>true</bool>
-     </property>
-     <property name="text">
-      <string>Histogram φ bins</string>
-     </property>
-    </widget>
-   </item>
-   <item row="2" column="0">
-    <widget class="QLabel" name="label_vector_block_x">
-     <property name="enabled">
-      <bool>true</bool>
-     </property>
-     <property name="text">
-      <string>Supervoxel X extent</string>
-     </property>
-    </widget>
-   </item>
-   <item row="5" column="0">
-    <widget class="QLabel" name="label_histogram_theta">
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_vector_block_y">
      <property name="enabled">
       <bool>true</bool>
      </property>
      <property name="text">
-      <string>Histogram θ bins</string>
+      <string>Supervoxel Y extent</string>
      </property>
     </widget>
    </item>
-   <item row="6" column="1" colspan="2">
-    <widget class="QSlider" name="slider_histogram_phi">
+   <item row="3" column="1" colspan="2">
+    <widget class="QSlider" name="slider_vector_block_y">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>4</horstretch>
@@ -116,26 +142,19 @@
       </size>
      </property>
      <property name="maximum">
-      <number>180</number>
+      <number>256</number>
+     </property>
+     <property name="singleStep">
+      <number>1</number>
      </property>
      <property name="value">
-      <number>50</number>
+      <number>64</number>
      </property>
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
     </widget>
    </item>
-   <item row="3" column="0">
-    <widget class="QLabel" name="label_vector_block_y">
-     <property name="enabled">
-      <bool>true</bool>
-     </property>
-     <property name="text">
-      <string>Supervoxel Y extent</string>
-     </property>
-    </widget>
-   </item>
    <item row="3" column="3">
     <widget class="QLineEdit" name="line_edit_vector_block_y">
      <property name="sizePolicy">
@@ -155,113 +174,6 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="1" colspan="3">
-    <layout class="QGridLayout" name="layout_depth_checkboxes">
-     <item row="1" column="0">
-      <widget class="QCheckBox" name="checkbox_depth_5">
-       <property name="text">
-        <string>5</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="2">
-      <widget class="QCheckBox" name="checkbox_depth_7">
-       <property name="text">
-        <string>7</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="4">
-      <widget class="QCheckBox" name="checkbox_depth_4">
-       <property name="text">
-        <string>4</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="3">
-      <widget class="QCheckBox" name="checkbox_depth_8">
-       <property name="text">
-        <string>8</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="3">
-      <widget class="QCheckBox" name="checkbox_depth_3">
-       <property name="text">
-        <string>3</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="1">
-      <widget class="QCheckBox" name="checkbox_depth_6">
-       <property name="text">
-        <string>6</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="0">
-      <widget class="QCheckBox" name="checkbox_depth_0">
-       <property name="toolTip">
-        <string/>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="2">
-      <widget class="QCheckBox" name="checkbox_depth_2">
-       <property name="text">
-        <string>2</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="1">
-      <widget class="QCheckBox" name="checkbox_depth_1">
-       <property name="text">
-        <string>1</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="4">
-      <widget class="QCheckBox" name="checkbox_depth_9">
-       <property name="text">
-        <string>9</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-    </layout>
-   </item>
    <item row="4" column="0">
     <widget class="QLabel" name="label_vector_block_z">
      <property name="enabled">
@@ -300,8 +212,37 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="1" colspan="2">
-    <widget class="QSlider" name="slider_vector_block_x">
+   <item row="4" column="3">
+    <widget class="QLineEdit" name="line_edit_vector_block_z">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="0">
+    <widget class="QLabel" name="label_histogram_theta">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Histogram θ bins</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="1" colspan="2">
+    <widget class="QSlider" name="slider_histogram_theta">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>4</horstretch>
@@ -315,27 +256,18 @@
       </size>
      </property>
      <property name="maximum">
-      <number>256</number>
-     </property>
-     <property name="singleStep">
-      <number>1</number>
+      <number>360</number>
      </property>
      <property name="value">
-      <number>64</number>
+      <number>100</number>
      </property>
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
-     <property name="tickPosition">
-      <enum>QSlider::NoTicks</enum>
-     </property>
-     <property name="tickInterval">
-      <number>1</number>
-     </property>
     </widget>
    </item>
-   <item row="2" column="3">
-    <widget class="QLineEdit" name="line_edit_vector_block_x">
+   <item row="5" column="3">
+    <widget class="QLineEdit" name="line_edit_histogram_theta">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>1</horstretch>
@@ -353,8 +285,18 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="1" colspan="2">
-    <widget class="QSlider" name="slider_vector_block_y">
+   <item row="6" column="0">
+    <widget class="QLabel" name="label_histogram_phi">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Histogram φ bins</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="1" colspan="2">
+    <widget class="QSlider" name="slider_histogram_phi">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>4</horstretch>
@@ -368,28 +310,18 @@
       </size>
      </property>
      <property name="maximum">
-      <number>256</number>
-     </property>
-     <property name="singleStep">
-      <number>1</number>
+      <number>180</number>
      </property>
      <property name="value">
-      <number>64</number>
+      <number>50</number>
      </property>
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
     </widget>
    </item>
-   <item row="0" column="0">
-    <widget class="QLabel" name="label_enabled">
-     <property name="text">
-      <string>Enabled</string>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="3">
-    <widget class="QLineEdit" name="line_edit_vector_block_z">
+   <item row="6" column="3">
+    <widget class="QLineEdit" name="line_edit_histogram_phi">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>1</horstretch>
@@ -407,26 +339,18 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="0">
-    <widget class="QLabel" name="label_depths">
-     <property name="text">
-      <string>Multiresolution
-Layers</string>
-     </property>
-    </widget>
-   </item>
-   <item row="8" column="0">
-    <widget class="QLabel" name="label_sampling_theta">
+   <item row="7" column="0">
+    <widget class="QLabel" name="label_maximum_sh_degree">
      <property name="enabled">
       <bool>true</bool>
      </property>
      <property name="text">
-      <string>Sampling θ partitions</string>
+      <string>Maximum SH degree</string>
      </property>
     </widget>
    </item>
-   <item row="8" column="1" colspan="2">
-    <widget class="QSlider" name="slider_sampling_theta">
+   <item row="7" column="1" colspan="2">
+    <widget class="QSlider" name="slider_maximum_sh_degree">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>4</horstretch>
@@ -440,38 +364,18 @@ Layers</string>
       </size>
      </property>
      <property name="maximum">
-      <number>360</number>
+      <number>20</number>
      </property>
      <property name="value">
-      <number>32</number>
+      <number>6</number>
      </property>
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
     </widget>
    </item>
-   <item row="11" column="0">
-    <widget class="QLabel" name="label_threshold">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-     <property name="text">
-      <string>Threshold</string>
-     </property>
-    </widget>
-   </item>
-   <item row="9" column="0">
-    <widget class="QLabel" name="label_sampling_phi">
-     <property name="enabled">
-      <bool>true</bool>
-     </property>
-     <property name="text">
-      <string>Sampling φ partitions</string>
-     </property>
-    </widget>
-   </item>
-   <item row="6" column="3">
-    <widget class="QLineEdit" name="line_edit_histogram_phi">
+   <item row="7" column="3">
+    <widget class="QLineEdit" name="line_edit_maximum_sh_degree">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>1</horstretch>
@@ -489,8 +393,18 @@ Layers</string>
      </property>
     </widget>
    </item>
-   <item row="7" column="1" colspan="2">
-    <widget class="QSlider" name="slider_maximum_sh_degree">
+   <item row="8" column="0">
+    <widget class="QLabel" name="label_sampling_theta">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Sampling θ partitions</string>
+     </property>
+    </widget>
+   </item>
+   <item row="8" column="1" colspan="2">
+    <widget class="QSlider" name="slider_sampling_theta">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>4</horstretch>
@@ -504,26 +418,16 @@ Layers</string>
       </size>
      </property>
      <property name="maximum">
-      <number>20</number>
+      <number>360</number>
      </property>
      <property name="value">
-      <number>6</number>
+      <number>32</number>
      </property>
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
     </widget>
    </item>
-   <item row="7" column="0">
-    <widget class="QLabel" name="label_maximum_sh_degree">
-     <property name="enabled">
-      <bool>true</bool>
-     </property>
-     <property name="text">
-      <string>Maximum SH degree</string>
-     </property>
-    </widget>
-   </item>
    <item row="8" column="3">
     <widget class="QLineEdit" name="line_edit_sampling_theta">
      <property name="sizePolicy">
@@ -543,22 +447,13 @@ Layers</string>
      </property>
     </widget>
    </item>
-   <item row="9" column="3">
-    <widget class="QLineEdit" name="line_edit_sampling_phi">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-       <horstretch>1</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
+   <item row="9" column="0">
+    <widget class="QLabel" name="label_sampling_phi">
+     <property name="enabled">
+      <bool>true</bool>
      </property>
      <property name="text">
-      <string/>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-     <property name="placeholderText">
-      <string>0</string>
+      <string>Sampling φ partitions</string>
      </property>
     </widget>
    </item>
@@ -587,18 +482,8 @@ Layers</string>
      </property>
     </widget>
    </item>
-   <item row="10" column="0">
-    <widget class="QLabel" name="label_clustering_enabled">
-     <property name="text">
-      <string>Clustering</string>
-     </property>
-    </widget>
-   </item>
-   <item row="11" column="3">
-    <widget class="QLineEdit" name="line_edit_threshold">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
+   <item row="9" column="3">
+    <widget class="QLineEdit" name="line_edit_sampling_phi">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
        <horstretch>1</horstretch>
@@ -616,26 +501,206 @@ Layers</string>
      </property>
     </widget>
    </item>
-   <item row="7" column="3">
-    <widget class="QLineEdit" name="line_edit_maximum_sh_degree">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-       <horstretch>1</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
+   <item row="10" column="0">
+    <widget class="QLabel" name="label_hierarchical">
+     <property name="text">
+      <string>Hierarchical</string>
+     </property>
+    </widget>
+   </item>
+   <item row="10" column="1">
+    <widget class="QCheckBox" name="checkbox_hierarchical">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="toolTip">
+      <string>See [HDAG2017].</string>
+     </property>
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
      </property>
      <property name="text">
       <string/>
      </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     <property name="checked">
+      <bool>false</bool>
      </property>
-     <property name="placeholderText">
-      <string>0</string>
+    </widget>
+   </item>
+   <item row="11" column="0">
+    <widget class="QLabel" name="label_layer_checkboxes">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="text">
+      <string>Visible Layers</string>
+     </property>
+    </widget>
+   </item>
+   <item row="11" column="1" colspan="3">
+    <widget class="QWidget" name="layout_layer_checkboxes" native="true">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <layout class="QGridLayout" name="layout_depth_checkboxes">
+      <property name="leftMargin">
+       <number>0</number>
+      </property>
+      <property name="topMargin">
+       <number>0</number>
+      </property>
+      <property name="rightMargin">
+       <number>0</number>
+      </property>
+      <property name="bottomMargin">
+       <number>0</number>
+      </property>
+      <property name="spacing">
+       <number>6</number>
+      </property>
+      <item row="1" column="0">
+       <widget class="QCheckBox" name="checkbox_depth_5">
+        <property name="text">
+         <string>5</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="2">
+       <widget class="QCheckBox" name="checkbox_depth_7">
+        <property name="text">
+         <string>7</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="4">
+       <widget class="QCheckBox" name="checkbox_depth_4">
+        <property name="text">
+         <string>4</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="3">
+       <widget class="QCheckBox" name="checkbox_depth_8">
+        <property name="text">
+         <string>8</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="3">
+       <widget class="QCheckBox" name="checkbox_depth_3">
+        <property name="text">
+         <string>3</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QCheckBox" name="checkbox_depth_6">
+        <property name="text">
+         <string>6</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="0">
+       <widget class="QCheckBox" name="checkbox_depth_0">
+        <property name="toolTip">
+         <string/>
+        </property>
+        <property name="text">
+         <string>0</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <widget class="QCheckBox" name="checkbox_depth_2">
+        <property name="text">
+         <string>2</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QCheckBox" name="checkbox_depth_1">
+        <property name="text">
+         <string>1</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="4">
+       <widget class="QCheckBox" name="checkbox_depth_9">
+        <property name="text">
+         <string>9</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="12" column="0">
+    <widget class="QLabel" name="label_clustering_enabled">
+     <property name="text">
+      <string>Clustering</string>
+     </property>
+    </widget>
+   </item>
+   <item row="12" column="1">
+    <widget class="QCheckBox" name="checkbox_clustering_enabled">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="toolTip">
+      <string>See [HDAG2017].</string>
+     </property>
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
      </property>
     </widget>
    </item>
-   <item row="11" column="1" colspan="2">
+   <item row="13" column="0">
+    <widget class="QLabel" name="label_threshold">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="text">
+      <string>Threshold</string>
+     </property>
+    </widget>
+   </item>
+   <item row="13" column="1" colspan="2">
     <widget class="QSlider" name="slider_threshold">
      <property name="enabled">
       <bool>false</bool>
@@ -666,56 +731,77 @@ Layers</string>
      </property>
     </widget>
    </item>
-   <item row="10" column="1">
-    <widget class="QCheckBox" name="checkbox_clustering_enabled">
+   <item row="13" column="3">
+    <widget class="QLineEdit" name="line_edit_threshold">
      <property name="enabled">
-      <bool>true</bool>
-     </property>
-     <property name="toolTip">
-      <string>See [HDAG2017].</string>
+      <bool>false</bool>
      </property>
-     <property name="layoutDirection">
-      <enum>Qt::LeftToRight</enum>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
      </property>
      <property name="text">
       <string/>
      </property>
-     <property name="checked">
-      <bool>false</bool>
-     </property>
-    </widget>
-   </item>
-   <item row="13" column="0">
-    <spacer name="spacer">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
      </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>96</width>
-       <height>67</height>
-      </size>
+     <property name="placeholderText">
+      <string>0</string>
      </property>
-    </spacer>
+    </widget>
    </item>
-   <item row="12" column="0" colspan="2">
+   <item row="14" column="0" colspan="2">
     <widget class="QPushButton" name="button_calculate">
      <property name="text">
       <string>Calculate</string>
      </property>
     </widget>
    </item>
-   <item row="12" column="2" colspan="2">
+   <item row="14" column="2" colspan="2">
     <widget class="QPushButton" name="button_extract_peaks">
      <property name="text">
       <string>Extract Peaks</string>
      </property>
     </widget>
    </item>
+   <item row="15" column="0">
+    <spacer name="spacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>96</width>
+       <height>67</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
   </layout>
  </widget>
  <tabstops>
   <tabstop>checkbox_enabled</tabstop>
+  <tabstop>checkbox_even_only</tabstop>
+  <tabstop>slider_vector_block_x</tabstop>
+  <tabstop>slider_vector_block_y</tabstop>
+  <tabstop>slider_vector_block_z</tabstop>
+  <tabstop>slider_histogram_theta</tabstop>
+  <tabstop>slider_histogram_phi</tabstop>
+  <tabstop>slider_maximum_sh_degree</tabstop>
+  <tabstop>slider_sampling_theta</tabstop>
+  <tabstop>slider_sampling_phi</tabstop>
+  <tabstop>line_edit_vector_block_x</tabstop>
+  <tabstop>line_edit_vector_block_y</tabstop>
+  <tabstop>line_edit_vector_block_z</tabstop>
+  <tabstop>line_edit_histogram_theta</tabstop>
+  <tabstop>line_edit_histogram_phi</tabstop>
+  <tabstop>line_edit_maximum_sh_degree</tabstop>
+  <tabstop>line_edit_sampling_theta</tabstop>
+  <tabstop>line_edit_sampling_phi</tabstop>
+  <tabstop>checkbox_hierarchical</tabstop>
   <tabstop>checkbox_depth_0</tabstop>
   <tabstop>checkbox_depth_1</tabstop>
   <tabstop>checkbox_depth_2</tabstop>
@@ -726,26 +812,11 @@ Layers</string>
   <tabstop>checkbox_depth_7</tabstop>
   <tabstop>checkbox_depth_8</tabstop>
   <tabstop>checkbox_depth_9</tabstop>
-  <tabstop>slider_vector_block_x</tabstop>
-  <tabstop>line_edit_vector_block_x</tabstop>
-  <tabstop>slider_vector_block_y</tabstop>
-  <tabstop>line_edit_vector_block_y</tabstop>
-  <tabstop>slider_vector_block_z</tabstop>
-  <tabstop>line_edit_vector_block_z</tabstop>
-  <tabstop>slider_histogram_theta</tabstop>
-  <tabstop>line_edit_histogram_theta</tabstop>
-  <tabstop>slider_histogram_phi</tabstop>
-  <tabstop>line_edit_histogram_phi</tabstop>
-  <tabstop>slider_maximum_sh_degree</tabstop>
-  <tabstop>line_edit_maximum_sh_degree</tabstop>
-  <tabstop>slider_sampling_theta</tabstop>
-  <tabstop>line_edit_sampling_theta</tabstop>
-  <tabstop>slider_sampling_phi</tabstop>
-  <tabstop>line_edit_sampling_phi</tabstop>
   <tabstop>checkbox_clustering_enabled</tabstop>
   <tabstop>slider_threshold</tabstop>
   <tabstop>line_edit_threshold</tabstop>
   <tabstop>button_calculate</tabstop>
+  <tabstop>button_extract_peaks</tabstop>
  </tabstops>
  <resources/>
  <connections/>
diff --git a/include/pli_vis/ui/plugins/polar_plot_plugin.hpp b/include/pli_vis/ui/plugins/polar_plot_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ffc02cf4deecac940981bcc95f84330de556e12
--- /dev/null
+++ b/include/pli_vis/ui/plugins/polar_plot_plugin.hpp
@@ -0,0 +1,25 @@
+#ifndef PLI_VIS_POLAR_PLOT_PLUGIN_HPP_
+#define PLI_VIS_POLAR_PLOT_PLUGIN_HPP_
+
+#include <future>
+
+#include <pli_vis/ui/plugin.hpp>
+#include <pli_vis/visualization/algorithms/polar_plot_field.hpp>
+#include <ui_polar_plot_toolbox.h>
+
+namespace pli
+{
+class polar_plot_plugin : public plugin<polar_plot_plugin, Ui_polar_plot_toolbox>
+{
+public:
+  explicit polar_plot_plugin(QWidget* parent = nullptr);
+
+  void start() override;
+
+private:
+  std::future<void> future_;
+  polar_plot_field* field_ = nullptr;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/plugins/polar_plot_toolbox.ui b/include/pli_vis/ui/plugins/polar_plot_toolbox.ui
new file mode 100644
index 0000000000000000000000000000000000000000..16be0516897ec83577857caf1c830032c6acdffd
--- /dev/null
+++ b/include/pli_vis/ui/plugins/polar_plot_toolbox.ui
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>polar_plot_toolbox</class>
+ <widget class="QWidget" name="polar_plot_toolbox">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>309</width>
+    <height>427</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Polar Plot Toolbox</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_enabled">
+     <property name="text">
+      <string>Enabled</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QCheckBox" name="checkbox_enabled">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_symmetric">
+     <property name="text">
+      <string>Symmetric</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QCheckBox" name="checkbox_symmetric">
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_superpixel_size">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Superpixel size</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QSlider" name="slider_superpixel_size">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>10000</number>
+     </property>
+     <property name="singleStep">
+      <number>1</number>
+     </property>
+     <property name="value">
+      <number>16</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="tickPosition">
+      <enum>QSlider::NoTicks</enum>
+     </property>
+     <property name="tickInterval">
+      <number>1</number>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="2">
+    <widget class="QLineEdit" name="line_edit_superpixel_size">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_angular_partitions">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Angular partitions</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QSlider" name="slider_angular_partitions">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>360</number>
+     </property>
+     <property name="singleStep">
+      <number>1</number>
+     </property>
+     <property name="value">
+      <number>360</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="tickPosition">
+      <enum>QSlider::NoTicks</enum>
+     </property>
+     <property name="tickInterval">
+      <number>1</number>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="2">
+    <widget class="QLineEdit" name="line_edit_angular_partitions">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0" colspan="3">
+    <widget class="QPushButton" name="button_calculate">
+     <property name="text">
+      <string>Calculate</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="0" colspan="3">
+    <spacer name="spacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>288</width>
+       <height>12</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/include/pli_vis/ui/plugins/scalar_plugin.hpp b/include/pli_vis/ui/plugins/scalar_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..70d3eef138c14896ea927ed96f4550a0fc5a4f33
--- /dev/null
+++ b/include/pli_vis/ui/plugins/scalar_plugin.hpp
@@ -0,0 +1,25 @@
+#ifndef PLI_VIS_SCALAR_PLUGIN_HPP_
+#define PLI_VIS_SCALAR_PLUGIN_HPP_
+
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_scalar_toolbox.h>
+
+namespace pli
+{
+class scalar_field;
+
+class scalar_plugin : public plugin<scalar_plugin, Ui_scalar_toolbox>
+{
+public:
+  explicit scalar_plugin(QWidget* parent = nullptr);
+
+  void start() override;
+
+private:
+  void upload();
+
+  scalar_field* scalar_field_;
+};
+}
+
+#endif
diff --git a/include/ui/plugins/scalar_toolbox.ui b/include/pli_vis/ui/plugins/scalar_toolbox.ui
similarity index 65%
rename from include/ui/plugins/scalar_toolbox.ui
rename to include/pli_vis/ui/plugins/scalar_toolbox.ui
index fb3a264ee804c18a0a82e1b10984e3f705cc7784..6b2c073527fccc41a60ee54e7849cdde9df330f9 100644
--- a/include/ui/plugins/scalar_toolbox.ui
+++ b/include/pli_vis/ui/plugins/scalar_toolbox.ui
@@ -6,22 +6,22 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>280</width>
-    <height>170</height>
+    <width>247</width>
+    <height>147</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Scalar Map Toolbox</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
-   <item row="0" column="0" rowspan="2">
+   <item row="0" column="0">
     <widget class="QLabel" name="label_enabled">
      <property name="text">
       <string>Enabled</string>
      </property>
     </widget>
    </item>
-   <item row="0" column="1" rowspan="2" colspan="2">
+   <item row="0" column="1">
     <widget class="QCheckBox" name="checkbox_enabled">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -40,27 +40,14 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="2" rowspan="5">
-    <spacer name="spacer_horizontal">
-     <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 row="2" column="0">
+   <item row="1" column="0">
     <widget class="QLabel" name="label_transmittance">
      <property name="text">
       <string>Transmittance</string>
      </property>
     </widget>
    </item>
-   <item row="2" column="1">
+   <item row="1" column="1">
     <widget class="QRadioButton" name="checkbox_transmittance">
      <property name="layoutDirection">
       <enum>Qt::LeftToRight</enum>
@@ -73,14 +60,14 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="0">
+   <item row="1" column="2">
     <widget class="QLabel" name="label_retardation">
      <property name="text">
       <string>Retardation</string>
      </property>
     </widget>
    </item>
-   <item row="3" column="1">
+   <item row="1" column="3">
     <widget class="QRadioButton" name="checkbox_retardation">
      <property name="layoutDirection">
       <enum>Qt::LeftToRight</enum>
@@ -93,14 +80,14 @@
      </property>
     </widget>
    </item>
-   <item row="4" column="0">
+   <item row="2" column="0">
     <widget class="QLabel" name="label_direction">
      <property name="text">
       <string>Direction</string>
      </property>
     </widget>
    </item>
-   <item row="4" column="1">
+   <item row="2" column="1">
     <widget class="QRadioButton" name="checkbox_direction">
      <property name="layoutDirection">
       <enum>Qt::LeftToRight</enum>
@@ -113,14 +100,14 @@
      </property>
     </widget>
    </item>
-   <item row="5" column="0">
+   <item row="2" column="2">
     <widget class="QLabel" name="label_inclination">
      <property name="text">
       <string>Inclination</string>
      </property>
     </widget>
    </item>
-   <item row="5" column="1">
+   <item row="2" column="3">
     <widget class="QRadioButton" name="checkbox_inclination">
      <property name="layoutDirection">
       <enum>Qt::LeftToRight</enum>
@@ -133,17 +120,77 @@
      </property>
     </widget>
    </item>
-   <item row="6" column="0" colspan="3">
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_slice">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>Slice</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1" colspan="3">
+    <widget class="QSlider" name="slider_slice">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>10000</number>
+     </property>
+     <property name="value">
+      <number>10000</number>
+     </property>
+     <property name="sliderPosition">
+      <number>10000</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="4">
+    <widget class="QLineEdit" name="line_edit_slice">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>1</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0" colspan="5">
     <widget class="QLabel" name="label">
      <property name="text">
-      <string>For visibility reasons, scalar maps are displayed one slice at a time. Only the first slice of the selection will be shown.</string>
+      <string>For visibility reasons, scalar maps are displayed one slice at a time.</string>
      </property>
      <property name="wordWrap">
       <bool>true</bool>
      </property>
     </widget>
    </item>
-   <item row="7" column="0" colspan="3">
+   <item row="5" column="2">
     <spacer name="spacer_vertical">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
@@ -158,13 +205,6 @@
    </item>
   </layout>
  </widget>
- <tabstops>
-  <tabstop>checkbox_enabled</tabstop>
-  <tabstop>checkbox_transmittance</tabstop>
-  <tabstop>checkbox_retardation</tabstop>
-  <tabstop>checkbox_direction</tabstop>
-  <tabstop>checkbox_inclination</tabstop>
- </tabstops>
  <resources/>
  <connections/>
 </ui>
diff --git a/include/pli_vis/ui/plugins/volume_rendering_plugin.hpp b/include/pli_vis/ui/plugins/volume_rendering_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..30a46d834280ac0d0cd9d8747426e7a66471fc12
--- /dev/null
+++ b/include/pli_vis/ui/plugins/volume_rendering_plugin.hpp
@@ -0,0 +1,25 @@
+#ifndef PLI_VIS_VOLUME_RENDERING_PLUGIN_HPP_
+#define PLI_VIS_VOLUME_RENDERING_PLUGIN_HPP_
+
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_volume_rendering_toolbox.h>
+
+namespace pli
+{
+class volume_renderer;
+
+class volume_rendering_plugin : public plugin<volume_rendering_plugin, Ui_volume_rendering_toolbox>
+{
+public:
+  explicit volume_rendering_plugin(QWidget* parent = nullptr);
+
+  void start () override;
+
+private:
+  void upload();
+  
+  volume_renderer* volume_renderer_ = nullptr;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/plugins/volume_rendering_toolbox.ui b/include/pli_vis/ui/plugins/volume_rendering_toolbox.ui
new file mode 100644
index 0000000000000000000000000000000000000000..b41316647b07c8c308c4b362813b3114514897c6
--- /dev/null
+++ b/include/pli_vis/ui/plugins/volume_rendering_toolbox.ui
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>volume_rendering_toolbox</class>
+ <widget class="QWidget" name="volume_rendering_toolbox">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>280</width>
+    <height>126</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Volume Rendering Toolbox</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_enabled">
+     <property name="text">
+      <string>Enabled</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QCheckBox" name="checkbox_enabled">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_step_size">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>Step Size</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QSlider" name="slider_step_size">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="minimum">
+      <number>1</number>
+     </property>
+     <property name="maximum">
+      <number>1000</number>
+     </property>
+     <property name="value">
+      <number>1</number>
+     </property>
+     <property name="sliderPosition">
+      <number>1</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="2">
+    <widget class="QLineEdit" name="line_edit_step_size">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>1</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QLabel" name="label_transfer_function">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>Transfer Function</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0" colspan="3">
+    <spacer name="spacer_vertical">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>258</width>
+       <height>130</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="3" column="0" colspan="3">
+    <widget class="QFrame" name="transfer_function_frame">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="frameShape">
+      <enum>QFrame::StyledPanel</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="lineWidth">
+      <number>2</number>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <widget class="pli::transfer_function_editor" name="transfer_function_editor" native="true"/>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>pli::transfer_function_editor</class>
+   <extends>QWidget</extends>
+   <header location="global">pli_vis/ui/widgets/transfer_function_editor.hpp</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/include/pli_vis/ui/plugins/zernike_plugin.hpp b/include/pli_vis/ui/plugins/zernike_plugin.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cec43ea17538f1b4527b3f1e9ba32faa99ede351
--- /dev/null
+++ b/include/pli_vis/ui/plugins/zernike_plugin.hpp
@@ -0,0 +1,41 @@
+#ifndef PLI_VIS_ZERNIKE_PLUGIN_HPP_
+#define PLI_VIS_ZERNIKE_PLUGIN_HPP_
+
+#include <future>
+
+#include <vector_types.h>
+
+#include <pli_vis/ui/plugin.hpp>
+#include <ui_zernike_toolbox.h>
+
+namespace pli
+{
+class zernike_field;
+
+class zernike_plugin : public plugin<zernike_plugin, Ui_zernike_toolbox>
+{
+public:
+  struct parameters
+  {
+    uint2    vectors_size   ;
+    uint2    superpixel_size;
+    uint2    partitions     ;
+    unsigned maximum_degree ;
+    bool     symmetric      ;
+    bool     normalize      ;
+    bool     even_only      ;
+    bool     edge_only      ;
+  };
+
+  explicit zernike_plugin(QWidget* parent = nullptr);
+
+  void       start         () override;
+  parameters get_parameters() const   ;
+
+private:
+  std::future<void> future_        ;
+  zernike_field*    zernike_field_ = nullptr;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/plugins/zernike_toolbox.ui b/include/pli_vis/ui/plugins/zernike_toolbox.ui
new file mode 100644
index 0000000000000000000000000000000000000000..cd5deca28d28799d7e4dc04150bd8dce66c7757d
--- /dev/null
+++ b/include/pli_vis/ui/plugins/zernike_toolbox.ui
@@ -0,0 +1,423 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>zernike_toolbox</class>
+ <widget class="QWidget" name="zernike_toolbox">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>312</width>
+    <height>427</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Zernike Moments Toolbox</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_enabled">
+     <property name="text">
+      <string>Enabled</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QCheckBox" name="checkbox_enabled">
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_superpixel_x">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Superpixel X extent</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QSlider" name="slider_superpixel_x">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>256</number>
+     </property>
+     <property name="singleStep">
+      <number>1</number>
+     </property>
+     <property name="value">
+      <number>64</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="tickPosition">
+      <enum>QSlider::NoTicks</enum>
+     </property>
+     <property name="tickInterval">
+      <number>1</number>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="2">
+    <widget class="QLineEdit" name="line_edit_superpixel_x">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_superpixel_y">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Superpixel Y extent</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QSlider" name="slider_superpixel_y">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>256</number>
+     </property>
+     <property name="singleStep">
+      <number>1</number>
+     </property>
+     <property name="value">
+      <number>64</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="2">
+    <widget class="QLineEdit" name="line_edit_superpixel_y">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_partitions_theta">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Angular partitions</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QSlider" name="slider_partitions_theta">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>360</number>
+     </property>
+     <property name="value">
+      <number>60</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="2">
+    <widget class="QLineEdit" name="line_edit_partitions_theta">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="label_partitions_rho">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Radial partitions</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QSlider" name="slider_partitions_rho">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>360</number>
+     </property>
+     <property name="value">
+      <number>60</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="2">
+    <widget class="QLineEdit" name="line_edit_partitions_rho">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="0">
+    <widget class="QLabel" name="label_maximum_degree">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Maximum degree</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="1">
+    <widget class="QSlider" name="slider_maximum_degree">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>4</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximum">
+      <number>250</number>
+     </property>
+     <property name="value">
+      <number>6</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="2">
+    <widget class="QLineEdit" name="line_edit_maximum_degree">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <property name="placeholderText">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="0">
+    <widget class="QLabel" name="label_symmetric">
+     <property name="text">
+      <string>Symmetric accumulation</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="1">
+    <widget class="QCheckBox" name="checkbox_symmetric">
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="0">
+    <widget class="QLabel" name="label_normalize">
+     <property name="text">
+      <string>Normalize histograms</string>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="1">
+    <widget class="QCheckBox" name="checkbox_normalize">
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="8" column="0">
+    <widget class="QLabel" name="label_even_only">
+     <property name="text">
+      <string>Even degrees only</string>
+     </property>
+    </widget>
+   </item>
+   <item row="8" column="1">
+    <widget class="QCheckBox" name="checkbox_even_only">
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="9" column="0">
+    <widget class="QLabel" name="label_edge_only">
+     <property name="text">
+      <string>Edges only</string>
+     </property>
+    </widget>
+   </item>
+   <item row="9" column="1">
+    <widget class="QCheckBox" name="checkbox_edge_only">
+     <property name="layoutDirection">
+      <enum>Qt::LeftToRight</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="10" column="0" colspan="3">
+    <widget class="QPushButton" name="button_calculate">
+     <property name="text">
+      <string>Calculate</string>
+     </property>
+    </widget>
+   </item>
+   <item row="11" column="0" colspan="3">
+    <spacer name="spacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>288</width>
+       <height>209</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/include/utility/line_edit_utility.hpp b/include/pli_vis/ui/utility/line_edit.hpp
similarity index 79%
rename from include/utility/line_edit_utility.hpp
rename to include/pli_vis/ui/utility/line_edit.hpp
index 9e68587b9a2c04b1489e4142aaa10f611d90bbcc..b6403cc5938d92b70ac1ad6dfa547d68e9f15809 100644
--- a/include/utility/line_edit_utility.hpp
+++ b/include/pli_vis/ui/utility/line_edit.hpp
@@ -1,5 +1,5 @@
-#ifndef PLI_VIS_LINE_EDIT_UTILITY_HPP_
-#define PLI_VIS_LINE_EDIT_UTILITY_HPP_
+#ifndef PLI_VIS_LINE_EDIT_HPP_
+#define PLI_VIS_LINE_EDIT_HPP_
 
 #include <string>
 
@@ -8,7 +8,7 @@
 
 namespace pli
 {
-class line_edit_utility
+class line_edit
 {
 public:
   template<typename type = std::string>
diff --git a/include/pli_vis/ui/utility/plot_interactor.hpp b/include/pli_vis/ui/utility/plot_interactor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e853f54651cea4219790824561ecc94d624bca04
--- /dev/null
+++ b/include/pli_vis/ui/utility/plot_interactor.hpp
@@ -0,0 +1,45 @@
+#ifndef PLI_VIS_PLOT_INTERACTOR_
+#define PLI_VIS_PLOT_INTERACTOR_
+
+#include <QObject>
+
+class QCustomEvent;
+class QPoint;
+
+class QwtPlot;
+class QwtPlotCurve;
+
+namespace pli
+{
+class plot_interactor : public QObject
+{
+  Q_OBJECT
+
+public:
+  plot_interactor(QwtPlot* plot);
+
+  virtual bool eventFilter(QObject* sender, QEvent* event);
+  virtual bool event      (QEvent* event);
+
+signals:
+  void on_change();
+
+private:
+  void select_or_add     (const QPoint& point);
+  void remove            (const QPoint& point);
+  void move              (const QPoint& point);
+  void move_by           (int x, int y);
+
+  void show_cursor       (bool enable);
+  void shift_point_cursor(bool up    );
+  void shift_curve_cursor(bool up    );
+
+        QwtPlot* plot();
+  const QwtPlot* plot() const;
+
+  QwtPlotCurve* selected_curve_;
+  int           selected_point_;
+};
+}
+
+#endif
diff --git a/include/pli_vis/ui/utility/text_browser_sink.hpp b/include/pli_vis/ui/utility/text_browser_sink.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c4494de163e9a290e9d6499ac3c51613a172e968
--- /dev/null
+++ b/include/pli_vis/ui/utility/text_browser_sink.hpp
@@ -0,0 +1,33 @@
+#ifndef PLI_VIS_TEXT_BROWSER_SINK_HPP_
+#define PLI_VIS_TEXT_BROWSER_SINK_HPP_
+
+#include <QScrollBar>
+#include <QTextBrowser>
+#include <spdlog/sinks/sink.h>
+
+namespace pli
+{
+class text_browser_sink : public spdlog::sinks::sink
+{
+public:
+  explicit text_browser_sink (QTextBrowser* text_browser) : text_browser_(text_browser)
+  {
+
+  }
+  
+  void log  (const spdlog::details::log_msg& message) override
+  {
+    text_browser_->append(message.formatted.c_str());
+    text_browser_->verticalScrollBar()->setValue(text_browser_->verticalScrollBar()->maximum());
+  }
+  void flush() override
+  {
+
+  }
+
+private:
+  QTextBrowser* text_browser_;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/ui/selection_square.hpp b/include/pli_vis/ui/widgets/roi_rectangle.hpp
similarity index 76%
rename from include/ui/selection_square.hpp
rename to include/pli_vis/ui/widgets/roi_rectangle.hpp
index 56cb03ed41e58612db3e3f6b0f3d71a7aecdb154..7b694e54a941498f480cf77ea6002caf0d87e2e1 100644
--- a/include/ui/selection_square.hpp
+++ b/include/pli_vis/ui/widgets/roi_rectangle.hpp
@@ -8,10 +8,10 @@
 
 namespace pli
 {
-class selection_square : public QWidget
+class roi_rectangle : public QWidget
 {
 public:
-  selection_square(QWidget* parent = nullptr);
+  roi_rectangle(QWidget* parent = nullptr);
 
 private:
   void resizeEvent(QResizeEvent* event) override;
diff --git a/include/ui/overview_image.hpp b/include/pli_vis/ui/widgets/roi_selector.hpp
similarity index 81%
rename from include/ui/overview_image.hpp
rename to include/pli_vis/ui/widgets/roi_selector.hpp
index 1fbc2fc048402fd048625e740f229e2c76c9d266..2a1459e8fbb39a8b74b4270a46d876b7c9a23a84 100644
--- a/include/ui/overview_image.hpp
+++ b/include/pli_vis/ui/widgets/roi_selector.hpp
@@ -6,16 +6,16 @@
 
 #include <QLabel>
 
-#include <ui/selection_square.hpp>
+#include <pli_vis/ui/widgets/roi_rectangle.hpp>
 
 namespace pli
 {
-class overview_image : public QLabel
+class roi_selector : public QLabel
 {
   Q_OBJECT
 
 public:
-  overview_image(QWidget* parent = nullptr);
+  roi_selector(QWidget* parent = nullptr);
 
   void set_selection_offset_percentage(const std::array<float, 2>& perc);
   void set_selection_size_percentage  (const std::array<float, 2>& perc);
@@ -31,7 +31,7 @@ private:
   void mouseReleaseEvent(QMouseEvent* event) override;
   void mouseMoveEvent   (QMouseEvent* event) override;
 
-  std::unique_ptr<selection_square> selection_square_;
+  std::unique_ptr<roi_rectangle> selection_square_;
   bool dragging_ = false;
 };
 }
diff --git a/include/pli_vis/ui/widgets/transfer_function_editor.hpp b/include/pli_vis/ui/widgets/transfer_function_editor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..13f844b436064c6ecc41e259d706954e185260ec
--- /dev/null
+++ b/include/pli_vis/ui/widgets/transfer_function_editor.hpp
@@ -0,0 +1,34 @@
+#ifndef PLI_VIS_TRANSFER_FUNCTION_WIDGET_
+#define PLI_VIS_TRANSFER_FUNCTION_WIDGET_
+
+#include <array>
+
+#include <qwt/qwt_plot.h>
+#include <vector_types.h>
+
+class QwtPlotCurve;
+class QwtPlotHistogram;
+
+namespace pli
+{
+class transfer_function_editor : public QwtPlot
+{
+  Q_OBJECT
+  
+public:
+  transfer_function_editor(QWidget* parent = nullptr);
+
+  std::vector<float4> get_function();
+
+  void set_histogram_entries(const std::vector<std::size_t>& histogram_entries);
+
+signals:
+  void on_change();
+
+protected:
+  std::array<QwtPlotCurve*, 4> curves_   ;
+  QwtPlotHistogram*            histogram_;
+};
+}
+
+#endif
diff --git a/include/ui/viewer.hpp b/include/pli_vis/ui/widgets/viewer.hpp
similarity index 65%
rename from include/ui/viewer.hpp
rename to include/pli_vis/ui/widgets/viewer.hpp
index 8ddfb7ce46a4382acc99fd8f31229083310175a6..cea0c0c2ed8295aaa00bfd83f05c94a12d846d80 100644
--- a/include/ui/viewer.hpp
+++ b/include/pli_vis/ui/widgets/viewer.hpp
@@ -4,15 +4,14 @@
 #include <memory>
 #include <vector>
 
-#include <opengl/opengl.hpp>
+#include <pli_vis/opengl/opengl.hpp>
 
 #include <QOpenGLWidget>
 
-#include <attributes/loggable.hpp>
-#include <attributes/renderable.hpp>
-#include <math/camera.hpp>
-#include <ui/wait_spinner.hpp>
-#include <visualization/interactors/simple_interactor.hpp>
+#include <pli_vis/aspects/loggable.hpp>
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/visualization/interactors/interactor.hpp>
+#include <pli_vis/visualization/primitives/camera.hpp>
 
 namespace pli
 {
@@ -25,8 +24,16 @@ public:
   type* add_renderable   (args&&...   arguments );
   void  remove_renderable(renderable* renderable);
 
-  pli::camera*       camera    () { return &camera_    ; }
-  simple_interactor* interactor() { return &interactor_; }
+  template<typename interactor_type>
+  void set_interactor()
+  {
+    interactor_ = std::make_unique<interactor_type>(&camera_);
+  }
+
+  camera*     camera    () { return &camera_         ; }
+  interactor* interactor() { return interactor_.get(); }
+
+  void reset_camera_transform();
 
   void initializeGL   ()                   override;
   void paintGL        ()                   override;
@@ -36,21 +43,18 @@ public:
   void mousePressEvent(QMouseEvent* event) override;
   void mouseMoveEvent (QMouseEvent* event) override;
 
-  void set_wait_spinner_enabled(bool enabled) const;
-
 private:
   bool                                     initialized_ = false;
   std::vector<std::unique_ptr<renderable>> renderables_ ;
   pli::camera                              camera_      ;
-  pli::simple_interactor                   interactor_;
-  pli::wait_spinner*                       wait_spinner_;
+  std::unique_ptr<pli::interactor>         interactor_  ;
 };
 
 template <typename type, typename ... args>
 type* viewer::add_renderable(args&&... arguments)
 {
   renderables_.emplace_back(new type(arguments...));
-  auto renderable = (type*) renderables_.back().get();
+  auto renderable = static_cast<type*>(renderables_.back().get());
 
   if (initialized_)
     renderable->initialize();
diff --git a/include/ui/wait_spinner.hpp b/include/pli_vis/ui/widgets/wait_spinner.hpp
similarity index 97%
rename from include/ui/wait_spinner.hpp
rename to include/pli_vis/ui/widgets/wait_spinner.hpp
index 98d7162139c26904f1b48e1aa492e4c6d48befec..99de9252be110809749fe3a994b3c44124b96c5f 100644
--- a/include/ui/wait_spinner.hpp
+++ b/include/pli_vis/ui/widgets/wait_spinner.hpp
@@ -1,3 +1,6 @@
+#ifndef PLI_VIS_WAIT_SPINNER_HPP_
+#define PLI_VIS_WAIT_SPINNER_HPP_
+
 /* Original Work Copyright (c) 2012-2014 Alexander Turkin
 Modified 2014 by William Hallatt
 Modified 2015 by Jacob Dawid
@@ -17,8 +20,6 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#pragma once
-
 #include <QWidget>
 #include <QTimer>
 #include <QColor>
@@ -92,4 +93,6 @@ protected:
   int     current_counter_        = 0;
   bool    is_spinning_            = false;
 };
-}
\ No newline at end of file
+}
+
+#endif
\ No newline at end of file
diff --git a/include/ui/window.ui b/include/pli_vis/ui/window.ui
similarity index 75%
rename from include/ui/window.ui
rename to include/pli_vis/ui/window.ui
index 826f1dd90ca4d7044e915a1fbb62f6243a8c2956..481c1725a23bf0de70983b44ce2af94ed1d54eab 100644
--- a/include/ui/window.ui
+++ b/include/pli_vis/ui/window.ui
@@ -195,7 +195,7 @@
           <x>0</x>
           <y>0</y>
           <width>117</width>
-          <height>406</height>
+          <height>364</height>
          </rect>
         </property>
         <property name="font">
@@ -208,30 +208,30 @@
          <string>Data</string>
         </attribute>
        </widget>
-       <widget class="pli::selector_plugin" name="plugin_selection">
+       <widget class="pli::interactor_plugin" name="plugin_interactor">
         <property name="geometry">
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>98</width>
-          <height>28</height>
+          <width>117</width>
+          <height>364</height>
          </rect>
         </property>
         <attribute name="label">
-         <string>Selector</string>
+         <string>Interactor</string>
         </attribute>
        </widget>
-       <widget class="pli::interactor_plugin" name="plugin_interactor">
+       <widget class="pli::color_plugin" name="plugin_color">
         <property name="geometry">
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>98</width>
-          <height>28</height>
+          <width>117</width>
+          <height>364</height>
          </rect>
         </property>
         <attribute name="label">
-         <string>Interactor</string>
+         <string>Color Space</string>
         </attribute>
        </widget>
        <widget class="pli::scalar_plugin" name="plugin_scalar">
@@ -239,8 +239,8 @@
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>98</width>
-          <height>28</height>
+          <width>117</width>
+          <height>364</height>
          </rect>
         </property>
         <attribute name="label">
@@ -252,8 +252,8 @@
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>98</width>
-          <height>28</height>
+          <width>117</width>
+          <height>364</height>
          </rect>
         </property>
         <attribute name="label">
@@ -261,31 +261,70 @@
         </attribute>
         <layout class="QGridLayout" name="grid_layout_fom"/>
        </widget>
-       <widget class="pli::fdm_plugin" name="plugin_fdm">
+       <widget class="pli::odf_plugin" name="plugin_odf">
         <property name="geometry">
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>98</width>
-          <height>28</height>
+          <width>117</width>
+          <height>364</height>
          </rect>
         </property>
         <attribute name="label">
-         <string>Fiber Distribution Maps</string>
+         <string>ODF Maps</string>
         </attribute>
         <layout class="QGridLayout" name="grid_layout_fdm"/>
        </widget>
-       <widget class="pli::tractography_plugin" name="plugin_tractography">
+       <widget class="pli::local_tractography_plugin" name="plugin_local_tractography">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>0</y>
+          <width>117</width>
+          <height>364</height>
+         </rect>
+        </property>
+        <attribute name="label">
+         <string>Local Tractography</string>
+        </attribute>
+       </widget>
+       <widget class="pli::global_tractography_plugin" name="plugin_global_tractography">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>0</y>
+          <width>117</width>
+          <height>364</height>
+         </rect>
+        </property>
+        <attribute name="label">
+         <string>Global Tractography</string>
+        </attribute>
+       </widget>
+       <widget class="pli::zernike_plugin" name="plugin_zernike_moments">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>0</y>
+          <width>117</width>
+          <height>364</height>
+         </rect>
+        </property>
+        <attribute name="label">
+         <string>Zernike Moments</string>
+        </attribute>
+       </widget>
+       <widget class="pli::polar_plot_plugin" name="plugin_polar_plots">
         <property name="geometry">
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>98</width>
-          <height>28</height>
+          <width>117</width>
+          <height>364</height>
          </rect>
         </property>
         <attribute name="label">
-         <string>Tractography</string>
+         <string>Polar Plots</string>
         </attribute>
        </widget>
       </widget>
@@ -293,32 +332,6 @@
     </item>
    </layout>
   </widget>
-  <widget class="QMenuBar" name="menu_bar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>800</width>
-     <height>21</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menu_file">
-    <property name="title">
-     <string>File</string>
-    </property>
-    <addaction name="action_fullscreen"/>
-    <addaction name="action_file_exit"/>
-   </widget>
-   <widget class="QMenu" name="menu_help">
-    <property name="title">
-     <string>Help</string>
-    </property>
-    <addaction name="action_help_version"/>
-    <addaction name="action_help_gpu_info"/>
-   </widget>
-   <addaction name="menu_file"/>
-   <addaction name="menu_help"/>
-  </widget>
   <widget class="QStatusBar" name="status_bar">
    <property name="enabled">
     <bool>false</bool>
@@ -371,49 +384,67 @@
   <customwidget>
    <class>pli::data_plugin</class>
    <extends>QWidget</extends>
-   <header location="global">ui/plugins/data_plugin.hpp</header>
+   <header location="global">pli_vis/ui/plugins/data_plugin.hpp</header>
    <container>1</container>
   </customwidget>
   <customwidget>
    <class>pli::fom_plugin</class>
    <extends>QWidget</extends>
-   <header location="global">ui/plugins/fom_plugin.hpp</header>
+   <header location="global">pli_vis/ui/plugins/fom_plugin.hpp</header>
    <container>1</container>
   </customwidget>
   <customwidget>
-   <class>pli::fdm_plugin</class>
+   <class>pli::odf_plugin</class>
    <extends>QWidget</extends>
-   <header location="global">ui/plugins/fdm_plugin.hpp</header>
+   <header location="global">pli_vis/ui/plugins/odf_plugin.hpp</header>
    <container>1</container>
   </customwidget>
   <customwidget>
    <class>pli::viewer</class>
    <extends>QWidget</extends>
-   <header location="global">ui/viewer.hpp</header>
+   <header location="global">pli_vis/ui/widgets/viewer.hpp</header>
    <container>1</container>
   </customwidget>
   <customwidget>
    <class>pli::scalar_plugin</class>
    <extends>QWidget</extends>
-   <header location="global">ui/plugins/scalar_plugin.hpp</header>
+   <header location="global">pli_vis/ui/plugins/scalar_plugin.hpp</header>
    <container>1</container>
   </customwidget>
   <customwidget>
    <class>pli::interactor_plugin</class>
    <extends>QWidget</extends>
-   <header location="global">ui/plugins/interactor_plugin.hpp</header>
+   <header location="global">pli_vis/ui/plugins/interactor_plugin.hpp</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>pli::local_tractography_plugin</class>
+   <extends>QWidget</extends>
+   <header location="global">pli_vis/ui/plugins/local_tractography_plugin.hpp</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>pli::color_plugin</class>
+   <extends>QWidget</extends>
+   <header location="global">pli_vis/ui/plugins/color_plugin.hpp</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>pli::zernike_plugin</class>
+   <extends>QWidget</extends>
+   <header location="global">pli_vis/ui/plugins/zernike_plugin.hpp</header>
    <container>1</container>
   </customwidget>
   <customwidget>
-   <class>pli::tractography_plugin</class>
+   <class>pli::global_tractography_plugin</class>
    <extends>QWidget</extends>
-   <header location="global">ui/plugins/tractography_plugin.hpp</header>
+   <header location="global">pli_vis/ui/plugins/global_tractography_plugin.hpp</header>
    <container>1</container>
   </customwidget>
   <customwidget>
-   <class>pli::selector_plugin</class>
+   <class>pli::polar_plot_plugin</class>
    <extends>QWidget</extends>
-   <header location="global">ui/plugins/selector_plugin.hpp</header>
+   <header location="global">pli_vis/ui/plugins/polar_plot_plugin.hpp</header>
    <container>1</container>
   </customwidget>
  </customwidgets>
diff --git a/include/pli_vis/utility/make_even.hpp b/include/pli_vis/utility/make_even.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..19b03b9ff23c169d2911afc0e09aaf0b80530061
--- /dev/null
+++ b/include/pli_vis/utility/make_even.hpp
@@ -0,0 +1,10 @@
+#ifndef MAKE_EVEN_HPP_
+#define MAKE_EVEN_HPP_
+
+template<typename type>
+inline int make_even(type value)
+{
+  return value - value % 2;
+}
+
+#endif
diff --git a/include/pli_vis/visualization/algorithms/lineao_streamline_renderer.hpp b/include/pli_vis/visualization/algorithms/lineao_streamline_renderer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ad76c65476d11f534270344a5a94b6ce517eb2b3
--- /dev/null
+++ b/include/pli_vis/visualization/algorithms/lineao_streamline_renderer.hpp
@@ -0,0 +1,70 @@
+#ifndef PLI_VIS_BASIC_TRACER_HPP_
+#define PLI_VIS_BASIC_TRACER_HPP_
+
+#include <memory>
+#include <vector>
+
+#include <vector_types.h>
+
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/opengl/all.hpp>
+#include <pli_vis/visualization/utility/render_target.hpp>
+
+namespace pli
+{
+class lineao_streamline_renderer : public renderable
+{
+public:
+  void initialize()                     override;
+  void render    (const camera* camera) override;
+  
+  void set_data      (
+    const std::vector<float3>& points        , 
+    const std::vector<float3>& directions    , 
+    const std::vector<float4>& random_vectors);
+  void set_ao_samples(const std::size_t& ao_samples);
+
+  glm::uvec2  screen_size() const;
+  std::size_t ao_samples () const;
+
+private:     
+  void initialize_normal_depth_pass(const glm::uvec2& screen_size);
+  void initialize_color_pass       (const glm::uvec2& screen_size);
+  void initialize_zoom_pass        (const glm::uvec2& screen_size);
+  void initialize_main_pass        (const glm::uvec2& screen_size);
+
+  void render_normal_depth_pass    (const camera* camera, const glm::uvec2& screen_size) const;
+  void render_color_pass           (const camera* camera, const glm::uvec2& screen_size) const;
+  void render_zoom_pass            (const camera* camera, const glm::uvec2& screen_size) const;
+  void render_main_pass            (const camera* camera, const glm::uvec2& screen_size) const;
+  
+  std::size_t                          draw_count_               = 0 ;
+  std::size_t                          ao_samples_               = 32;
+                                       
+  // Common data.                      
+  std::unique_ptr<gl::array_buffer>    vertex_buffer_            ;
+  std::unique_ptr<gl::array_buffer>    direction_buffer_         ;
+                                       
+  // Normal depth pass data.           
+  std::unique_ptr<render_target>       normal_depth_map_         ;
+  std::unique_ptr<gl::program>         normal_depth_program_     ;
+  std::unique_ptr<gl::vertex_array>    normal_depth_vertex_array_;
+                                       
+  // Color pass data.                  
+  std::unique_ptr<render_target>       color_map_                ;
+  std::unique_ptr<gl::program>         color_program_            ;
+  std::unique_ptr<gl::vertex_array>    color_vertex_array_       ;
+                                       
+  // Zoom pass data.                   
+  std::unique_ptr<render_target>       zoom_map_                 ;
+  std::unique_ptr<gl::program>         zoom_program_             ;
+  std::unique_ptr<gl::vertex_array>    zoom_vertex_array_        ;
+                                       
+  // Main pass data.                   
+  std::unique_ptr<gl::program>         program_                  ;
+  std::unique_ptr<gl::vertex_array>    vertex_array_             ;
+  std::unique_ptr<gl::texture_3d>      random_texture_           ;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/visualization/odf_field.hpp b/include/pli_vis/visualization/algorithms/odf_field.hpp
similarity index 84%
rename from include/visualization/odf_field.hpp
rename to include/pli_vis/visualization/algorithms/odf_field.hpp
index b405e159d9bc3b00ced30305e1fa7d1d6772b2fa..bf88ae79dbfd334fdda4105f08933ee39ebb79c8 100644
--- a/include/visualization/odf_field.hpp
+++ b/include/pli_vis/visualization/algorithms/odf_field.hpp
@@ -7,8 +7,8 @@
 
 #include <vector_types.h>
 
-#include <attributes/renderable.hpp>
-#include <opengl/all.hpp>
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/opengl/all.hpp>
 
 namespace pli
 {
@@ -20,18 +20,17 @@ public:
 
   void set_data(
     const uint3&   dimensions        ,
-    const unsigned coefficient_count ,
+    const unsigned maximum_degree    ,
     const float*   coefficients      ,
     const uint2&   tessellations     ,
-    const float3&  vector_spacing    ,
     const uint3&   vector_dimensions ,
     const float    scale             = 1.0  ,
+    const bool     hierarchical      = false,
     const bool     clustering        = false,
     const float    cluster_threshold = 0.0  ,
     std::function<void(const std::string&)> status_callback = [](const std::string&){});
 
-  void set_visible_layers(
-    const std::vector<bool>& visible_layers);
+  void set_visible_layers(const std::vector<bool>& visible_layers);
 
 private:
   std::unique_ptr<gl::program>      shader_program_ ;
diff --git a/include/pli_vis/visualization/algorithms/polar_plot_field.hpp b/include/pli_vis/visualization/algorithms/polar_plot_field.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ce66fb4de1705252dda238d5c7895045a0d23a40
--- /dev/null
+++ b/include/pli_vis/visualization/algorithms/polar_plot_field.hpp
@@ -0,0 +1,29 @@
+#ifndef PLI_VIS_POLAR_PLOT_FIELD_HPP_
+#define PLI_VIS_POLAR_PLOT_FIELD_HPP_
+
+#include <memory>
+
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/opengl/all.hpp>
+
+namespace pli
+{
+class polar_plot_field : public renderable
+{
+public:
+  void initialize()                     override;
+  void render    (const camera* camera) override;
+
+  void set_data  (const std::vector<float3>& vertices  , 
+                  const std::vector<float3>& directions);
+
+private:
+  std::unique_ptr<gl::program>      shader_program_  ;
+  std::unique_ptr<gl::vertex_array> vertex_array_    ;
+  std::unique_ptr<gl::array_buffer> vertex_buffer_   ;
+  std::unique_ptr<gl::array_buffer> direction_buffer_;
+  std::size_t                       draw_count_      = 0;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/visualization/scalar_field.hpp b/include/pli_vis/visualization/algorithms/scalar_field.hpp
similarity index 84%
rename from include/visualization/scalar_field.hpp
rename to include/pli_vis/visualization/algorithms/scalar_field.hpp
index 7049ce6f4388ed497882b1e31f3b942d609392b7..0a4010a51add117572cfbe1d199cb69490bc4588 100644
--- a/include/visualization/scalar_field.hpp
+++ b/include/pli_vis/visualization/algorithms/scalar_field.hpp
@@ -5,8 +5,8 @@
 
 #include <vector_types.h>
 
-#include <attributes/renderable.hpp>
-#include <opengl/all.hpp>
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/opengl/all.hpp>
 
 namespace pli
 {
@@ -18,8 +18,7 @@ public:
 
   void set_data(
     const uint3&  dimensions,
-    const float*  scalars   ,
-    const float3& spacing   );
+    const float*  scalars   );
 
 private:
   std::unique_ptr<gl::program>      shader_program_ ;
diff --git a/include/pli_vis/visualization/algorithms/streamline_renderer.hpp b/include/pli_vis/visualization/algorithms/streamline_renderer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d056de15ddc5debf8b144dc7a1c6dc33e291d19
--- /dev/null
+++ b/include/pli_vis/visualization/algorithms/streamline_renderer.hpp
@@ -0,0 +1,33 @@
+#ifndef PLI_VIS_STREAMLINE_RENDERER_HPP_
+#define PLI_VIS_STREAMLINE_RENDERER_HPP_
+
+#include <memory>
+#include <vector>
+
+#include <vector_types.h>
+
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/opengl/all.hpp>
+
+namespace pli
+{
+class streamline_renderer : public renderable
+{
+public:
+  void initialize()                     override;
+  void render    (const camera* camera) override;
+  
+  void set_data(
+    const std::vector<float3>& points    , 
+    const std::vector<float3>& directions);
+
+private:
+  std::size_t                       draw_count_      = 0;
+  std::unique_ptr<gl::program>      program_         ;
+  std::unique_ptr<gl::vertex_array> vertex_array_    ;
+  std::unique_ptr<gl::array_buffer> vertex_buffer_   ;
+  std::unique_ptr<gl::array_buffer> direction_buffer_;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/visualization/algorithms/vector_field.hpp b/include/pli_vis/visualization/algorithms/vector_field.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c204943217f3cb7528eb94ddbd71dd56268817be
--- /dev/null
+++ b/include/pli_vis/visualization/algorithms/vector_field.hpp
@@ -0,0 +1,43 @@
+#ifndef PLI_VIS_VECTOR_FIELD_HPP_
+#define PLI_VIS_VECTOR_FIELD_HPP_
+
+#include <functional>
+#include <memory>
+#include <string>
+
+#include <vector_types.h>
+
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/opengl/all.hpp>
+
+namespace pli
+{
+class vector_field : public renderable
+{
+public:
+  void initialize()                     override;
+  void render    (const camera* camera) override;
+
+  void set_data(
+    const uint3&   dimensions       ,
+    const unsigned vectors_per_point,
+    const float3*  unit_vectors     ,
+    std::function<void(const std::string&)> status_callback = [](const std::string&){});
+  void set_scale                       (float scale  );
+  void set_view_dependent_transparency (bool  enabled);
+  void set_view_dependent_rate_of_decay(float value  );
+
+private:
+  std::unique_ptr<gl::program>      shader_program_  ;
+  std::unique_ptr<gl::vertex_array> vertex_array_    ;
+  std::unique_ptr<gl::array_buffer> direction_buffer_;
+  glm::uvec3                        dimensions_;
+  std::size_t                       draw_count_                   = 0;
+  unsigned                          vectors_per_point_            = 1;
+  float                             scale_                        = 1.0F ;
+  bool                              view_dependent_transparency_  = false;
+  float                             view_dependent_rate_of_decay_ = 1.0F ;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/visualization/algorithms/volume_renderer.hpp b/include/pli_vis/visualization/algorithms/volume_renderer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5a6cadd155f592316978029dde6eac3b03cdf712
--- /dev/null
+++ b/include/pli_vis/visualization/algorithms/volume_renderer.hpp
@@ -0,0 +1,43 @@
+#ifndef PLI_VIS_VOLUME_RENDERER_HPP_
+#define PLI_VIS_VOLUME_RENDERER_HPP_
+
+#include <memory>
+
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/opengl/all.hpp>
+
+namespace pli
+{
+class volume_renderer : public renderable
+{
+public:
+  void initialize()                     override;
+  void render    (const camera* camera) override;
+
+  void set_data             (const uint3& dimensions, const float* data);
+  void set_transfer_function(const std::vector<float4>& transfer_function);
+  void set_step_size        (float step_size);
+
+private:
+  std::unique_ptr<gl::program>      prepass_shader_program_   ;
+  std::unique_ptr<gl::program>      shader_program_           ;
+
+  std::unique_ptr<gl::vertex_array> prepass_vertex_array_     ;
+  std::unique_ptr<gl::vertex_array> vertex_array_             ;
+
+  std::unique_ptr<gl::array_buffer> vertex_buffer_            ;
+  std::unique_ptr<gl::array_buffer> color_buffer_             ;
+  std::unique_ptr<gl::index_buffer> index_buffer_             ;
+
+  std::unique_ptr<gl::texture_1d>   transfer_function_texture_;
+  std::unique_ptr<gl::texture_3d>   volume_texture_           ;
+  
+  std::unique_ptr<gl::framebuffer>  framebuffer_              ;
+  std::unique_ptr<gl::texture_2d>   exit_points_color_texture_;
+  std::unique_ptr<gl::texture_2d>   exit_points_depth_texture_;
+
+  std::size_t                       draw_count_               = 0;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/visualization/algorithms/zernike_field.hpp b/include/pli_vis/visualization/algorithms/zernike_field.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5561bd5e58ac890edf5514465564e576b638272a
--- /dev/null
+++ b/include/pli_vis/visualization/algorithms/zernike_field.hpp
@@ -0,0 +1,43 @@
+#ifndef PLI_VIS_ZERNIKE_FIELD_HPP_
+#define PLI_VIS_ZERNIKE_FIELD_HPP_
+
+#include <memory>
+
+#include <pli_vis/aspects/renderable.hpp>
+#include <pli_vis/opengl/all.hpp>
+#include <pli_vis/opengl/auxiliary/glm_uniforms.hpp>
+#include <pli_vis/visualization/utility/render_target.hpp>
+
+namespace pli
+{
+class zernike_field final : public renderable
+{
+public:
+  void initialize()                     override;
+  void render    (const camera* camera) override;
+
+  void set_data  (const uint2& dimensions, const uint2& spacing, const unsigned coefficients_per_voxel, const std::vector<float>& coefficients);
+
+private:
+  bool                                       needs_update_           = false;
+  glm::uvec2                                 dimensions_             ;
+  glm::uvec2                                 spacing_                ;
+  unsigned                                   coefficients_per_voxel_ = 0;
+  std::size_t                                draw_count_             = 0;
+  std::size_t                                primitive_count_        = 0;
+
+  std::unique_ptr<gl::program>               prepass_program_        ;
+  std::unique_ptr<gl::vertex_array>          prepass_vertex_array_   ;
+  std::unique_ptr<gl::program>               main_program_           ;
+  std::unique_ptr<gl::vertex_array>          main_vertex_array_      ;
+
+  std::unique_ptr<gl::array_buffer>          vertex_buffer_          ;
+  std::unique_ptr<gl::array_buffer>          texcoord_buffer_        ;
+  std::unique_ptr<gl::index_buffer>          index_buffer_           ;
+  std::unique_ptr<gl::shader_storage_buffer> coefficient_buffer_     ;
+
+  std::unique_ptr<render_target>             render_target_          ;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/visualization/interactors/first_person_interactor.hpp b/include/pli_vis/visualization/interactors/first_person_interactor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f89417c0e21dee27b3caa86f4c2c1b9c35a02323
--- /dev/null
+++ b/include/pli_vis/visualization/interactors/first_person_interactor.hpp
@@ -0,0 +1,33 @@
+#ifndef PLI_VIS_FIRST_PERSON_INTERACTOR_HPP_
+#define PLI_VIS_FIRST_PERSON_INTERACTOR_HPP_
+
+#include <map>
+
+#include <QPoint>
+
+#include <pli_vis/visualization/interactors/interactor.hpp>
+
+class QKeyEvent;
+class QMouseEvent;
+
+namespace pli
+{
+class first_person_interactor : public interactor
+{
+public:
+  first_person_interactor(camera* camera);
+
+  void update_transform   ()                   override;
+  void key_press_handler  (QKeyEvent*   event) override;
+  void key_release_handler(QKeyEvent*   event) override;
+  void mouse_press_handler(QMouseEvent* event) override;
+  void mouse_move_handler (QMouseEvent* event) override;
+
+private:
+  std::map<int, bool> key_map_;
+
+  QPoint last_mouse_position_;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/visualization/interactors/interactor.hpp b/include/pli_vis/visualization/interactors/interactor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2eda95d3c3bcb03d2250f8de19e7d3b89d709498
--- /dev/null
+++ b/include/pli_vis/visualization/interactors/interactor.hpp
@@ -0,0 +1,51 @@
+#ifndef PLI_VIS_INTERACTOR_HPP_
+#define PLI_VIS_INTERACTOR_HPP_
+
+class QKeyEvent;
+class QMouseEvent;
+
+namespace pli
+{
+class camera;
+
+class interactor
+{
+public:
+  interactor(camera* camera) : camera_(camera)
+  {
+    
+  }
+  virtual ~interactor() = default;
+
+  virtual void update_transform   ()                   { }
+  virtual void key_press_handler  (QKeyEvent*   event) { }
+  virtual void key_release_handler(QKeyEvent*   event) { }
+  virtual void mouse_press_handler(QMouseEvent* event) { }
+  virtual void mouse_move_handler (QMouseEvent* event) { }
+
+  float move_speed() const
+  {
+    return move_speed_;
+  }
+  float look_speed() const
+  {
+    return look_speed_;
+  }
+
+  void set_move_speed(float move_speed)
+  {
+    move_speed_ = move_speed;
+  }
+  void set_look_speed(float look_speed)
+  {
+    look_speed_ = look_speed;
+  }
+
+protected:
+  camera* camera_;
+  float   move_speed_ = 1.0;
+  float   look_speed_ = 1.0;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/visualization/interactors/orbit_interactor.hpp b/include/pli_vis/visualization/interactors/orbit_interactor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..544285fb5ceb827cafc1e2e747efa19aba84e49d
--- /dev/null
+++ b/include/pli_vis/visualization/interactors/orbit_interactor.hpp
@@ -0,0 +1,26 @@
+#ifndef PLI_VIS_ORBIT_INTERACTOR_HPP_
+#define PLI_VIS_ORBIT_INTERACTOR_HPP_
+
+#include <QPoint>
+
+#include <pli_vis/visualization/interactors/interactor.hpp>
+
+class QKeyEvent;
+class QMouseEvent;
+
+namespace pli
+{
+class orbit_interactor : public interactor
+{
+public:
+  orbit_interactor(camera* camera);
+
+  void mouse_press_handler(QMouseEvent* event) override;
+  void mouse_move_handler (QMouseEvent* event) override;
+
+private:
+  QPoint last_mouse_position_;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/visualization/interactors/simple_interactor.hpp b/include/pli_vis/visualization/interactors/simple_interactor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..70f3755d96464ffe600ff6e2310f9002a3b06baa
--- /dev/null
+++ b/include/pli_vis/visualization/interactors/simple_interactor.hpp
@@ -0,0 +1,26 @@
+#ifndef PLI_VIS_SIMPLE_INTERACTOR_HPP_
+#define PLI_VIS_SIMPLE_INTERACTOR_HPP_
+
+#include <QPoint>
+
+#include <pli_vis/visualization/interactors/interactor.hpp>
+
+class QKeyEvent;
+class QMouseEvent;
+
+namespace pli
+{
+class simple_interactor : public interactor
+{
+public:
+  simple_interactor(camera* camera);
+
+  void mouse_press_handler(QMouseEvent* event) override;
+  void mouse_move_handler (QMouseEvent* event) override;
+
+private:
+  QPoint last_mouse_position_;
+};
+}
+
+#endif
\ No newline at end of file
diff --git a/include/pli_vis/visualization/primitives/camera.hpp b/include/pli_vis/visualization/primitives/camera.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ad24d41ccbc1465f6b37f28b0452b5ad1d2bcae6
--- /dev/null
+++ b/include/pli_vis/visualization/primitives/camera.hpp
@@ -0,0 +1,55 @@
+#ifndef PLI_VIS_CAMERA_HPP_
+#define PLI_VIS_CAMERA_HPP_
+
+#include <glm/glm.hpp>
+
+#include <pli_vis/visualization/primitives/transform.hpp>
+
+namespace pli
+{
+/// A camera is a view transform with an additional projection matrix.
+/// Supports perspective and orthographic projection.
+class camera : public transform
+{
+public:
+  camera();
+
+  bool             orthographic             () const { return orthographic_     ; }
+  float	           near_clip_plane          () const { return near_clip_plane_  ; }
+  float	           far_clip_plane           () const { return far_clip_plane_   ; }
+  float	           aspect_ratio             () const { return aspect_ratio_     ; }
+  float	           vertical_fov             () const { return vertical_fov_     ; }
+  float	           orthographic_size        () const { return orthographic_size_; }
+  
+  const glm::mat4& projection_matrix        () const { return projection_matrix_; }
+        glm::mat4  view_projection_matrix   () const { return projection_matrix_ * inverse_absolute_matrix(); }
+
+  void             set_orthographic         (bool  orthographic     );
+  void             set_near_clip_plane      (float near_clip_plane  );
+  void             set_far_clip_plane       (float far_clip_plane   );
+  void             set_aspect_ratio         (float aspect_ratio     );
+  void             set_vertical_fov         (float vertical_fov     );
+  void             set_orthographic_size    (float orthographic_size);
+
+private:
+  void             update_projection_matrix ();
+
+  /// Toggle for orthographic / perspective. Default perspective.
+  bool  orthographic_      = false;
+ 
+  /// Shared parameters.
+  float	near_clip_plane_   = 0.01F;
+  float	far_clip_plane_    = 10000.0F;
+  float	aspect_ratio_      = 4.0F / 3.0F;
+
+  /// When camera is perspective , camera's viewing volume is defined by vertical fov.
+  float	vertical_fov_      = glm::radians(68.0F);
+  
+  /// When camera is orthographic, camera's viewing volume is defined by orthographic size.
+  float	orthographic_size_ = 100;
+
+  glm::mat4 projection_matrix_;
+};
+}
+
+#endif
diff --git a/include/pli_vis/visualization/primitives/directional_light.hpp b/include/pli_vis/visualization/primitives/directional_light.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2556e43ff24b2557efe3d886b559a031484bf395
--- /dev/null
+++ b/include/pli_vis/visualization/primitives/directional_light.hpp
@@ -0,0 +1,15 @@
+#ifndef PLI_VIS_DIRECTIONAL_LIGHT_HPP_
+#define PLI_VIS_DIRECTIONAL_LIGHT_HPP_
+
+#include <pli_vis/visualization/primitives/light.hpp>
+
+namespace pli
+{
+class directional_light : public light
+{
+public:
+
+};
+}
+
+#endif
diff --git a/include/pli_vis/visualization/primitives/light.hpp b/include/pli_vis/visualization/primitives/light.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..02646e05c525aecd6090bd4e7baa3a4ea9c2d384
--- /dev/null
+++ b/include/pli_vis/visualization/primitives/light.hpp
@@ -0,0 +1,25 @@
+#ifndef PLI_VIS_LIGHT_HPP_
+#define PLI_VIS_LIGHT_HPP_
+
+#include <glm/glm.hpp>
+
+#include <pli_vis/visualization/primitives/transform.hpp>
+
+namespace pli
+{
+class light : public transform
+{
+public:
+  const float&     intensity() const;
+  const glm::vec3& color    () const;
+
+  void set_intensity(float            intensity);
+  void set_color    (const glm::vec3& color    );
+
+protected:
+  float     intensity_ = 1.0F;
+  glm::vec3 color_     = glm::vec3(1.0F);
+};
+}
+
+#endif
diff --git a/include/pli_vis/visualization/primitives/point_light.hpp b/include/pli_vis/visualization/primitives/point_light.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc6bd300643ed873aa4319a7ee51b42e5a3de128
--- /dev/null
+++ b/include/pli_vis/visualization/primitives/point_light.hpp
@@ -0,0 +1,20 @@
+#ifndef PLI_VIS_POINT_LIGHT_HPP_
+#define PLI_VIS_POINT_LIGHT_HPP_
+
+#include <pli_vis/visualization/primitives/light.hpp>
+
+namespace pli
+{
+class point_light : public light
+{
+public:
+  const float& range() const;
+
+  void set_range(float range);
+
+protected:
+  float range_ = 10.0F;
+};
+}
+
+#endif
diff --git a/include/pli_vis/visualization/primitives/spot_light.hpp b/include/pli_vis/visualization/primitives/spot_light.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..56af82f4637a55edd533966c7cedb4843686299b
--- /dev/null
+++ b/include/pli_vis/visualization/primitives/spot_light.hpp
@@ -0,0 +1,23 @@
+#ifndef PLI_VIS_SPOT_LIGHT_HPP_
+#define PLI_VIS_SPOT_LIGHT_HPP_
+
+#include <pli_vis/visualization/primitives/light.hpp>
+
+namespace pli
+{
+class spot_light : public light
+{
+public:
+  const float& range     () const;
+  const float& spot_angle() const;
+
+  void set_range     (float range     );
+  void set_spot_angle(float spot_angle);
+
+protected:
+  float range_      = 10.0F;
+  float spot_angle_ = 30.0F;
+};
+}
+
+#endif
diff --git a/include/pli_vis/visualization/primitives/transform.hpp b/include/pli_vis/visualization/primitives/transform.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ec1212b86ba6f49baf205778fea949ca7188ebe3
--- /dev/null
+++ b/include/pli_vis/visualization/primitives/transform.hpp
@@ -0,0 +1,63 @@
+#ifndef PLI_VIS_TRANSFORM_HPP_
+#define PLI_VIS_TRANSFORM_HPP_
+
+#include <glm/glm.hpp>
+#include <glm/gtc/quaternion.hpp>
+
+#include <vector>
+
+namespace pli
+{
+/// Represents a transformation in 3D space. Supports hierarchies.
+class transform
+{
+public:
+  transform(const glm::vec3& translation                     , const glm::quat& rotation       = glm::quat(0, 0, 0, 1), const glm::vec3& scale = glm::vec3(1, 1, 1));
+  transform(const glm::vec3& translation = glm::vec3(0, 0, 0), const glm::vec3& rotation_euler = glm::vec3(0, 0, 0)   , const glm::vec3& scale = glm::vec3(1, 1, 1));
+  
+  const glm::vec3& translation             () const { return translation_                    ;}
+  const glm::quat& rotation                () const { return rotation_                       ;}
+        glm::vec3  rotation_euler          () const { return glm::degrees(glm::eulerAngles(rotation_)) ;}
+  const glm::vec3& scale                   () const { return scale_                          ;}
+                                       
+  const glm::mat4& matrix                  () const { return matrix_                         ;}
+        glm::mat4  inverse_matrix          () const { return glm::inverse(matrix_)                ;}
+  const glm::mat4& absolute_matrix         () const { return absolute_matrix_                ;}
+        glm::mat4  inverse_absolute_matrix () const { return glm::inverse(absolute_matrix_)       ;}
+                                       
+        glm::vec3  forward                 () const { return rotation_ * glm::vec3(0, 0, 1)      ;}
+        glm::vec3  up                      () const { return rotation_ * glm::vec3(0, 1, 0)      ;}
+        glm::vec3  right                   () const { return rotation_ * glm::vec3(1, 0, 0)      ;}
+                                       
+  transform*       parent                  () const { return parent_                         ;}
+  std::size_t      child_count             () const { return children_.size()                ;}
+                                           
+  transform&       set_translation         (const glm::vec3& translation   );
+  transform&       set_rotation            (const glm::quat& rotation      );
+  transform&       set_rotation_euler      (const glm::vec3& rotation_euler);
+  transform&       set_scale               (const glm::vec3& scale         );
+                                           
+  transform&       translate               (const glm::vec3& amount);
+  transform&       rotate                  (const glm::quat& amount);
+  transform&       look_at                 (const glm::vec3& target, const glm::vec3& up_vector = glm::vec3(0, 1, 0));
+                                           
+  void             set_parent              (transform*  parent);
+  transform*       child                   (std::size_t index ) const;
+
+protected:
+  void             update_matrix           ();
+  void             update_absolute_matrix  ();
+
+  glm::vec3               translation_;
+  glm::quat               rotation_;      
+  glm::vec3               scale_;
+                          
+  glm::mat4               matrix_;        
+  glm::mat4               absolute_matrix_;
+                          
+  transform*              parent_ = nullptr;
+  std::vector<transform*> children_;
+};
+}
+
+#endif
diff --git a/include/pli_vis/visualization/utility/render_target.hpp b/include/pli_vis/visualization/utility/render_target.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0809ddfb99653007846b5f376b239be7b5fa9f0d
--- /dev/null
+++ b/include/pli_vis/visualization/utility/render_target.hpp
@@ -0,0 +1,49 @@
+#ifndef PLI_VIS_RENDER_TARGET_HPP_
+#define PLI_VIS_RENDER_TARGET_HPP_
+
+#include <glm/glm.hpp>
+
+#include <pli_vis/opengl/opengl.hpp>
+#include <pli_vis/opengl/framebuffer.hpp>
+#include <pli_vis/opengl/texture.hpp>
+
+namespace pli
+{
+class render_target
+{
+public:
+  enum class mode
+  {
+    color_only,
+    depth_only,
+    color_and_depth
+  };
+
+  render_target(const glm::uvec2& size = {1u, 1u}, mode mode = mode::color_and_depth);
+  render_target(const render_target&  that) = default;
+  render_target(      render_target&& temp) = default;
+  virtual ~render_target()                  = default;
+
+  render_target& operator=(const render_target&  that) = default;
+  render_target& operator=(      render_target&& temp) = default;
+
+  void resize(const glm::uvec2& size );
+  void bind  ();
+  void unbind();
+
+  void resize_to_viewport();
+
+  gl::framebuffer* framebuffer  ();
+  gl::texture_2d*  color_texture();
+  gl::texture_2d*  depth_texture();
+
+protected:
+  mode            mode_            ;
+  gl::framebuffer last_framebuffer_;
+  gl::framebuffer framebuffer_     ;
+  gl::texture_2d  color_texture_   ;
+  gl::texture_2d  depth_texture_   ;
+};
+}
+
+#endif
diff --git a/include/pli_vis/visualization/utility/sphere_tessellation.hpp b/include/pli_vis/visualization/utility/sphere_tessellation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a326afd33a73cdfcc6015f43b293a2ea5d6f175b
--- /dev/null
+++ b/include/pli_vis/visualization/utility/sphere_tessellation.hpp
@@ -0,0 +1,83 @@
+#ifndef PLI_VIS_SPHERE_TESSELLATION_H_
+#define PLI_VIS_SPHERE_TESSELLATION_H_
+
+#include <vector>
+
+#include <vector_types.h>
+
+#include <pli_vis/cuda/sh/vector_ops.h>
+
+namespace pli
+{
+template<typename vector_precision>
+struct polyhedron
+{
+  std::vector<vector_precision> vertices;
+  std::vector<vector_precision> indices ;
+};
+
+template<typename scalar_precision = float, typename vector_precision = float3>
+polyhedron<vector_precision> make_icosahedron()
+{
+  const scalar_precision x(0.525731112119133606);
+  const scalar_precision z(0.850650808352039932);
+  return polyhedron<vector_precision>
+  {
+    {
+      {-x, 0, z}, { x, 0,  z}, {-x,  0, -z}, { x,  0, -z},
+      { 0, z, x}, { 0, z, -x}, { 0, -z,  x}, { 0, -z, -x},
+      { z, x, 0}, {-z, x,  0}, { z, -x,  0}, {-z, -x,  0}
+    },
+    {
+      {0,  4,  1}, {0, 9,  4}, {9,  5, 4}, { 4, 5, 8}, {4, 8,  1},
+      {8, 10,  1}, {8, 3, 10}, {5,  3, 8}, { 5, 2, 3}, {2, 7,  3},
+      {7, 10,  3}, {7, 6, 10}, {7, 11, 6}, {11, 0, 6}, {0, 1,  6},
+      {6,  1, 10}, {9, 0, 11}, {9, 11, 2}, { 9, 2, 5}, {7, 2, 11}
+    }
+  };
+}
+
+// Normals are in spherical coordinates.
+template<typename vector_precision = float3>
+void tessellate_triangle_normals(
+  const vector_precision&        v1, 
+  const vector_precision&        v2, 
+  const vector_precision&        v3, 
+  const std::size_t&             depth, 
+  std::vector<vector_precision>& normals)
+{
+  if(depth == 0)
+  {
+    normals.push_back(normalize(cross(v2 - v1, v3 - v1)));
+    return;
+  }
+
+  auto v12 = normalize(v1 + v2);
+  auto v23 = normalize(v2 + v3);
+  auto v31 = normalize(v3 + v1);
+
+  tessellate_triangle_normals(v1 , v12, v31, depth - 1, normals);
+  tessellate_triangle_normals(v2 , v23, v12, depth - 1, normals);
+  tessellate_triangle_normals(v3 , v31, v23, depth - 1, normals);
+  tessellate_triangle_normals(v12, v23, v31, depth - 1, normals);
+}
+
+// Normals are in spherical coordinates.
+template<typename vector_precision = float3>
+std::vector<vector_precision> tessellate_polyhedron_normals(
+  const polyhedron<vector_precision>& polyhedron, 
+  const std::size_t&                  depth     )
+{
+  std::vector<vector_precision> normals;
+  for(auto i = 0; i < polyhedron.indices.size(); i++)
+    tessellate_triangle_normals(
+      polyhedron.vertices[polyhedron.indices[i].x],
+      polyhedron.vertices[polyhedron.indices[i].y],
+      polyhedron.vertices[polyhedron.indices[i].z],
+      depth  ,
+      normals);
+  return normals;
+}
+}
+
+#endif
diff --git a/include/sh/clebsch_gordan.h b/include/sh/clebsch_gordan.h
deleted file mode 100644
index 120e35b6e5fc43aa6102e3adbd6d375e2ae82571..0000000000000000000000000000000000000000
--- a/include/sh/clebsch_gordan.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef CUSH_CLEBSCH_GORDAN_H_
-#define CUSH_CLEBSCH_GORDAN_H_
-
-#include <math.h>
-
-#include <sh/decorators.h>
-#include <sh/wigner.h>
-
-namespace cush
-{
-// Based on "Wigner 3j-Symbol." of Eric Weisstein at http://mathworld.wolfram.com/Wigner3j-Symbol.html
-template<typename precision>
-INLINE COMMON precision clebsch_gordan(
-  unsigned int l1, unsigned int l2, unsigned int l3,
-  unsigned int m1, unsigned int m2, unsigned int m3)
-{
-  return pow (precision(-1.0), m3 + l1 - l2) * 
-         sqrt(precision(2.0) * l3 + precision(1.0)) *
-         wigner_3j<precision>(precision(2.0) * l1, precision(2.0) * l2, precision(2.0) * l3, precision(2.0) * m1, precision(2.0) * m2, -precision(2.0) * m3);
-}
-}
-
-#endif
diff --git a/include/sh/cush.h b/include/sh/cush.h
deleted file mode 100644
index 98efe893e19dcf5c058dfb7f8b894c021ebbeed1..0000000000000000000000000000000000000000
--- a/include/sh/cush.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef CUSH_CUSH_H_
-#define CUSH_CUSH_H_
-
-#include <sh/convert.h>
-#include <sh/spherical_harmonics.h>
-
-#endif
diff --git a/include/sh/decorators.h b/include/sh/decorators.h
deleted file mode 100644
index 4b4064be5ba87ec3780205885a62d04c42dcff56..0000000000000000000000000000000000000000
--- a/include/sh/decorators.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef CUSH_CONFIG_H_
-#define CUSH_CONFIG_H_
-
-#ifdef __CUDACC__
-
-  #define GLOBAL __global__
-  #define HOST   __host__
-  #define DEVICE __device__
-  #define SHARED __shared__
-  #define INLINE __forceinline__
-  #define CONSTANT  __constant__ const
-
-#else
-
-  #define GLOBAL
-  #define HOST
-  #define DEVICE
-  #define SHARED
-  #define INLINE inline
-  #define CONSTANT  const
-
-#endif
-
-#define COMMON HOST DEVICE
-
-#endif
diff --git a/include/sh/launch.h b/include/sh/launch.h
deleted file mode 100644
index 34033eb661561235222b8066e51147ce5fbd3502..0000000000000000000000000000000000000000
--- a/include/sh/launch.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef CUSH_LAUNCH_H_
-#define CUSH_LAUNCH_H_
-
-#include <math.h>
-
-#include <device_launch_parameters.h>
-
-#include <sh/decorators.h>
-
-namespace cush
-{
-INLINE COMMON unsigned block_size_1d()
-{
-  return 64;
-}
-INLINE COMMON dim3     block_size_2d()
-{
-  return {32, 32, 1};
-}
-INLINE COMMON dim3     block_size_3d()
-{
-  return {16, 16, 4};
-}
-
-INLINE COMMON unsigned grid_size_1d(unsigned target_dimension )
-{
-  return unsigned(ceil(float(target_dimension) / block_size_1d()));
-}
-INLINE COMMON dim3     grid_size_2d(dim3     target_dimensions)
-{
-  auto block_size = block_size_2d();
-  return {
-    unsigned(ceil(float(target_dimensions.x) / block_size.x)),
-    unsigned(ceil(float(target_dimensions.y) / block_size.y)),
-    1u
-  };
-}
-INLINE COMMON dim3     grid_size_3d(dim3     target_dimensions)
-{
-  auto block_size = block_size_3d();
-  return {
-    unsigned(ceil(float(target_dimensions.x) / block_size.x)),
-    unsigned(ceil(float(target_dimensions.y) / block_size.y)),
-    unsigned(ceil(float(target_dimensions.z) / block_size.z))
-  };
-}
-}
-
-#endif
diff --git a/include/sh/sign.h b/include/sh/sign.h
deleted file mode 100644
index d2f5d5e693678b718fa5e57d5f35d702fe61a3e9..0000000000000000000000000000000000000000
--- a/include/sh/sign.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef CUSH_SIGN_H_
-#define CUSH_SIGN_H_
-
-#include <sh/decorators.h>
-
-namespace cush
-{
-template <typename type> 
-INLINE COMMON int sign(type value) 
-{
-  return (type(0) < value) - (value < type(0));
-}
-}
-
-#endif
diff --git a/include/ui/plugins/data_plugin.hpp b/include/ui/plugins/data_plugin.hpp
deleted file mode 100644
index f6cc1b1d422e984764fd39c2ad55b0cee4dfe929..0000000000000000000000000000000000000000
--- a/include/ui/plugins/data_plugin.hpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef PLI_VIS_DATA_PLUGIN_HPP_
-#define PLI_VIS_DATA_PLUGIN_HPP_
-
-#include <memory>
-
-#include <attributes/loggable.hpp>
-#include <io/hdf5_io_base.hpp>
-#include <ui/plugins/plugin.hpp>
-#include <ui_data_toolbox.h>
-
-namespace pli
-{
-class data_plugin : public plugin, public loggable<data_plugin>, public Ui_data_toolbox
-{
-  Q_OBJECT
-
-public:
-  data_plugin(QWidget* parent = nullptr);
-  void start() override;
-
-  hdf5_io_base* io() const;
-
-signals:
-  void on_change();
-
-private:
-  void set_file(const std::string& filename);
-  std::unique_ptr<hdf5_io_base> io_;
-};
-}
-
-#endif
diff --git a/include/ui/plugins/data_toolbox.ui b/include/ui/plugins/data_toolbox.ui
deleted file mode 100644
index eb2669f17db681ba48072d71086e748647037ddb..0000000000000000000000000000000000000000
--- a/include/ui/plugins/data_toolbox.ui
+++ /dev/null
@@ -1,326 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>data_toolbox</class>
- <widget class="QWidget" name="data_toolbox">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>264</width>
-    <height>289</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Data Toolbox</string>
-  </property>
-  <layout class="QGridLayout" name="gridLayout">
-   <item row="13" column="0">
-    <widget class="QLabel" name="label_unit_vector">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-     <property name="text">
-      <string>Unit Vector</string>
-     </property>
-    </widget>
-   </item>
-   <item row="7" column="1" colspan="2">
-    <widget class="QLineEdit" name="line_edit_vector_spacing">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-       <horstretch>1</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="toolTip">
-      <string>The attribute which specifies the distance between two voxels in each axis.</string>
-     </property>
-     <property name="whatsThis">
-      <string/>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-     <property name="placeholderText">
-      <string>VectorSpacing</string>
-     </property>
-    </widget>
-   </item>
-   <item row="1" column="1">
-    <widget class="QRadioButton" name="radio_button_slice_by_slice">
-     <property name="toolTip">
-      <string>Each slice is represented as a separate dataset, similar to Vervet1818.</string>
-     </property>
-     <property name="text">
-      <string>Slice by slice</string>
-     </property>
-     <property name="checked">
-      <bool>true</bool>
-     </property>
-    </widget>
-   </item>
-   <item row="1" column="2">
-    <widget class="QRadioButton" name="radio_button_volume">
-     <property name="toolTip">
-      <string>The whole data is stored in a single dataset, similar to MSA0309.</string>
-     </property>
-     <property name="text">
-      <string>Volume</string>
-     </property>
-    </widget>
-   </item>
-   <item row="12" column="0">
-    <widget class="QLabel" name="label_inclination">
-     <property name="text">
-      <string>Inclination</string>
-     </property>
-    </widget>
-   </item>
-   <item row="10" column="0">
-    <widget class="QLabel" name="label_retardation">
-     <property name="text">
-      <string>Retardation</string>
-     </property>
-    </widget>
-   </item>
-   <item row="0" column="0">
-    <widget class="QLabel" name="label_file">
-     <property name="font">
-      <font>
-       <weight>75</weight>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>File</string>
-     </property>
-    </widget>
-   </item>
-   <item row="12" column="1" colspan="2">
-    <widget class="QLineEdit" name="line_edit_inclination">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-       <horstretch>1</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="toolTip">
-      <string>The path to the inclination dataset. For &quot;slice by slice&quot; type data, %Slice% acts as a reserved keyword indicating the slice number dataset.</string>
-     </property>
-     <property name="text">
-      <string/>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-     <property name="placeholderText">
-      <string>%Slice%/Microscope/Processed/Registered/Inclination</string>
-     </property>
-    </widget>
-   </item>
-   <item row="5" column="2">
-    <widget class="QPushButton" name="button_browse">
-     <property name="text">
-      <string>Browse</string>
-     </property>
-    </widget>
-   </item>
-   <item row="6" column="0">
-    <widget class="QLabel" name="label_attributes">
-     <property name="font">
-      <font>
-       <weight>75</weight>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Attributes</string>
-     </property>
-    </widget>
-   </item>
-   <item row="11" column="0">
-    <widget class="QLabel" name="label_direction">
-     <property name="text">
-      <string>Direction</string>
-     </property>
-    </widget>
-   </item>
-   <item row="9" column="0">
-    <widget class="QLabel" name="label_transmittance">
-     <property name="text">
-      <string>Transmittance</string>
-     </property>
-    </widget>
-   </item>
-   <item row="11" column="1" colspan="2">
-    <widget class="QLineEdit" name="line_edit_direction">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-       <horstretch>1</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="toolTip">
-      <string>The path to the direction dataset. For &quot;slice by slice&quot; type data, %Slice% acts as a reserved keyword indicating the slice number dataset.</string>
-     </property>
-     <property name="text">
-      <string/>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-     <property name="placeholderText">
-      <string>%Slice%/Microscope/Processed/Registered/Direction</string>
-     </property>
-    </widget>
-   </item>
-   <item row="8" column="0">
-    <widget class="QLabel" name="label_datasets">
-     <property name="font">
-      <font>
-       <weight>75</weight>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Datasets</string>
-     </property>
-    </widget>
-   </item>
-   <item row="5" column="0">
-    <widget class="QLabel" name="label_file_path">
-     <property name="text">
-      <string>Path</string>
-     </property>
-    </widget>
-   </item>
-   <item row="9" column="1" colspan="2">
-    <widget class="QLineEdit" name="line_edit_transmittance">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-       <horstretch>1</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="toolTip">
-      <string>The path to the transmittance dataset. For &quot;slice by slice&quot; type data, %Slice% acts as a reserved keyword indicating the slice number dataset.</string>
-     </property>
-     <property name="text">
-      <string/>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-     <property name="placeholderText">
-      <string>%Slice%/Microscope/Processed/Registered/NTransmittance</string>
-     </property>
-    </widget>
-   </item>
-   <item row="10" column="1" colspan="2">
-    <widget class="QLineEdit" name="line_edit_retardation">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-       <horstretch>1</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="toolTip">
-      <string>The path to the retardation dataset. For &quot;slice by slice&quot; type data, %Slice% acts as a reserved keyword indicating the slice number dataset.</string>
-     </property>
-     <property name="text">
-      <string/>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-     <property name="placeholderText">
-      <string>%Slice%/Microscope/Processed/Registered/Retardation</string>
-     </property>
-    </widget>
-   </item>
-   <item row="1" column="0">
-    <widget class="QLabel" name="label_file_type">
-     <property name="text">
-      <string>Type</string>
-     </property>
-    </widget>
-   </item>
-   <item row="7" column="0">
-    <widget class="QLabel" name="label_vector_spacing">
-     <property name="toolTip">
-      <string/>
-     </property>
-     <property name="whatsThis">
-      <string/>
-     </property>
-     <property name="text">
-      <string>Vector Spacing</string>
-     </property>
-    </widget>
-   </item>
-   <item row="5" column="1">
-    <widget class="QLineEdit" name="line_edit_file">
-     <property name="toolTip">
-      <string>The system path to the root file.</string>
-     </property>
-     <property name="readOnly">
-      <bool>true</bool>
-     </property>
-     <property name="placeholderText">
-      <string>C:/Vervet1818.h5</string>
-     </property>
-    </widget>
-   </item>
-   <item row="13" column="1" colspan="2">
-    <widget class="QLineEdit" name="line_edit_unit_vector">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-       <horstretch>1</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="toolTip">
-      <string>The path to the inclination dataset. For &quot;slice by slice&quot; type data, %Slice% acts as a reserved keyword indicating the slice number dataset.</string>
-     </property>
-     <property name="text">
-      <string/>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-     <property name="placeholderText">
-      <string>%Slice%/Microscope/Processed/Registered/Inclination</string>
-     </property>
-    </widget>
-   </item>
-   <item row="14" column="0" colspan="4">
-    <spacer name="spacer_vertical">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>168</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-  </layout>
- </widget>
- <tabstops>
-  <tabstop>radio_button_slice_by_slice</tabstop>
-  <tabstop>radio_button_volume</tabstop>
-  <tabstop>line_edit_file</tabstop>
-  <tabstop>line_edit_vector_spacing</tabstop>
-  <tabstop>line_edit_transmittance</tabstop>
-  <tabstop>line_edit_retardation</tabstop>
-  <tabstop>line_edit_direction</tabstop>
-  <tabstop>line_edit_inclination</tabstop>
- </tabstops>
- <resources/>
- <connections/>
-</ui>
diff --git a/include/ui/plugins/fdm_plugin.hpp b/include/ui/plugins/fdm_plugin.hpp
deleted file mode 100644
index 7ba1cdeafe76cc678419821dd328d05d72267482..0000000000000000000000000000000000000000
--- a/include/ui/plugins/fdm_plugin.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef PLI_VIS_FDM_PLUGIN_HPP_
-#define PLI_VIS_FDM_PLUGIN_HPP_
-
-#include <future>
-
-#include <cusolverDn.h>
-
-#include <boost/multi_array.hpp>
-
-#include <attributes/loggable.hpp>
-#include <ui/plugins/plugin.hpp>
-#include <ui_fdm_toolbox.h>
-
-namespace pli
-{
-class odf_field;
-
-class fdm_plugin : 
-  public plugin, 
-  public loggable<fdm_plugin>, 
-  public Ui_fdm_toolbox
-{
-public:
-  fdm_plugin(QWidget* parent = nullptr);
-  void start  () override;
-  void destroy() override;
-
-private:
-  void calculate         ();
-  void extract_peaks     ();
-  void set_visible_layers() const;
-
-  float                        threshold_multiplier_ = 0.01F;
-  boost::multi_array<float, 4> coefficients_;
-  odf_field*                   odf_field_   ;                              
-  std::future<void>            future_      ;                                          
-  cusolverDnHandle_t           cusolver_    ;
-  cublasHandle_t               cublas_      ;
-
-};
-}
-
-#endif
diff --git a/include/ui/plugins/fom_plugin.hpp b/include/ui/plugins/fom_plugin.hpp
deleted file mode 100644
index 7f12bc0b2aabeb7ed8ff6662d5e8f878fb7be7c9..0000000000000000000000000000000000000000
--- a/include/ui/plugins/fom_plugin.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef PLI_VIS_FOM_PLUGIN_HPP_
-#define PLI_VIS_FOM_PLUGIN_HPP_
-
-#include <future>
-
-#include <attributes/loggable.hpp>
-#include <ui/plugins/plugin.hpp>
-#include <ui_fom_toolbox.h>
-
-namespace pli
-{
-class vector_field;
-
-class fom_plugin : 
-  public plugin, 
-  public loggable<fom_plugin>, 
-  public Ui_fom_toolbox
-{
-public:
-  fom_plugin(QWidget* parent = nullptr);
-  void start () override;
-
-private:
-  void upload();
-
-  vector_field*     vector_field_;
-  std::future<void> future_      ;
-};
-}
-
-#endif
diff --git a/include/ui/plugins/interactor_plugin.hpp b/include/ui/plugins/interactor_plugin.hpp
deleted file mode 100644
index 54f435cd56d64cf2d84c1f1647f195081adfc28b..0000000000000000000000000000000000000000
--- a/include/ui/plugins/interactor_plugin.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef PLI_VIS_INTERACTOR_PLUGIN_HPP_
-#define PLI_VIS_INTERACTOR_PLUGIN_HPP_
-
-#include <attributes/loggable.hpp>
-#include <ui/plugins/plugin.hpp>
-#include <ui_interactor_toolbox.h>
-
-namespace pli
-{
-class interactor_plugin : 
-  public plugin, 
-  public loggable<interactor_plugin>, 
-  public Ui_interactor_toolbox
-{
-public:
-  interactor_plugin(QWidget* parent = nullptr);
-  void start () override;
-};
-}
-
-#endif
diff --git a/include/ui/plugins/plugin.hpp b/include/ui/plugins/plugin.hpp
deleted file mode 100644
index d1a610d38cf834d2bad979016825d5021e595cce..0000000000000000000000000000000000000000
--- a/include/ui/plugins/plugin.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef PLI_VIS_PLUGIN_HPP_
-#define PLI_VIS_PLUGIN_HPP_
-
-#include <QWidget>
-
-namespace pli
-{
-class window;
-
-class plugin : public QWidget
-{
-public:
-  plugin(QWidget* parent = nullptr);
-
-  void set_owner(pli::window* owner);
-  
-  virtual void awake  ();
-  virtual void start  ();
-  virtual void destroy();
-
-protected:
-  pli::window* owner_ = nullptr;
-};
-}
-
-#endif
diff --git a/include/ui/plugins/scalar_plugin.hpp b/include/ui/plugins/scalar_plugin.hpp
deleted file mode 100644
index 52451d229272a8cb1a74335a03232b4b6c9eea3d..0000000000000000000000000000000000000000
--- a/include/ui/plugins/scalar_plugin.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef PLI_VIS_SCALAR_PLUGIN_HPP_
-#define PLI_VIS_SCALAR_PLUGIN_HPP_
-
-#include <future>
-
-#include <attributes/loggable.hpp>
-#include <ui/plugins/plugin.hpp>
-#include <ui_scalar_toolbox.h>
-
-namespace pli
-{
-class scalar_field;
-
-class scalar_plugin : 
-  public plugin, 
-  public loggable<scalar_plugin>,
-  public Ui_scalar_toolbox
-{
-public:
-  scalar_plugin(QWidget* parent = nullptr);
-  void start () override;
-
-private:
-  void upload();
-
-  scalar_field*     scalar_field_;
-  std::future<void> future_      ;
-};
-}
-
-#endif
diff --git a/include/ui/plugins/selector_plugin.hpp b/include/ui/plugins/selector_plugin.hpp
deleted file mode 100644
index 75cd9ffd18bd8ddacd18e6ada35e485fdab1c0d4..0000000000000000000000000000000000000000
--- a/include/ui/plugins/selector_plugin.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef PLI_VIS_SELECTOR_HPP_
-#define PLI_VIS_SELECTOR_HPP_
-
-#include <array>
-#include <cstddef>
-
-#include <attributes/loggable.hpp>
-#include <ui/plugins/plugin.hpp>
-#include <ui_selector_toolbox.h>
-
-namespace pli
-{
-class window;
-
-class selector_plugin : public plugin, public Ui_selector_toolbox, public loggable<selector_plugin>
-{
-  Q_OBJECT
-
-public:
-  selector_plugin(QWidget* parent = nullptr);
-  
-  std::array<std::size_t, 3> selection_offset() const;
-  std::array<std::size_t, 3> selection_size  () const;
-  std::array<std::size_t, 3> selection_stride() const;
-
-  void start() override;
-
-signals:
-  void on_change(
-    const std::array<std::size_t, 3>& offset, 
-    const std::array<std::size_t, 3>& size  , 
-    const std::array<std::size_t, 3>& stride);
-  
-};
-}
-
-#endif
diff --git a/include/ui/plugins/tractography_plugin.hpp b/include/ui/plugins/tractography_plugin.hpp
deleted file mode 100644
index 397c2d22b48632116c90f117c06c6238f1848b06..0000000000000000000000000000000000000000
--- a/include/ui/plugins/tractography_plugin.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef PLI_VIS_TRACTOGRAPHY_PLUGIN_HPP_
-#define PLI_VIS_TRACTOGRAPHY_PLUGIN_HPP_
-
-#include <attributes/loggable.hpp>
-#include <ui/plugins/plugin.hpp>
-#include <ui_tractography_toolbox.h>
-
-namespace pli
-{
-class tractography_plugin : 
-  public plugin, 
-  public loggable<tractography_plugin>,
-  public Ui_tractography_toolbox
-{
-public:
-  tractography_plugin(QWidget* parent = nullptr);
-  void start () override;
-
-private:
-  void trace ();
-};
-}
-
-#endif
diff --git a/include/ui/plugins/tractography_toolbox.ui b/include/ui/plugins/tractography_toolbox.ui
deleted file mode 100644
index 61dcb6e880b2cc9f65196d70c16a671776d98df6..0000000000000000000000000000000000000000
--- a/include/ui/plugins/tractography_toolbox.ui
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>tractography_toolbox</class>
- <widget class="QWidget" name="tractography_toolbox">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>280</width>
-    <height>50</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Tractography Toolbox</string>
-  </property>
-  <layout class="QGridLayout" name="gridLayout">
-   <item row="0" column="0" colspan="2">
-    <widget class="QPushButton" name="button_trace_selection">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-       <horstretch>0</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="text">
-      <string>Trace Selection</string>
-     </property>
-    </widget>
-   </item>
-   <item row="1" column="0" colspan="2">
-    <spacer name="spacer">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>291</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/include/ui/window.hpp b/include/ui/window.hpp
deleted file mode 100644
index e7cbfc119ae9f9d16c3b1e253e13804f9ef7d1bd..0000000000000000000000000000000000000000
--- a/include/ui/window.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef PLI_VIS_WINDOW_HPP_
-#define PLI_VIS_WINDOW_HPP_
-
-#include <vector>
-
-#include <QMainWindow>
-
-#include <attributes/loggable.hpp>
-#include <ui/plugins/plugin.hpp>
-#include <ui_window.h>
-
-namespace pli
-{
-class plugin;
-
-class window : public QMainWindow, public Ui_window, public loggable<window>
-{
-public:
-   window();
-  ~window();
-
-  template<typename plugin_type>
-  plugin_type* get_plugin()
-  {
-    for (auto plugin : plugins_)
-      if (typeid(*plugin) == typeid(plugin_type))
-        return reinterpret_cast<plugin_type*>(plugin);
-    return nullptr;
-  }
-
-private:
-  void bind_actions();
-
-  std::vector<plugin*> plugins_ ;
-};
-}
-
-#endif
diff --git a/include/utility/qt_text_browser_sink.hpp b/include/utility/qt_text_browser_sink.hpp
deleted file mode 100644
index 58ea708206e8edbf9bf049cee8302a762cd17e1a..0000000000000000000000000000000000000000
--- a/include/utility/qt_text_browser_sink.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef PLI_VIS_QT_TEXT_BROWSER_SINK_HPP_
-#define PLI_VIS_QT_TEXT_BROWSER_SINK_HPP_
-
-#include <QScrollBar>
-#include <QTextBrowser>
-#include <spdlog/sinks/sink.h>
-
-namespace pli
-{
-class qt_text_browser_sink : public spdlog::sinks::sink
-{
-public:
-  qt_text_browser_sink (QTextBrowser* text_browser) : text_browser_(text_browser)
-  {
-
-  }
-  
-  void log  (const spdlog::details::log_msg& message) override
-  {
-    text_browser_->append(message.formatted.c_str());
-    auto* scroll_bar = text_browser_->verticalScrollBar();
-    scroll_bar->setValue(scroll_bar->maximum());
-  }
-  void flush() override
-  {
-
-  }
-
-private:
-  QTextBrowser* text_browser_;
-};
-}
-
-#endif
\ No newline at end of file
diff --git a/include/utility/thread_pool.hpp b/include/utility/thread_pool.hpp
deleted file mode 100644
index 21f8b8e5c7aed8b06abb6cee65f812dfa5b38eb0..0000000000000000000000000000000000000000
--- a/include/utility/thread_pool.hpp
+++ /dev/null
@@ -1,93 +0,0 @@
-#ifndef PLI_VIS_THREAD_POOL_HPP_
-#define PLI_VIS_THREAD_POOL_HPP_
-
-#include <condition_variable>
-#include <functional>
-#include <future>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <stdexcept>
-#include <thread>
-#include <vector>
-
-namespace pli
-{
-class thread_pool 
-{
-public:
-  // Constructor launches worker threads.
-   thread_pool(size_t);
-  // Destructor joins all worker threads.
-  ~thread_pool();
-
-  // Adds a new work item to the pool.
-  template<class function, class... arguments>
-  std::future<typename std::result_of<function(arguments...)>::type> enqueue(function&& func, arguments&&... args);
-  
-private:
-  // Keep track of threads in order to join them.
-  std::vector<std::thread> worker_threads_;
-  // The task queue.
-  std::queue<std::function<void()>> tasks_;
-
-  // Synchronization.
-  std::mutex queue_mutex_;
-  std::condition_variable condition_;
-  bool stop_;
-};
-
-inline thread_pool::thread_pool(size_t count) : stop_(false)
-{
-  for (size_t i = 0; i < count; ++i)
-    worker_threads_.emplace_back(
-    [this]
-    {
-      for (;;)
-      {
-        std::function<void()> task;
-        {
-          std::unique_lock<std::mutex> lock(this->queue_mutex_);
-          this->condition_.wait(lock, [this]{ return this->stop_ || !this->tasks_.empty(); });
-          if (this->stop_ && this->tasks_.empty())
-            return;
-          task = std::move(this->tasks_.front());
-          this->tasks_.pop();
-        }
-        task();
-      }
-    });
-}
-inline thread_pool::~thread_pool()
-{
-  {
-    std::unique_lock<std::mutex> lock(queue_mutex_);
-    stop_ = true;
-  }
-  condition_.notify_all();
-  for (auto& worker : worker_threads_)
-    worker.join();
-}
-
-template<class function, class... arguments>
-std::future<typename std::result_of<function(arguments...)>::type> thread_pool::enqueue(function&& func, arguments&&... args)
-{
-  using return_type = typename std::result_of<function(arguments...)>::type;
-
-  auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<function>(func), std::forward<arguments>(args)...));
-
-  std::future<return_type> result = task->get_future();
-  {
-    std::unique_lock<std::mutex> lock(queue_mutex_);
-
-    if (stop_)
-      throw std::runtime_error("Cannot enqueue on a stopped thread_pool.");
-
-    tasks_.emplace([task](){ (*task)(); });
-  }
-  condition_.notify_one();
-  return result;
-}
-}
-
-#endif
\ No newline at end of file
diff --git a/include/visualization/interactors/first_person_interactor.hpp b/include/visualization/interactors/first_person_interactor.hpp
deleted file mode 100644
index 7e312526a43b73eef644cac4b633b54991310893..0000000000000000000000000000000000000000
--- a/include/visualization/interactors/first_person_interactor.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef PLI_VIS_FIRST_PERSON_INTERACTOR_HPP_
-#define PLI_VIS_FIRST_PERSON_INTERACTOR_HPP_
-
-#include <map>
-
-#include <QPoint>
-
-class QKeyEvent;
-class QMouseEvent;
-
-namespace pli
-{
-class transform;
-
-class first_person_interactor
-{
-public:
-  first_person_interactor(transform* transform);
-
-  void update_transform();
-
-  void key_press_handler  (QKeyEvent*   event);
-  void key_release_handler(QKeyEvent*   event);
-  void mouse_press_handler(QMouseEvent* event);
-  void mouse_move_handler (QMouseEvent* event);
-
-  float move_speed() const
-  {
-    return move_speed_;
-  }
-  float look_speed() const
-  {
-    return look_speed_;
-  }
-
-  void set_move_speed(float move_speed)
-  {
-    move_speed_ = move_speed;
-  }
-  void set_look_speed(float look_speed)
-  {
-    look_speed_ = look_speed;
-  }
-
-private:
-  transform*          transform_  ;
-  float               move_speed_ = 1.0;
-  float               look_speed_ = 1.0;
-  std::map<int, bool> key_map_    ;
-
-  QPoint last_mouse_position_;
-};
-}
-
-#endif
\ No newline at end of file
diff --git a/include/visualization/interactors/orbit_interactor.hpp b/include/visualization/interactors/orbit_interactor.hpp
deleted file mode 100644
index 6e4290ddc883516984a57ca6eaff9ca3f3ad9eb1..0000000000000000000000000000000000000000
--- a/include/visualization/interactors/orbit_interactor.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef PLI_VIS_ORBIT_INTERACTOR_HPP_
-#define PLI_VIS_ORBIT_INTERACTOR_HPP_
-
-#include <QPoint>
-
-class QKeyEvent;
-class QMouseEvent;
-
-namespace pli
-{
-class transform;
-
-class orbit_interactor
-{
-public:
-  orbit_interactor(transform* transform);
-
-  void update_transform();
-
-  void key_press_handler  (QKeyEvent*   event);
-  void key_release_handler(QKeyEvent*   event);
-  void mouse_press_handler(QMouseEvent* event);
-  void mouse_move_handler (QMouseEvent* event);
-
-  float move_speed() const
-  {
-    return move_speed_;
-  }
-  float look_speed() const
-  {
-    return look_speed_;
-  }
-
-  void set_move_speed(float move_speed)
-  {
-    move_speed_ = move_speed;
-  }
-  void set_look_speed(float look_speed)
-  {
-    look_speed_ = look_speed;
-  }
-
-private:
-  transform* transform_  ;
-  float      move_speed_ = 1.0;
-  float      look_speed_ = 1.0;
-
-  QPoint last_mouse_position_;
-};
-}
-
-#endif
\ No newline at end of file
diff --git a/include/visualization/interactors/simple_interactor.hpp b/include/visualization/interactors/simple_interactor.hpp
deleted file mode 100644
index 22cec9a36a519187c4c1e0bf721b8a78ec60fcd4..0000000000000000000000000000000000000000
--- a/include/visualization/interactors/simple_interactor.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef PLI_VIS_SIMPLE_INTERACTOR_HPP_
-#define PLI_VIS_SIMPLE_INTERACTOR_HPP_
-
-#include <QPoint>
-
-class QKeyEvent;
-class QMouseEvent;
-
-namespace pli
-{
-class transform;
-
-class simple_interactor
-{
-public:
-  simple_interactor(transform* transform);
-
-  void update_transform();
-
-  void key_press_handler  (QKeyEvent*   event);
-  void key_release_handler(QKeyEvent*   event);
-  void mouse_press_handler(QMouseEvent* event);
-  void mouse_move_handler (QMouseEvent* event);
-
-  float move_speed() const
-  {
-    return move_speed_;
-  }
-  float look_speed() const
-  {
-    return look_speed_;
-  }
-
-  void set_move_speed(float move_speed)
-  {
-    move_speed_ = move_speed;
-  }
-  void set_look_speed(float look_speed)
-  {
-    look_speed_ = look_speed;
-  }
-
-private:
-  transform* transform_  ;
-  float      move_speed_ = 1.0;
-  float      look_speed_ = 1.0;
-
-  QPoint last_mouse_position_;
-};
-}
-
-#endif
\ No newline at end of file
diff --git a/include/visualization/vector_field.hpp b/include/visualization/vector_field.hpp
deleted file mode 100644
index a404a23994ef0f824810bebce1f126ee38970c35..0000000000000000000000000000000000000000
--- a/include/visualization/vector_field.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef PLI_VIS_VECTOR_FIELD_HPP_
-#define PLI_VIS_VECTOR_FIELD_HPP_
-
-#include <functional>
-#include <memory>
-#include <string>
-
-#include <vector_types.h>
-
-#include <attributes/renderable.hpp>
-#include <opengl/all.hpp>
-
-namespace pli
-{
-class vector_field : public renderable
-{
-public:
-  void initialize()                     override;
-  void render    (const camera* camera) override;
-
-  void set_data(
-    const uint3&  dimensions  ,
-    const float*  directions  ,
-    const float*  inclinations,
-    const float3& spacing     ,
-    float         scale       = 1.0,
-    std::function<void(const std::string&)> status_callback = [](const std::string&){});
-
-  void set_data(
-    const uint3&  dimensions  , 
-    const float3* unit_vectors, 
-    const float3& spacing     , 
-    float         scale       , 
-    std::function<void(const std::string&)> status_callback = [](const std::string&){});
-
-private:
-  std::unique_ptr<gl::program>      shader_program_;
-  std::unique_ptr<gl::vertex_array> vertex_array_  ;
-  std::unique_ptr<gl::array_buffer> vertex_buffer_ ;
-  std::unique_ptr<gl::array_buffer> color_buffer_  ;
-  std::size_t                       draw_count_    = 0;
-};
-}
-
-#endif
\ No newline at end of file
diff --git a/shaders/depth_pass.frag.glsl b/shaders/depth_pass.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..42dbb0f2e91fe96eaaaf615121a3664640f32659
--- /dev/null
+++ b/shaders/depth_pass.frag.glsl
@@ -0,0 +1,18 @@
+#ifndef DEPTH_PASS_FRAG_GLSL_
+#define DEPTH_PASS_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string depth_pass_frag = R"(\
+#version 400
+
+void main()
+{
+  // Intentionally empty.
+}
+)";
+}
+
+#endif
diff --git a/shaders/depth_pass.vert.glsl b/shaders/depth_pass.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..ae5e629b7ec5d8be0e8f4822b61394e31136db0e
--- /dev/null
+++ b/shaders/depth_pass.vert.glsl
@@ -0,0 +1,24 @@
+#ifndef DEPTH_PASS_VERT_GLSL_
+#define DEPTH_PASS_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string depth_pass_vert = R"(\
+#version 400
+
+uniform mat4 model     ;
+uniform mat4 view      ;
+uniform mat4 projection;
+in      vec3 vertex    ;
+
+void main()
+{
+  gl_Position = projection * view * model * vec4(vertex, 1.0);
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/lineao_color_pass.frag.glsl b/shaders/lineao_color_pass.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..a72402f9a7b5659fc5ff257ba53ef51337365464
--- /dev/null
+++ b/shaders/lineao_color_pass.frag.glsl
@@ -0,0 +1,21 @@
+#ifndef LINEAO_COLOR_PASS_FRAG_GLSL_
+#define LINEAO_COLOR_PASS_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string lineao_color_pass_frag = R"(\
+#version 400
+
+in  vec3 vert_direction;
+out vec4 frag_color    ;
+
+void main()
+{
+  frag_color = vec4(abs(vert_direction.x), abs(vert_direction.z), abs(vert_direction.y), 1.0);
+}
+)";
+}
+
+#endif
diff --git a/shaders/lineao_color_pass.vert.glsl b/shaders/lineao_color_pass.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..1fa00373b15d1d510376445749bb9c27dc4b1240
--- /dev/null
+++ b/shaders/lineao_color_pass.vert.glsl
@@ -0,0 +1,27 @@
+#ifndef LINEAO_COLOR_PASS_VERT_GLSL_
+#define LINEAO_COLOR_PASS_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string lineao_color_pass_vert = R"(\
+#version 400
+
+uniform mat4 model         ;
+uniform mat4 view          ;
+uniform mat4 projection    ;
+in      vec3 vertex        ;
+in      vec3 direction     ;
+out     vec3 vert_direction;
+
+void main()
+{
+  gl_Position    = projection * view * model * vec4(vertex, 1.0);
+  vert_direction = direction;
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/lineao_main_pass.frag.glsl b/shaders/lineao_main_pass.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..c07d1b9f0a789dd1b5eac40200da02fcfeef014f
--- /dev/null
+++ b/shaders/lineao_main_pass.frag.glsl
@@ -0,0 +1,78 @@
+#ifndef LINEAO_MAIN_PASS_FRAG_GLSL_
+#define LINEAO_MAIN_PASS_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string lineao_main_pass_frag = R"(\
+#version 420
+
+uniform uint      layer_count          = 4      ;
+uniform uint      sample_count         = 32     ;
+uniform float     radius_screen_space  = 16     ;
+uniform float     radius_0             = 1.5    ;
+uniform float     falloff_0            = 0.00001;
+uniform vec2      screen_size          ;
+uniform sampler2D normal_depth_texture ;
+uniform sampler2D color_texture        ;
+uniform sampler2D zoom_texture         ;
+uniform sampler3D random_texture       ;
+in      vec2      vert_texcoords       ;
+out     vec4      frag_color           ;
+
+void main()
+{ 
+  vec4  color        = texture(color_texture       , vert_texcoords);
+  vec4  normal_depth = texture(normal_depth_texture, vert_texcoords);
+  float zoom         = texture(zoom_texture        , vert_texcoords).x;
+  
+  float radius       = (zoom * radius_screen_space / screen_size.x) / (1.0 - normal_depth.w);
+  float radius_scale = 0.0F;
+
+  float ambient_occlusion    = 0.0;
+  uint  current_sample_count = sample_count;
+  for(uint i = 0; i < layer_count; i++)
+  {
+    radius_scale += radius_0 + i;
+    float current_ambient_occlusion = 0.0;
+    for(uint j = 0; j < current_sample_count; j++)
+    {
+      float visibility = 0.0;
+      float weight     = 0.0;
+      
+      vec3 random_vector = radius * radius_scale * normalize(texture(random_texture, vec3(vert_texcoords.xy, j)).xyz);
+      vec3 random_point  = vec3(vert_texcoords, normal_depth.w) + random_vector;
+      
+      vec4  occluder_normal_depth = texture(normal_depth_texture, random_point.xy);
+      float delta_depth           = normal_depth.w - occluder_normal_depth.w;
+      if(delta_depth < 0.0)
+        visibility = 1.0;
+
+      float falloff_layer = pow(1.0 - float(i) / float(layer_count), 2);
+      if (delta_depth > falloff_layer)
+        weight = 0.0;
+      else if (delta_depth < falloff_0)
+        weight = 1.0;
+      else
+      {
+        float x = (occluder_normal_depth.w - falloff_0) / (falloff_layer - falloff_0);
+        weight  = 1.0 - 3.0 * pow(x, 2) + 2.0 * pow(x, 3);
+      }
+
+      current_ambient_occlusion += (1.0 - visibility) * weight;
+    }
+    ambient_occlusion    += current_ambient_occlusion / current_sample_count;
+    current_sample_count /= (i + 1);
+  }
+  ambient_occlusion /= layer_count;
+
+  frag_color = ambient_occlusion * color;
+
+  if(frag_color.a == 0.0)
+    discard;
+}
+)";
+}
+
+#endif
diff --git a/shaders/lineao_main_pass.vert.glsl b/shaders/lineao_main_pass.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..b44c823a62b891fc7cd1ea0e59658da328987bda
--- /dev/null
+++ b/shaders/lineao_main_pass.vert.glsl
@@ -0,0 +1,24 @@
+#ifndef LINEAO_MAIN_PASS_VERT_GLSL_
+#define LINEAO_MAIN_PASS_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string lineao_main_pass_vert = R"(\
+#version 420
+
+in  vec3 vertex        ;
+in  vec2 texcoords     ;
+out vec2 vert_texcoords;
+
+void main()
+{
+  gl_Position    = vec4(vertex, 1.0);
+  vert_texcoords = texcoords;
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/lineao_normal_depth_pass.frag.glsl b/shaders/lineao_normal_depth_pass.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..a47040f66c138e38bc3c5a6fd9275865f6bf6961
--- /dev/null
+++ b/shaders/lineao_normal_depth_pass.frag.glsl
@@ -0,0 +1,41 @@
+#ifndef LINEAO_NORMAL_DEPTH_PASS_FRAG_GLSL_
+#define LINEAO_NORMAL_DEPTH_PASS_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string lineao_normal_depth_pass_frag = R"(\
+#version 400
+
+uniform uvec2 screen_size   ;
+uniform mat4  model         ;
+uniform mat4  view          ;
+uniform mat4  projection    ;
+in      vec3  vert_direction;
+out     vec4  color         ;
+
+vec3 get_world_position()
+{
+  vec4 normalized_device_coordinates = vec4(
+    2.0 * gl_FragCoord.x / screen_size.x - 1.0,
+    2.0 * gl_FragCoord.y / screen_size.y - 1.0,
+    2.0 * gl_FragCoord.z                 - 1.0,
+    1.0);
+  vec4 clip_coordinates  = normalized_device_coordinates / gl_FragCoord.w;
+  vec4 world_coordinates = inverse(projection * view * model) * clip_coordinates;
+  return world_coordinates.xyz;
+}
+
+void main()
+{
+  vec3 T   = normalize(vert_direction);
+  vec3 C   = normalize(inverse(view)[3].xyz - get_world_position());
+  vec3 TxC = cross(T, C);
+  vec3 N   = cross(TxC / length(TxC), T);
+  color    = vec4(N, gl_FragCoord.z * gl_FragCoord.w);
+}
+)";
+}
+
+#endif
diff --git a/shaders/lineao_normal_depth_pass.vert.glsl b/shaders/lineao_normal_depth_pass.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..fc9ea1730613dc009b06d57b6ae71816c188cdbc
--- /dev/null
+++ b/shaders/lineao_normal_depth_pass.vert.glsl
@@ -0,0 +1,27 @@
+#ifndef LINEAO_NORMAL_DEPTH_PASS_VERT_GLSL_
+#define LINEAO_NORMAL_DEPTH_PASS_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string lineao_normal_depth_pass_vert = R"(\
+#version 400
+
+uniform mat4 model         ;
+uniform mat4 view          ;
+uniform mat4 projection    ;
+in      vec3 vertex        ;
+in      vec3 direction     ;
+out     vec3 vert_direction;
+
+void main()
+{
+  gl_Position    = projection * view * model * vec4(vertex, 1.0);
+  vert_direction = direction;
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/lineao_zoom_pass.frag.glsl b/shaders/lineao_zoom_pass.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..6db0c442d9cbf54ff7b50e4b125a2d2cede15085
--- /dev/null
+++ b/shaders/lineao_zoom_pass.frag.glsl
@@ -0,0 +1,24 @@
+#ifndef LINEAO_ZOOM_PASS_FRAG_GLSL_
+#define LINEAO_ZOOM_PASS_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string lineao_zoom_pass_frag = R"(\
+#version 400
+
+uniform mat4  model     ;
+uniform mat4  view      ;
+uniform mat4  projection;
+flat in float vert_zoom ;
+out     vec4  frag_color;
+
+void main()
+{
+  frag_color = vec4(vert_zoom, vert_zoom, vert_zoom, vert_zoom);
+}
+)";
+}
+
+#endif
diff --git a/shaders/lineao_zoom_pass.vert.glsl b/shaders/lineao_zoom_pass.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..b2c991b57784290ce931c7ede41fbc1d436ff13e
--- /dev/null
+++ b/shaders/lineao_zoom_pass.vert.glsl
@@ -0,0 +1,27 @@
+#ifndef LINEAO_ZOOM_PASS_VERT_GLSL_
+#define LINEAO_ZOOM_PASS_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string lineao_zoom_pass_vert = R"(\
+#version 400
+
+uniform  mat4  model     ;
+uniform  mat4  view      ;
+uniform  mat4  projection;
+in       vec3  vertex    ;
+flat out float vert_zoom ;
+
+void main()
+{
+  vec4 position = projection * view * model * vec4(vertex, 1.0);
+  gl_Position   = position;
+  vert_zoom     = length(position);
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/odf_field.frag.glsl b/shaders/odf_field.frag.glsl
deleted file mode 100644
index c9e456dfe5d5ed42bb2c8a8431d4ab717312e941..0000000000000000000000000000000000000000
--- a/shaders/odf_field.frag.glsl
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef ODF_FIELD_FRAG_GLSL_
-#define ODF_FIELD_FRAG_GLSL_
-
-#include <string>
-
-namespace shaders
-{
-std::string odf_field_frag = R"(\
-#version 400
-
-in  vec4 vert_color;
-out vec4 frag_color;
-
-void main()
-{
-  frag_color = vert_color;
-}
-)";
-}
-
-#endif
diff --git a/shaders/odf_field.vert.glsl b/shaders/odf_field.vert.glsl
deleted file mode 100644
index 95dec71fef159a5356e0b8fd6ddb91b2cda78be3..0000000000000000000000000000000000000000
--- a/shaders/odf_field.vert.glsl
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef ODF_FIELD_VERT_GLSL_
-#define ODF_FIELD_VERT_GLSL_
-
-#include <string>
-
-namespace shaders
-{
-std::string odf_field_vert = R"(\
-#version 400
-
-uniform mat4 projection;
-uniform mat4 view      ;
-in      vec3 vertex    ;
-in      vec4 color     ;
-out     vec4 vert_color;
-
-void main()
-{
-  gl_Position = projection * view * vec4(vertex, 1.0);
-  vert_color  = color;
-}
-)";
-}
-
-
-#endif
\ No newline at end of file
diff --git a/shaders/odf_field_renderer.frag.glsl b/shaders/odf_field_renderer.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..fb16bd40b05c40aaf630eaffebea77d97770f819
--- /dev/null
+++ b/shaders/odf_field_renderer.frag.glsl
@@ -0,0 +1,78 @@
+#ifndef ODF_FIELD_RENDERER_FRAG_GLSL_
+#define ODF_FIELD_RENDERER_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string odf_field_renderer_frag = R"(\
+#version 400
+
+uniform int   color_mode     = 0    ;
+uniform float color_k        = 0.5  ;
+uniform bool  color_inverted = false;
+in      vec3  vert_direction ;
+out     vec4  frag_color     ;
+
+vec3 hue_to_rgb(float hue)
+{
+  float R = abs(hue * 6 - 3) - 1;
+  float G = 2 - abs(hue * 6 - 2);
+  float B = 2 - abs(hue * 6 - 4);
+  return clamp(vec3(R,G,B), 0, 1);
+}
+vec3 hsv_to_rgb(vec3 hsv)
+{
+  vec3 rgb = hue_to_rgb(hsv.x);
+  return ((rgb - 1.0) * hsv.y + 1.0) * hsv.z;
+}
+vec3 hsl_to_rgb(vec3 hsl)
+{
+  vec3 rgb = hue_to_rgb(hsl.x);
+  float C = (1 - abs(2 * hsl.z - 1)) * hsl.y;
+  return (rgb - 0.5) * C + hsl.z;
+}
+
+vec3 to_spherical(vec3 cartesian)
+{
+  float r = length(cartesian);
+  float t = atan  (cartesian.y , cartesian.x);
+  float p = acos  (cartesian.z / r);
+  return vec3(r, t, p);
+}
+
+vec3 map_color(vec3 direction)
+{
+  vec3 spherical = to_spherical(direction);
+
+  if(spherical.y <  0.0)            spherical.y += radians(180.0);
+  if(spherical.y >= radians(180.0)) spherical.y -= radians(180.0);
+  spherical.y = radians(180.0) - spherical.y;
+
+  if(spherical.z < 0.0)             spherical.z = abs(spherical.z);
+  if(spherical.z >= radians( 90.0)) spherical.z = radians(180.0) - spherical.z;
+
+  float t = spherical.y / radians(180.0);
+  float p = spherical.z / radians(90.0);
+  if(color_inverted)
+    p = 1.0 - p;
+
+  if(color_mode == 0)
+    return hsl_to_rgb(vec3(t, color_k, p));
+  if(color_mode == 1)
+    return hsl_to_rgb(vec3(t, p, color_k));
+  if(color_mode == 2)
+    return hsv_to_rgb(vec3(t, color_k, p));
+  if(color_mode == 3)
+    return hsv_to_rgb(vec3(t, p, color_k));
+  return vec3(abs(direction.x), abs(direction.z), abs(direction.y));
+}
+
+void main()
+{
+  frag_color = vec4(map_color(vert_direction), 1.0);
+}
+)";
+}
+
+#endif
diff --git a/shaders/odf_field_renderer.vert.glsl b/shaders/odf_field_renderer.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..d358f89e2b3fba29b5669129727f3528384f5d1f
--- /dev/null
+++ b/shaders/odf_field_renderer.vert.glsl
@@ -0,0 +1,27 @@
+#ifndef ODF_FIELD_RENDERER_VERT_GLSL_
+#define ODF_FIELD_RENDERER_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string odf_field_renderer_vert = R"(\
+#version 400
+
+uniform  mat4 model         ;
+uniform  mat4 view          ;
+uniform  mat4 projection    ;
+in       vec3 vertex        ;
+in       vec3 direction     ;
+out      vec3 vert_direction;
+
+void main()
+{
+  gl_Position    = projection * view * model * vec4(vertex, 1.0);
+  vert_direction = direction;
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/polar_plot.frag.glsl b/shaders/polar_plot.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..4b7716147c9dadccd9c097c0fdeffc6b345ffa52
--- /dev/null
+++ b/shaders/polar_plot.frag.glsl
@@ -0,0 +1,78 @@
+#ifndef POLAR_PLOT_FRAG_GLSL_
+#define POLAR_PLOT_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string polar_plot_frag = R"(\
+#version 400
+
+uniform int   color_mode     = 0    ;
+uniform float color_k        = 0.5  ;
+uniform bool  color_inverted = false;
+in      vec3  vert_direction ;
+out     vec4  frag_color     ;
+
+vec3 hue_to_rgb(float hue)
+{
+  float R = abs(hue * 6 - 3) - 1;
+  float G = 2 - abs(hue * 6 - 2);
+  float B = 2 - abs(hue * 6 - 4);
+  return clamp(vec3(R,G,B), 0, 1);
+}
+vec3 hsv_to_rgb(vec3 hsv)
+{
+  vec3 rgb = hue_to_rgb(hsv.x);
+  return ((rgb - 1.0) * hsv.y + 1.0) * hsv.z;
+}
+vec3 hsl_to_rgb(vec3 hsl)
+{
+  vec3 rgb = hue_to_rgb(hsl.x);
+  float C = (1 - abs(2 * hsl.z - 1)) * hsl.y;
+  return (rgb - 0.5) * C + hsl.z;
+}
+
+vec3 to_spherical(vec3 cartesian)
+{
+  float r = length(cartesian);
+  float t = atan  (cartesian.y , cartesian.x);
+  float p = acos  (cartesian.z / r);
+  return vec3(r, t, p);
+}
+
+vec3 map_color(vec3 direction)
+{
+  vec3 spherical = to_spherical(direction);
+
+  if(spherical.y <  0.0)            spherical.y += radians(180.0);
+  if(spherical.y >= radians(180.0)) spherical.y -= radians(180.0);
+  spherical.y = radians(180.0) - spherical.y;
+
+  if(spherical.z < 0.0)             spherical.z = abs(spherical.z);
+  if(spherical.z >= radians( 90.0)) spherical.z = radians(180.0) - spherical.z;
+
+  float t = spherical.y / radians(180.0);
+  float p = spherical.z / radians(90.0);
+  if(color_inverted)
+    p = 1.0 - p;
+
+  if(color_mode == 0)
+    return hsl_to_rgb(vec3(t, color_k, p));
+  if(color_mode == 1)
+    return hsl_to_rgb(vec3(t, p, color_k));
+  if(color_mode == 2)
+    return hsv_to_rgb(vec3(t, color_k, p));
+  if(color_mode == 3)
+    return hsv_to_rgb(vec3(t, p, color_k));
+  return vec3(abs(direction.x), abs(direction.z), abs(direction.y));
+}
+
+void main()
+{
+  frag_color = vec4(map_color(vert_direction), 1.0);
+}
+)";
+}
+
+#endif
diff --git a/shaders/polar_plot.vert.glsl b/shaders/polar_plot.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..d1b9da1be6db59b7041b1826223b878c35bd5817
--- /dev/null
+++ b/shaders/polar_plot.vert.glsl
@@ -0,0 +1,26 @@
+#ifndef POLAR_PLOT_VERT_GLSL_
+#define POLAR_PLOT_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string polar_plot_vert = R"(\
+#version 400
+
+uniform  mat4 model         ;
+uniform  mat4 view          ;
+uniform  mat4 projection    ;
+in       vec3 vertex        ;
+in       vec3 direction     ;
+out      vec3 vert_direction;
+
+void main()
+{
+  gl_Position    = projection * view * model * vec4(vertex, 1.0);
+  vert_direction = direction;
+}
+)";
+}
+
+#endif
\ No newline at end of file
diff --git a/shaders/vector_field.frag.glsl b/shaders/simple_color.frag.glsl
similarity index 58%
rename from shaders/vector_field.frag.glsl
rename to shaders/simple_color.frag.glsl
index aa818a25500bfe5b60d2c03956a707f71394d421..a2af5958f4e73b2eebc3b2b092f6e59cc1af7496 100644
--- a/shaders/vector_field.frag.glsl
+++ b/shaders/simple_color.frag.glsl
@@ -1,11 +1,11 @@
-#ifndef VECTOR_FIELD_FRAG_GLSL_
-#define VECTOR_FIELD_FRAG_GLSL_
+#ifndef SIMPLE_COLOR_FRAG_GLSL_
+#define SIMPLE_COLOR_FRAG_GLSL_
 
 #include <string>
 
 namespace shaders
 {
-std::string vector_field_frag = R"(\
+static std::string simple_color_frag = R"(\
 #version 400
 
 in  vec4 vert_color;
diff --git a/shaders/vector_field.vert.glsl b/shaders/simple_color.vert.glsl
similarity index 54%
rename from shaders/vector_field.vert.glsl
rename to shaders/simple_color.vert.glsl
index 27f01164904d8514e91888f05c0848d14cac00dc..8b3cc54a0b5c90e019c348456ade77f1d14c0b83 100644
--- a/shaders/vector_field.vert.glsl
+++ b/shaders/simple_color.vert.glsl
@@ -1,22 +1,23 @@
-#ifndef VECTOR_FIELD_VERT_GLSL_
-#define VECTOR_FIELD_VERT_GLSL_
+#ifndef SIMPLE_COLOR_VERT_GLSL_
+#define SIMPLE_COLOR_VERT_GLSL_
 
 #include <string>
 
 namespace shaders
 {
-std::string vector_field_vert = R"(\
+static std::string simple_color_vert = R"(\
 #version 400
 
-uniform mat4 projection;
+uniform mat4 model     ;
 uniform mat4 view      ;
+uniform mat4 projection;
 in      vec3 vertex    ;
 in      vec4 color     ;
 out     vec4 vert_color;
 
 void main()
 {
-  gl_Position = projection * view * vec4(vertex, 1.0);
+  gl_Position = projection * view * model * vec4(vertex, 1.0);
   vert_color  = color;
 }
 )";
diff --git a/shaders/simple_color_texture.frag.glsl b/shaders/simple_color_texture.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..0c9b376d230a5a8eeabe4e102bae10edf7a1f3a2
--- /dev/null
+++ b/shaders/simple_color_texture.frag.glsl
@@ -0,0 +1,24 @@
+#ifndef SIMPLE_COLOR_TEXTURE_FRAG_GLSL_
+#define SIMPLE_COLOR_TEXTURE_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string simple_color_texture_frag = R"(\
+#version 400
+
+uniform sampler2D texture_unit  ;
+in      vec2      vert_texcoords;
+out     vec4      frag_color    ;
+
+void main()
+{
+  vec4 color = texture(texture_unit, vert_texcoords);
+  if(color.a == 0.0f) discard;
+  frag_color = color;
+}
+)";
+}
+
+#endif
diff --git a/shaders/simple_color_texture.vert.glsl b/shaders/simple_color_texture.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..41cb3ec16887b41775ff5d4f28b05345d63f64ae
--- /dev/null
+++ b/shaders/simple_color_texture.vert.glsl
@@ -0,0 +1,27 @@
+#ifndef SIMPLE_COLOR_TEXTURE_VERT_GLSL_
+#define SIMPLE_COLOR_TEXTURE_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string simple_color_texture_vert = R"(\
+#version 400
+
+uniform mat4 model         ;
+uniform mat4 view          ;
+uniform mat4 projection    ;
+uniform vec2 size          ;
+in      vec3 position      ;
+in      vec2 texcoords     ;
+out     vec2 vert_texcoords;
+
+void main()
+{
+  gl_Position    = projection * view * model * vec4(2.0f * (position.x - 0.5f) * size.x - 0.5f, 2.0f * (position.y - 0.5f) * size.y - 0.5f, position.z, 1.0);
+  vert_texcoords = texcoords;
+}
+)";
+}
+
+#endif
diff --git a/shaders/scalar_field.frag.glsl b/shaders/simple_texture.frag.glsl
similarity index 72%
rename from shaders/scalar_field.frag.glsl
rename to shaders/simple_texture.frag.glsl
index 6491967937feef6d6bc123304e29d0a310551993..bb31df11ad293230f8cb880bcf1375bb9da0da3c 100644
--- a/shaders/scalar_field.frag.glsl
+++ b/shaders/simple_texture.frag.glsl
@@ -1,11 +1,11 @@
-#ifndef SCALAR_FIELD_FRAG_GLSL_
-#define SCALAR_FIELD_FRAG_GLSL_
+#ifndef SIMPLE_TEXTURE_FRAG_GLSL_
+#define SIMPLE_TEXTURE_FRAG_GLSL_
 
 #include <string>
 
 namespace shaders
 {
-  std::string scalar_field_frag = R"(\
+static std::string simple_texture_frag = R"(\
 #version 400
 
 uniform sampler2D texture_unit  ;
diff --git a/shaders/scalar_field.vert.glsl b/shaders/simple_texture.vert.glsl
similarity index 55%
rename from shaders/scalar_field.vert.glsl
rename to shaders/simple_texture.vert.glsl
index bcd6c45da0e5ff293a7920e8fa76dcd696c24cf8..f346049e28362fda6a0844ce9a04ba2a93869e91 100644
--- a/shaders/scalar_field.vert.glsl
+++ b/shaders/simple_texture.vert.glsl
@@ -1,22 +1,23 @@
-#ifndef SCALAR_FIELD_VERT_GLSL_
-#define SCALAR_FIELD_VERT_GLSL_
+#ifndef SIMPLE_TEXTURE_VERT_GLSL_
+#define SIMPLE_TEXTURE_VERT_GLSL_
 
 #include <string>
 
 namespace shaders
 {
-std::string scalar_field_vert = R"(\
+static std::string simple_texture_vert = R"(\
 #version 400
 
-uniform mat4 projection    ;
+uniform mat4 model         ;
 uniform mat4 view          ;
+uniform mat4 projection    ;
 in      vec3 vertex        ;
 in      vec2 texcoords     ;
 out     vec2 vert_texcoords;
 
 void main()
 {
-  gl_Position    = projection * view * vec4(vertex, 1.0);
+  gl_Position    = projection * view * model * vec4(vertex, 1.0);
   vert_texcoords = texcoords;
 }
 )";
diff --git a/shaders/streamline_renderer.frag.glsl b/shaders/streamline_renderer.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..5ea450fa16b5bdb433919b7e64dd4ef6406996be
--- /dev/null
+++ b/shaders/streamline_renderer.frag.glsl
@@ -0,0 +1,78 @@
+#ifndef STREAMLINE_RENDERER_FRAG_GLSL_
+#define STREAMLINE_RENDERER_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string streamline_renderer_frag = R"(\
+#version 400
+
+uniform int   color_mode     = 0    ;
+uniform float color_k        = 0.5  ;
+uniform bool  color_inverted = false;
+flat in vec3  vert_direction ;
+out     vec4  frag_color     ;
+
+vec3 hue_to_rgb(float hue)
+{
+  float R = abs(hue * 6 - 3) - 1;
+  float G = 2 - abs(hue * 6 - 2);
+  float B = 2 - abs(hue * 6 - 4);
+  return clamp(vec3(R,G,B), 0, 1);
+}
+vec3 hsv_to_rgb(vec3 hsv)
+{
+  vec3 rgb = hue_to_rgb(hsv.x);
+  return ((rgb - 1.0) * hsv.y + 1.0) * hsv.z;
+}
+vec3 hsl_to_rgb(vec3 hsl)
+{
+  vec3 rgb = hue_to_rgb(hsl.x);
+  float C = (1 - abs(2 * hsl.z - 1)) * hsl.y;
+  return (rgb - 0.5) * C + hsl.z;
+}
+
+vec3 to_spherical(vec3 cartesian)
+{
+  float r = length(cartesian);
+  float t = atan  (cartesian.y , cartesian.x);
+  float p = acos  (cartesian.z / r);
+  return vec3(r, t, p);
+}
+
+vec3 map_color(vec3 direction)
+{
+  vec3 spherical = to_spherical(direction);
+
+  if(spherical.y <  0.0)            spherical.y += radians(180.0);
+  if(spherical.y >= radians(180.0)) spherical.y -= radians(180.0);
+  spherical.y = radians(180.0) - spherical.y;
+
+  if(spherical.z < 0.0)             spherical.z = abs(spherical.z);
+  if(spherical.z >= radians( 90.0)) spherical.z = radians(180.0) - spherical.z;
+
+  float t = spherical.y / radians(180.0);
+  float p = spherical.z / radians(90.0);
+  if(color_inverted)
+    p = 1.0 - p;
+
+  if(color_mode == 0)
+    return hsl_to_rgb(vec3(t, color_k, p));
+  if(color_mode == 1)
+    return hsl_to_rgb(vec3(t, p, color_k));
+  if(color_mode == 2)
+    return hsv_to_rgb(vec3(t, color_k, p));
+  if(color_mode == 3)
+    return hsv_to_rgb(vec3(t, p, color_k));
+  return vec3(abs(direction.x), abs(direction.z), abs(direction.y));
+}
+
+void main()
+{
+  frag_color = vec4(map_color(vert_direction), 1.0);
+}
+)";
+}
+
+#endif
diff --git a/shaders/streamline_renderer.vert.glsl b/shaders/streamline_renderer.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..89928c75d5c6e147367a92bf313654dfee97bc5b
--- /dev/null
+++ b/shaders/streamline_renderer.vert.glsl
@@ -0,0 +1,27 @@
+#ifndef STREAMLINE_RENDERER_VERT_GLSL_
+#define STREAMLINE_RENDERER_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string streamline_renderer_vert = R"(\
+#version 400
+
+uniform  mat4 model         ;
+uniform  mat4 view          ;
+uniform  mat4 projection    ;
+in       vec3 vertex        ;
+in       vec3 direction     ;
+flat out vec3 vert_direction;
+
+void main()
+{
+  gl_Position    = projection * view * model * vec4(vertex, 1.0);
+  vert_direction = direction;
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/view_dependent.frag.glsl b/shaders/view_dependent.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..9f71874709dae7838d465bfef7e411117694fa3e
--- /dev/null
+++ b/shaders/view_dependent.frag.glsl
@@ -0,0 +1,35 @@
+#ifndef VIEW_DEPENDENT_FRAG_GLSL_
+#define VIEW_DEPENDENT_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string view_dependent_frag = R"(\
+#version 400
+
+uniform bool  view_dependent = true;
+uniform bool  invert         = true;
+uniform float rate_of_decay  = 1.0 ;
+uniform float cutoff         = 0.25;
+uniform mat4  view           ;
+in      vec3  vert_direction ;
+out     vec4  frag_color     ;
+
+void main()
+{
+  frag_color = vec4(abs(vert_direction.x), abs(vert_direction.z), abs(vert_direction.y), 1.0);
+  if(view_dependent)
+  {
+    float alpha = abs(dot(normalize(inverse(view)[2].xyz), normalize(vert_direction)));
+    if(invert)
+      alpha = 1.0 - alpha;
+    if(alpha < cutoff)
+      discard;
+    frag_color.a = pow(alpha, rate_of_decay);
+  }
+}
+)";
+}
+
+#endif
diff --git a/shaders/view_dependent.vert.glsl b/shaders/view_dependent.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..686ceb2421dc60516abf2a6058efa73b8d16748e
--- /dev/null
+++ b/shaders/view_dependent.vert.glsl
@@ -0,0 +1,27 @@
+#ifndef VIEW_DEPENDENT_VERT_GLSL_
+#define VIEW_DEPENDENT_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string view_dependent_vert = R"(\
+#version 400
+
+uniform mat4 model         ;
+uniform mat4 view          ;
+uniform mat4 projection    ;
+in      vec3 vertex        ;
+in      vec3 direction     ;
+out     vec3 vert_direction;
+
+void main()
+{
+  gl_Position    = projection * view * model * vec4(vertex, 1.0);
+  vert_direction = direction;
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/view_dependent_vector_field.frag.glsl b/shaders/view_dependent_vector_field.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..c8227fc436e1d4f9ef3cb34f2f6ba25b3cfa69be
--- /dev/null
+++ b/shaders/view_dependent_vector_field.frag.glsl
@@ -0,0 +1,96 @@
+#ifndef VIEW_DEPENDENT_FRAG_GLSL_
+#define VIEW_DEPENDENT_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string view_dependent_vector_field_frag = R"(\
+#version 450
+
+uniform int   color_mode     = 0    ;
+uniform float color_k        = 0.5  ;
+uniform bool  color_inverted = false;
+uniform bool  view_dependent = false;
+uniform bool  invert         = true ;
+uniform float rate_of_decay  = 1.0  ;
+uniform float cutoff         = 0.25 ;
+uniform mat4  view           ;
+out     vec4  frag_color     ;
+
+in vertex_data {
+  flat vec3 direction;
+} fs_in;
+
+vec3 hue_to_rgb(float hue)
+{
+  float R = abs(hue * 6 - 3) - 1;
+  float G = 2 - abs(hue * 6 - 2);
+  float B = 2 - abs(hue * 6 - 4);
+  return clamp(vec3(R,G,B), 0, 1);
+}
+vec3 hsv_to_rgb(vec3 hsv)
+{
+  vec3 rgb = hue_to_rgb(hsv.x);
+  return ((rgb - 1.0) * hsv.y + 1.0) * hsv.z;
+}
+vec3 hsl_to_rgb(vec3 hsl)
+{
+  vec3 rgb = hue_to_rgb(hsl.x);
+  float C = (1 - abs(2 * hsl.z - 1)) * hsl.y;
+  return (rgb - 0.5) * C + hsl.z;
+}
+
+vec3 to_spherical(vec3 cartesian)
+{
+  float r = length(cartesian);
+  float t = atan  (cartesian.y , cartesian.x);
+  float p = acos  (cartesian.z / r);
+  return vec3(r, t, p);
+}
+
+vec3 map_color(vec3 direction)
+{
+  vec3 spherical = to_spherical(direction);
+
+  if(spherical.y <  0.0)            spherical.y += radians(180.0);
+  if(spherical.y >= radians(180.0)) spherical.y -= radians(180.0);
+  spherical.y = radians(180.0) - spherical.y;
+
+  if(spherical.z < 0.0)             spherical.z = abs(spherical.z);
+  if(spherical.z >= radians( 90.0)) spherical.z = radians(180.0) - spherical.z;
+
+  float t = spherical.y / radians(180.0);
+  float p = spherical.z / radians(90.0);
+  if(color_inverted)
+    p = 1.0 - p;
+
+  if(color_mode == 0)
+    return hsl_to_rgb(vec3(t, color_k, p));
+  if(color_mode == 1)
+    return hsl_to_rgb(vec3(t, p, color_k));
+  if(color_mode == 2)
+    return hsv_to_rgb(vec3(t, color_k, p));
+  if(color_mode == 3)
+    return hsv_to_rgb(vec3(t, p, color_k));
+  return vec3(abs(direction.x), abs(direction.z), abs(direction.y));
+}
+
+void main()
+{
+  frag_color = vec4(map_color(fs_in.direction), 1.0);
+
+  if(view_dependent)
+  {
+    float alpha = abs(dot(normalize(inverse(view)[2].xyz), normalize(fs_in.direction)));
+    if(invert)
+      alpha = 1.0 - alpha;
+    if(alpha < cutoff)
+      discard;
+    frag_color.a = pow(alpha, rate_of_decay);
+  }
+}
+)";
+}
+
+#endif
diff --git a/shaders/view_dependent_vector_field.geom.glsl b/shaders/view_dependent_vector_field.geom.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..7ff341bd71895bfcbd5372c6ce0a2dabf4e1f395
--- /dev/null
+++ b/shaders/view_dependent_vector_field.geom.glsl
@@ -0,0 +1,48 @@
+#ifndef VIEW_DEPENDENT_GEOM_GLSL_
+#define VIEW_DEPENDENT_GEOM_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string view_dependent_vector_field_geom = R"(\
+#version 450
+
+layout (points) in;
+layout (line_strip, max_vertices = 2) out;
+
+uniform uint  vectors_per_point = 1;
+uniform float scale             = 1.0;
+uniform uvec3 dimensions        ;
+uniform mat4  model             ;
+uniform mat4  view              ;
+uniform mat4  projection        ;
+
+in  vertex_data {
+  vec3 direction;
+} gs_in[];
+ 
+out vertex_data {
+  flat vec3 direction;
+} gs_out;
+
+void main()
+{
+  mat4 mvp = projection * view * model;
+
+  uvec3 position;
+  uint index = gl_PrimitiveIDIn;
+  uint t     = index % vectors_per_point;
+  uint z     = ((index - t) / vectors_per_point) % dimensions.z;
+  uint y     = ((index - z * vectors_per_point - t) / (vectors_per_point * dimensions.z)) % dimensions.y;
+  uint x     = ((index - y * dimensions.z * vectors_per_point - z * vectors_per_point - t) / (vectors_per_point * dimensions.z * dimensions.y));
+
+  vec4 direction   = vec4(gs_in[0].direction * 0.5 * scale, 0.0);
+  gs_out.direction =  gs_in[0].direction; gl_Position = mvp * (vec4(x, y, z, 1.0) + direction); EmitVertex();
+  gs_out.direction = -gs_in[0].direction; gl_Position = mvp * (vec4(x, y, z, 1.0) - direction); EmitVertex();
+  EndPrimitive();
+}
+)";
+}
+
+#endif
diff --git a/shaders/view_dependent_vector_field.vert.glsl b/shaders/view_dependent_vector_field.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..6118f0b54c3eee25c58a9e2f3fcf00efee9e731a
--- /dev/null
+++ b/shaders/view_dependent_vector_field.vert.glsl
@@ -0,0 +1,27 @@
+#ifndef VIEW_DEPENDENT_VERT_GLSL_
+#define VIEW_DEPENDENT_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string view_dependent_vector_field_vert = R"(\
+#version 450
+
+in vec3 direction;
+
+out vertex_data 
+{
+  vec3 direction;
+} vs_out;
+
+void main()
+{
+  gl_Position      = vec4(0.0);
+  vs_out.direction = direction;
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/volume_renderer.frag.glsl b/shaders/volume_renderer.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..4ab96f2d38a891d467954b571f27b6ce7bf11b9c
--- /dev/null
+++ b/shaders/volume_renderer.frag.glsl
@@ -0,0 +1,77 @@
+#ifndef VOLUME_RENDERER_FRAG_GLSL_
+#define VOLUME_RENDERER_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+std::string volume_renderer_frag = R"(\
+#version 400
+
+const   uint      iterations       = 10000u;
+uniform vec4      background_color = vec4(0.0, 0.0, 0.0, 0.0);
+uniform float     step_size        = 0.001;
+uniform uvec2     screen_size      ;
+uniform sampler1D transfer_function;
+uniform sampler2D exit_points      ;
+uniform sampler3D volume           ;
+in      vec3      vert_color       ; // Entry point.
+out     vec4      frag_color       ;
+
+void main()
+{
+  vec3 exit_point = texture(exit_points, vec2(gl_FragCoord.x / screen_size.x, gl_FragCoord.y / screen_size.y)).xyz;
+
+  if(vert_color == exit_point)
+    discard;
+
+  vec3  ray          = (exit_point - vert_color).xyz;
+  float ray_length   = length(ray);
+  
+  vec3  delta        = normalize(ray) * step_size;
+  float delta_length = length(delta);
+
+  vec3  current_step = vert_color.xyz;
+  float current_length;
+
+  float volume_sample;
+  vec4  transfer_function_sample;
+
+  for(uint i = 0; i < iterations; i++)
+  {
+    volume_sample            = texture(volume, current_step).x;
+    transfer_function_sample = texture(transfer_function, volume_sample);
+
+    // Calculate contribution.
+    if(transfer_function_sample.a > 0.0)
+    {
+      transfer_function_sample.a = 1.0 - pow(1.0 - transfer_function_sample.a, step_size);
+      frag_color.rgb += (1.0 - frag_color.a) * transfer_function_sample.rgb * transfer_function_sample.a;
+      frag_color.a   += (1.0 - frag_color.a) * transfer_function_sample.a;
+    }
+  
+    current_step   += delta;
+    current_length += delta_length;
+  
+    // Early termination.
+    if(current_length >= ray_length)
+    {
+      frag_color.rgb = frag_color.a * frag_color.rgb + (1.0 - frag_color.a) * background_color.rgb;
+      break;
+    }
+    else if(frag_color.a > 1.0)
+    {
+      frag_color.a = 1.0;
+      break;
+    }
+  }
+
+  //frag_color = vec4(vert_color, 1.0);
+  //frag_color = vec4(exit_point, 1.0);
+  //frag_color = vec4(normalize(ray), 1.0);
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/volume_renderer.vert.glsl b/shaders/volume_renderer.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..5130b47ba3f372889079daf092679e2921fa1318
--- /dev/null
+++ b/shaders/volume_renderer.vert.glsl
@@ -0,0 +1,26 @@
+#ifndef VOLUME_RENDERER_VERT_GLSL_
+#define VOLUME_RENDERER_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+std::string volume_renderer_vert = R"(\
+#version 400
+
+uniform mat4 model     ;
+uniform mat4 view      ;
+uniform mat4 projection;
+in      vec3 vertex    ;
+out     vec3 vert_color;
+
+void main()
+{
+  vert_color  = vertex;
+  gl_Position = projection * view * model * vec4(vertex, 1.0);
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/volume_renderer_prepass.frag.glsl b/shaders/volume_renderer_prepass.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..3f05b70ae7acc809599603d7585e98bb730f4f8f
--- /dev/null
+++ b/shaders/volume_renderer_prepass.frag.glsl
@@ -0,0 +1,22 @@
+#ifndef VOLUME_RENDERER_PREPASS_FRAG_GLSL_
+#define VOLUME_RENDERER_PREPASS_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+std::string volume_renderer_prepass_frag = R"(\
+#version 400
+
+                      in  vec3 vert_color;
+layout (location = 0) out vec4 frag_color;
+
+void main()
+{
+  frag_color = vec4(vert_color, 1.0);
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/volume_renderer_prepass.vert.glsl b/shaders/volume_renderer_prepass.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..8fe8229857a823dffaa1f785fbb19c66c77cc2d1
--- /dev/null
+++ b/shaders/volume_renderer_prepass.vert.glsl
@@ -0,0 +1,26 @@
+#ifndef VOLUME_RENDERER_PREPASS_VERT_GLSL_
+#define VOLUME_RENDERER_PREPASS_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+std::string volume_renderer_prepass_vert = R"(\
+#version 400
+
+uniform mat4 model     ;
+uniform mat4 view      ;
+uniform mat4 projection;
+in      vec3 vertex    ;
+out     vec3 vert_color;
+
+void main()
+{
+  vert_color  = vertex;
+  gl_Position = projection * view * model * vec4(vertex, 1.0);
+}
+)";
+}
+
+
+#endif
\ No newline at end of file
diff --git a/shaders/zernike.frag.glsl b/shaders/zernike.frag.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..cae7e41f9cc2116e945e49946c283a687d4579c5
--- /dev/null
+++ b/shaders/zernike.frag.glsl
@@ -0,0 +1,117 @@
+#ifndef ZERNIKE_FRAG_GLSL_
+#define ZERNIKE_FRAG_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string zernike_frag = R"(\
+#version 450
+#extension GL_ARB_explicit_attrib_location : enable
+
+const float pi       = 3.1415926535897932384626433832795;
+const float infinity = 1.0 / 0.0;
+
+in vertex_data 
+{
+  vec3      relative_position;
+  flat uint offset;
+} fs_in;
+
+layout(std430, binding=0) buffer Coefficients 
+{
+  float coefficients[];
+};
+uniform uint  coefficients_per_voxel;
+uniform int   color_mode     = 0    ;
+uniform float color_k        = 0.5  ;
+uniform bool  color_inverted = false;
+
+layout(location = 0) out vec4 color;
+
+vec3 hue_to_rgb(float hue)
+{
+  float R = abs(hue * 6 - 3) - 1;
+  float G = 2 - abs(hue * 6 - 2);
+  float B = 2 - abs(hue * 6 - 4);
+  return clamp(vec3(R,G,B), 0, 1);
+}
+vec3 hsv_to_rgb(vec3 hsv)
+{
+  vec3 rgb = hue_to_rgb(hsv.x);
+  return ((rgb - 1.0) * hsv.y + 1.0) * hsv.z;
+}
+vec3 hsl_to_rgb(vec3 hsl)
+{
+  vec3 rgb = hue_to_rgb(hsl.x);
+  float C = (1 - abs(2 * hsl.z - 1)) * hsl.y;
+  return (rgb - 0.5) * C + hsl.z;
+}
+vec3 map_color(vec2 radial, float scalar)
+{
+  if(radial.y <  0.0)            radial.y += radians(180.0);
+  if(radial.y >= radians(180.0)) radial.y -= radians(180.0);
+  radial.y = radians(180.0) - radial.y;
+
+  float t = radial.y / radians(180.0);
+  if(color_inverted)
+    scalar = 1.0 - scalar;
+
+  if(color_mode == 0)
+    return hsl_to_rgb(vec3(t, color_k, scalar));
+  if(color_mode == 1)
+    return hsl_to_rgb(vec3(t, scalar, color_k));
+  if(color_mode == 2)
+    return hsv_to_rgb(vec3(t, color_k, scalar));
+  if(color_mode == 3)
+    return hsv_to_rgb(vec3(t, scalar, color_k));
+  return vec3(scalar, scalar, scalar);
+}
+
+vec2 to_radial(vec2 cartesian)
+{
+  return vec2(length(cartesian), atan(cartesian.y, cartesian.x));
+}
+ivec2 quantum_index(int index)
+{
+  ivec2 nm;
+  nm.x = int(ceil((-3.0 + sqrt(float(9 + 8 * index))) / 2.0));
+  nm.y = 2 * index - nm.x * (nm.x + 2);
+  return nm;
+}
+float factorial(int n)
+{
+  float result = 1.0;
+  for(int i = 2; i <= n; i++)
+    result *= float(i);
+  return result;
+}
+float mode(ivec2 nm, float rho)
+{
+  float result = 0.0;
+  for(int i = 0; i <= (nm.x - nm.y) / 2; i++)
+    result += pow(rho, float(nm.x - 2 * i)) * ((mod(i, 2) == 0 ? 1.0 : -1.0) * factorial(nm.x - i)) / (factorial(i) * factorial((nm.x + nm.y) / 2 - i) * factorial((nm.x - nm.y) / 2 - i));
+  return result;
+}
+float evaluate(ivec2 nm, vec2 rt)
+{
+  return (nm.y >= 0 ? 1.0f : -1.0f) * sqrt((2.0f * float(nm.x) + 2.0f) / (1.0f + (nm.y == 0 ? 1.0f : 0.0f))) * mode(ivec2(abs(nm.x), abs(nm.y)), rt.x) * (nm.y >= 0 ? cos(float(nm.y) * rt.y) : sin(float(-nm.y) * rt.y));
+}
+
+void main()
+{
+  int  coefficient_offset = int(fs_in.offset * coefficients_per_voxel);
+  vec2 radial             = to_radial(2.0 * (fs_in.relative_position.xy - vec2(0.5, 0.5)));
+  if  (radial.x >= 1.0) discard;
+  radial.y += pi;
+
+  float scalar = 0.0;
+  for(int i = 0; i < int(coefficients_per_voxel); i++)
+    scalar += coefficients[coefficient_offset + i] * evaluate(quantum_index(i), radial);
+  
+  color = vec4(map_color(radial, abs(scalar)), 1.0);
+}
+)";
+}
+
+#endif
diff --git a/shaders/zernike.vert.glsl b/shaders/zernike.vert.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..c7fa96990a6e721d6a128b414f5a25f12608c6fc
--- /dev/null
+++ b/shaders/zernike.vert.glsl
@@ -0,0 +1,37 @@
+#ifndef ZERNIKE_VERT_GLSL_
+#define ZERNIKE_VERT_GLSL_
+
+#include <string>
+
+namespace shaders
+{
+static std::string zernike_vert = R"(\
+#version 450
+#extension GL_ARB_explicit_attrib_location : enable
+
+layout(location = 0) in vec3 position;
+
+uniform uvec2 dimensions;
+uniform uvec2 spacing   ;
+
+layout(location = 0) out vertex_data 
+{
+  vec3      relative_position;
+  flat uint offset;
+} vs_out;
+
+void main()
+{
+  uvec2 size        = dimensions * spacing;
+  uvec2 location    = uvec2(gl_InstanceID / dimensions.x % dimensions.y, gl_InstanceID % dimensions.x);
+  vec3  translation = vec3(location.x * spacing.x, location.y * spacing.y, 0.0);
+  vec3  scale       = vec3(             spacing.x,              spacing.y, 1.0);
+
+  gl_Position              = vec4(position * scale + translation, 1.0) / vec4(size.x, size.y, 1.0, 1.0);
+  vs_out.relative_position = position;
+  vs_out.offset            = gl_InstanceID;
+}
+)";
+}
+
+#endif
\ No newline at end of file
diff --git a/source/cuda/odf_field.cu b/source/cuda/odf_field.cu
index ed784b3ac0f616bc9b8e99940a6d50c796da1b3c..8002defe85e3bf7eb1f6015196d4ee1e36c5f768 100644
--- a/source/cuda/odf_field.cu
+++ b/source/cuda/odf_field.cu
@@ -1,304 +1,212 @@
-#include /* implements */ <cuda/odf_field.h>
+#include <pli_vis/cuda/odf_field.h>
 
 #include <chrono>
+#include <math.h>
 #include <string>
 
+#include <cuda_runtime.h>
+#include <cublas_v2.h>
 #include <cusolverDn.h>
+#include <device_launch_parameters.h>
 #include <thrust/device_vector.h>
 #include <thrust/extrema.h>
 
-#include <sh/cush.h>
-#include <sh/vector_ops.h>
-
-#include <cuda/spherical_histogram.h>
-#include <sh/launch.h>
-#include <sh/spherical_harmonics.h>
+#include <pli_vis/cuda/sh/launch.h>
+#include <pli_vis/cuda/sh/spherical_harmonics.h>
+#include <pli_vis/cuda/utility/convert.h>
+#include <pli_vis/cuda/utility/vector_ops.h>
+#include <pli_vis/cuda/spherical_histogram.h>
 
 namespace pli
 {
-void calculate_odfs(
-  cublasHandle_t     cublas        ,
-  cusolverDnHandle_t cusolver      ,
-  const uint3&       dimensions    , 
-  const uint3&       vectors_size  , 
-  const uint2&       histogram_bins,
-  const unsigned     maximum_degree, 
-  const float*       directions    , 
-  const float*       inclinations  , 
-        float*       coefficients  ,
-        std::function<void(const std::string&)> status_callback)
+// Call on a dimensions.x x dimensions.y x dimensions.z 3D grid.
+// Vectors are in spherical coordinates.
+template<typename vector_type, typename scalar_type>
+__global__ void quantify_and_project_kernel(
+  // Input related parameters.
+        uint3          dimensions         ,
+        uint3          vectors_dimensions ,
+  // Quantification related parameters. 
+  const vector_type*   vectors            , 
+        unsigned       histogram_bin_count,
+        vector_type*   histogram_vectors  ,
+  // Projection related parameters.     
+        unsigned       maximum_degree     ,
+  const scalar_type*   inverse_transform  ,
+        scalar_type*   coefficient_vectors)
 {
-  auto total_start = std::chrono::system_clock::now();
-
-  status_callback("Allocating and copying vectors.");
-  auto voxel_count        = dimensions  .x * dimensions  .y * dimensions  .z;
-  auto vector_count       = vectors_size.x * vectors_size.y * vectors_size.z;
-  auto total_vector_count = voxel_count * vector_count;
-  thrust::device_vector<float> longitudes(total_vector_count);
-  thrust::device_vector<float> latitudes (total_vector_count);
-  copy_n(directions  , total_vector_count, longitudes.begin());
-  copy_n(inclinations, total_vector_count, latitudes .begin());
-  auto longitudes_ptr = raw_pointer_cast(&longitudes[0]);
-  auto latitudes_ptr  = raw_pointer_cast(&latitudes [0]);
-  transform(longitudes.begin(), longitudes.end(), longitudes.begin(), (90.0 + thrust::placeholders::_1) * M_PI / 180.0);
-  transform(latitudes .begin(), latitudes .end(), latitudes .begin(), (90.0 - thrust::placeholders::_1) * M_PI / 180.0);
-  cudaDeviceSynchronize();
-
-  status_callback("Allocating histogram vector and magnitudes.");
-  auto histogram_bin_count = histogram_bins.x * histogram_bins.y;
-  thrust::device_vector<float3> histogram_vectors   (histogram_bin_count);
-  thrust::device_vector<float > histogram_magnitudes(histogram_bin_count);
-  auto histogram_vectors_ptr    = raw_pointer_cast(&histogram_vectors   [0]);
-  auto histogram_magnitudes_ptr = raw_pointer_cast(&histogram_magnitudes[0]);
-
-  status_callback("Allocating spherical harmonics basis matrix.");
-  auto coefficient_count = cush::coefficient_count(maximum_degree);
-  auto matrix_size       = histogram_bin_count * coefficient_count;
-  thrust::device_vector<float> basis_matrix(matrix_size, 0.0F);
-  auto basis_matrix_ptr = raw_pointer_cast(&basis_matrix[0]);
-
-  status_callback("Allocating spherical harmonics coefficients.");
-  auto total_coefficient_count = voxel_count * coefficient_count;
-  thrust::device_vector<float> coefficient_vectors(total_coefficient_count);
-  auto coefficient_vectors_ptr = raw_pointer_cast(&coefficient_vectors[0]);
-
-  status_callback("Generating histogram bins.");
-  create_bins<<<cush::grid_size_2d(dim3(histogram_bins.x, histogram_bins.y)), cush::block_size_2d()>>>(
-    histogram_bins       , 
-    histogram_vectors_ptr);
-  cudaDeviceSynchronize();
-
-  status_callback("Calculating spherical harmonics basis matrix.");
-  cush::calculate_matrix<<<cush::grid_size_2d(dim3(histogram_bin_count, coefficient_count)), cush::block_size_2d()>>>(
-    histogram_bin_count  , 
-    coefficient_count    ,
-    histogram_vectors_ptr, 
-    basis_matrix_ptr     );
-  cudaDeviceSynchronize();
-
-  status_callback("Calculating SVD work buffer size.");
-  int buffer_size;
-  cusolverDnSgesvd_bufferSize(cusolver, histogram_bin_count, coefficient_count, &buffer_size);
-  cudaDeviceSynchronize();
-  auto complex_buffer_size = static_cast<float>(buffer_size);
-
-  status_callback("Allocating SVD buffers.");
-  thrust::device_vector<float> buffer (buffer_size, 0.0);
-  thrust::device_vector<int>   info   (1);
-  thrust::device_vector<float> U      (histogram_bin_count * histogram_bin_count);
-  thrust::device_vector<float> E      (coefficient_count                        );
-  thrust::device_vector<float> VT     (coefficient_count   * coefficient_count  );
-  thrust::device_vector<float> UT     (histogram_bin_count * histogram_bin_count);
-  thrust::device_vector<float> EI_UT  (histogram_bin_count * coefficient_count  );
-  thrust::device_vector<float> V_EI_UT(histogram_bin_count * coefficient_count  );
-  auto alpha       = 1.0F;
-  auto beta        = 0.0F;
-  auto buffer_ptr  = raw_pointer_cast(&buffer [0]);
-  auto info_ptr    = raw_pointer_cast(&info   [0]);
-  auto U_ptr       = raw_pointer_cast(&U      [0]);
-  auto E_ptr       = raw_pointer_cast(&E      [0]);
-  auto VT_ptr      = raw_pointer_cast(&VT     [0]);
-  auto UT_ptr      = raw_pointer_cast(&UT     [0]);
-  auto EI_UT_ptr   = raw_pointer_cast(&EI_UT  [0]);
-  auto V_EI_UT_ptr = raw_pointer_cast(&V_EI_UT[0]);
+  auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  auto z = blockIdx.z * blockDim.z + threadIdx.z;
 
-  status_callback("Applying SVD.");
-  cusolverDnSgesvd(
-    cusolver            ,
-    'A'                 ,
-    'A'                 ,
-    histogram_bin_count ,
-    coefficient_count   ,
-    basis_matrix_ptr    ,
-    histogram_bin_count ,
-    E_ptr               ,
-    U_ptr               ,
+  if (x >= dimensions.x || y >= dimensions.y || z >= dimensions.z)
+    return;
+    
+  auto volume_index              = z + dimensions.z * (y + dimensions.y * x);
+  auto coefficient_count         = pli::coefficient_count(maximum_degree);
+  auto coefficient_vector_offset = volume_index * coefficient_count;
+  auto alpha                     = 1.0F;
+  auto beta                      = 0.0F;
+  
+  float* histogram_magnitudes;
+  cudaMalloc(&histogram_magnitudes, histogram_bin_count * sizeof(float));
+  for(auto i = 0; i < histogram_bin_count; ++i)
+    histogram_magnitudes[i] = 0.0F;
+    
+  uint3 offset {
+    vectors_dimensions.x * x,
+    vectors_dimensions.y * y,
+    vectors_dimensions.z * z };
+  uint3 size {
+    vectors_dimensions.x * dimensions.x,
+    vectors_dimensions.y * dimensions.y,
+    vectors_dimensions.z * dimensions.z };
+
+  cublasHandle_t cublas;
+  cublasCreate(&cublas);
+
+  accumulate_kernel<<<grid_size_3d(vectors_dimensions), block_size_3d()>>>(
+    vectors_dimensions  ,
+    offset              ,
+    size                ,
+    vectors             ,
     histogram_bin_count ,
-    VT_ptr              ,
-    coefficient_count   ,
-    buffer_ptr          ,
-    buffer_size         ,
-    &complex_buffer_size,
-    info_ptr);
+    histogram_vectors   ,
+    histogram_magnitudes);
+  __syncthreads();
   cudaDeviceSynchronize();
+  __syncthreads();
+
+  cublasSgemv(
+    cublas                                         ,
+    CUBLAS_OP_N                                    ,
+    coefficient_count                              ,
+    histogram_bin_count                            ,
+    &alpha                                         ,
+    inverse_transform                              ,
+    coefficient_count                              ,
+    histogram_magnitudes                           ,
+    1                                              ,
+    &beta                                          ,
+    coefficient_vectors + coefficient_vector_offset,
+    1                                              );
+
+  cublasDestroy(cublas);
+
+  cudaFree(histogram_magnitudes);
+}
 
-  status_callback("Transposing U to U^T.");
-  cublasSgeam(
-    cublas              ,
-    CUBLAS_OP_T         ,
-    CUBLAS_OP_N         ,
-    histogram_bin_count ,
-    histogram_bin_count ,
-    &alpha              ,
-    U_ptr               ,
-    histogram_bin_count ,
-    &beta               ,
-    nullptr             ,
-    histogram_bin_count ,
-    UT_ptr              ,
-    histogram_bin_count);
-  cudaDeviceSynchronize();
+// Call on a dimensions.x x dimensions.y x dimensions.z 3D grid.
+__global__ void sum_and_cluster_kernel(
+  // Sum related parameters. 
+  const uint3    dimensions       , 
+  const unsigned offset           ,
+  unsigned       maximum_degree   ,
+  float*         coefficients     , 
+  bool           limit_to_2d      ,
+  // Clustering related parameters.     
+  bool           clustering       , 
+  float          cluster_threshold)
+{
+  auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  auto z = blockIdx.z * blockDim.z + threadIdx.z;
 
-  status_callback("Inverting E to E^-1.");
-  thrust::transform(
-    E.begin(),
-    E.end  (),
-    E.begin(),
-    [] COMMON (float& entry) -> float
-    {
-      if (int(entry) == 0)
-        return 0;
-      return entry = 1.0F / entry;
-    });
-  cudaDeviceSynchronize();
+  if (x >= dimensions.x || y >= dimensions.y || z >= dimensions.z)
+    return;
 
-  status_callback("Computing E^-1 * U^T.");
-  cublasSdgmm(
-    cublas             ,
-    CUBLAS_SIDE_LEFT   ,
-    coefficient_count  ,
-    histogram_bin_count,
-    UT_ptr             ,
-    histogram_bin_count,
-    E_ptr              ,
-    1                  ,
-    EI_UT_ptr          ,
-    coefficient_count);
-  cudaDeviceSynchronize();
+  uint3 lower_layer_dimensions = {dimensions.x * 2, dimensions.y * 2, limit_to_2d ? 1 : dimensions.z * 2};
+  auto  lower_layer_offset     = offset - lower_layer_dimensions.x * lower_layer_dimensions.y * lower_layer_dimensions.z;
+  auto  coefficient_count      = pli::coefficient_count(maximum_degree);
+  auto  coefficient_offset     = coefficient_count * (offset + z + dimensions.z * (y + dimensions.y * x));
 
-  status_callback("Computing V * E^-1 U^T.");
-  cublasSgemm(
-    cublas             ,
-    CUBLAS_OP_T        ,
-    CUBLAS_OP_N        ,
-    coefficient_count  ,
-    histogram_bin_count,
-    coefficient_count  ,
-    &alpha             ,
-    VT_ptr             ,
-    coefficient_count  ,
-    EI_UT_ptr          ,
-    coefficient_count  ,
-    &beta              ,
-    V_EI_UT_ptr        ,
-    coefficient_count);
-  cudaDeviceSynchronize();
+  for (auto i = 0; i < 2; i++)
+    for (auto j = 0; j < 2; j++)
+      for (auto k = 0; k < (limit_to_2d ? 1 : 2); k++)
+        for (auto c = 0; c < coefficient_count; c++)
+          coefficients[coefficient_offset + c] += coefficients[coefficient_count * (lower_layer_offset + (2 * z + k) + lower_layer_dimensions.z * (2 * y + j + lower_layer_dimensions.y * (2 * x + i))) + c] / powf(2, limit_to_2d ? 2 : 3);
 
-  status_callback("Accumulating histograms and projecting via V E^-1 U^T * h. This might take a while.");
-  for (unsigned x = 0; x < dimensions.x; x++)
+  if (clustering)
   {
-    for (unsigned y = 0; y < dimensions.y; y++)
-    {
-      for (unsigned z = 0; z < dimensions.z; z++)
-      {
-        auto volume_index        = z + dimensions.z * (y + dimensions.y * x);
-        auto coefficients_offset = volume_index * coefficient_count;
-
-        fill(histogram_magnitudes.begin(), histogram_magnitudes.end(), 0.0F);
-
-        uint3 offset {
-          vectors_size.x * x,
-          vectors_size.y * y,
-          vectors_size.z * z};
-        uint3 size  {
-          vectors_size.x * dimensions.x,
-          vectors_size.y * dimensions.y,
-          vectors_size.z * dimensions.z};
-
-        accumulate<<<cush::grid_size_3d(vectors_size), cush::block_size_3d()>>>(
-          vectors_size         ,
-          offset               ,
-          size                 ,
-          longitudes_ptr       , 
-          latitudes_ptr        , 
-          histogram_bins       , 
-          histogram_vectors_ptr, 
-          histogram_magnitudes_ptr);
-
-        cublasSgemv(
-          cublas                                        ,
-          CUBLAS_OP_N                                   ,
-          coefficient_count                             ,
-          histogram_bin_count                           ,
-          &alpha                                        ,
-          V_EI_UT_ptr                                   ,
-          coefficient_count                             ,
-          histogram_magnitudes_ptr                      , 
-          1                                             ,
-          &beta                                         ,
-          coefficient_vectors_ptr  + coefficients_offset,
-          1                                             );
-
-        cudaDeviceSynchronize();
-
-        //status_callback(std::to_string(volume_index + 1) + "/" + std::to_string(voxel_count));
-      }
-    }
-  }
-
-  status_callback("Copying coefficients to CPU.");
-  cudaMemcpy(coefficients, coefficient_vectors_ptr, sizeof(float) * total_coefficient_count, cudaMemcpyDeviceToHost);
+    // Compare this voxel to each associated voxel. 
+    auto is_similar = true;
+    for (auto i = 0; i < 2; i++)
+      for (auto j = 0; j < 2; j++)
+        for (auto k = 0; k < (limit_to_2d ? 1 : 2); k++)
+        {
+          auto other_offset = coefficient_count * (lower_layer_offset + (2 * z + k) + lower_layer_dimensions.z * (2 * y + j + lower_layer_dimensions.y * (2 * x + i)));
+          if (is_zero(coefficient_count, coefficients + other_offset) || l2_distance(coefficient_count, coefficients + coefficient_offset, coefficients + other_offset) > cluster_threshold)
+            is_similar = false;
+        }
 
-  auto total_end = std::chrono::system_clock::now();
-  std::chrono::duration<double> total_elapsed_seconds = total_end - total_start;
-  status_callback("Cuda ODF calculation operations took " + std::to_string(total_elapsed_seconds.count()) + " seconds.");
+    // If deemed similar, drop the associated voxels' coefficients.
+    if (is_similar)
+      for (auto i = 0; i < 2; i++)
+        for (auto j = 0; j < 2; j++)
+          for (auto k = 0; k < (limit_to_2d ? 1 : 2); k++)
+          {
+            auto other_offset = coefficient_count * (lower_layer_offset + (2 * z + k) + lower_layer_dimensions.z * (2 * y + j + lower_layer_dimensions.y * (2 * x + i)));
+            for (auto c = 0; c < coefficient_count; c++)
+              coefficients[other_offset + c] = 0.0;
+          }
+    // Else, drop this voxel's coefficients.
+    else
+      for (auto c = 0; c < coefficient_count; c++)
+        coefficients[coefficient_offset + c] = 0.0;
+  }
 }
 
 void calculate_odfs(
-  cublasHandle_t     cublas        , 
-  cusolverDnHandle_t cusolver      , 
-  const uint3&       dimensions    , 
-  const uint3&       vectors_size  , 
-  const uint2&       histogram_bins, 
-  const unsigned     maximum_degree, 
-  const float3*      unit_vectors  , 
-        float*       coefficients  , 
+  cublasHandle_t     cublas            , 
+  cusolverDnHandle_t cusolver          , 
+  const uint3&       dimensions        , 
+  const uint3&       vectors_dimensions, 
+  const uint2&       histogram_bins    , 
+  const unsigned     maximum_degree    , 
+  const float3*      unit_vectors      , 
+        float*       coefficients      , 
+        bool         even_only         ,
         std::function<void(const std::string&)> status_callback)
 {
-  auto total_start = std::chrono::system_clock::now();
+  auto start_time = std::chrono::system_clock::now();
 
   status_callback("Allocating and copying vectors.");
   auto voxel_count        = dimensions  .x * dimensions  .y * dimensions  .z;
-  auto vector_count       = vectors_size.x * vectors_size.y * vectors_size.z;
-  auto total_vector_count = voxel_count * vector_count;
-  thrust::device_vector<float3> vectors(total_vector_count);
-  copy_n(unit_vectors, total_vector_count, vectors.begin());
-  auto vectors_ptr = raw_pointer_cast(&vectors[0]);
-  transform(vectors.begin(), vectors.end(), vectors.begin(), [] COMMON (const float3& value) {
-    return cush::to_spherical_coords(value / length(value));
-  });
+  auto total_vector_count = voxel_count * vectors_dimensions.x * vectors_dimensions.y * vectors_dimensions.z;
+  thrust::device_vector<float3> gpu_vectors(total_vector_count);
+  auto gpu_vectors_ptr = raw_pointer_cast(&gpu_vectors[0]);
+  copy_n(unit_vectors, total_vector_count, gpu_vectors.begin());
   cudaDeviceSynchronize();
 
   status_callback("Allocating histogram vector and magnitudes.");
   auto histogram_bin_count = histogram_bins.x * histogram_bins.y;
-  thrust::device_vector<float3> histogram_vectors   (histogram_bin_count);
-  thrust::device_vector<float > histogram_magnitudes(histogram_bin_count);
-  auto histogram_vectors_ptr    = raw_pointer_cast(&histogram_vectors   [0]);
-  auto histogram_magnitudes_ptr = raw_pointer_cast(&histogram_magnitudes[0]);
+  thrust::device_vector<float3> histogram_vectors(histogram_bin_count);
+  auto histogram_vectors_ptr = raw_pointer_cast(&histogram_vectors[0]);
 
   status_callback("Allocating spherical harmonics basis matrix.");
-  auto coefficient_count = cush::coefficient_count(maximum_degree);
+  auto coefficient_count = pli::coefficient_count(maximum_degree);
   auto matrix_size       = histogram_bin_count * coefficient_count;
   thrust::device_vector<float> basis_matrix(matrix_size, 0.0F);
   auto basis_matrix_ptr = raw_pointer_cast(&basis_matrix[0]);
 
   status_callback("Allocating spherical harmonics coefficients.");
   auto total_coefficient_count = voxel_count * coefficient_count;
-  thrust::device_vector<float> coefficient_vectors(total_coefficient_count);
-  auto coefficient_vectors_ptr = raw_pointer_cast(&coefficient_vectors[0]);
+  thrust::device_vector<float> gpu_coefficients(total_coefficient_count);
+  auto gpu_coefficients_ptr = raw_pointer_cast(&gpu_coefficients[0]);
 
   status_callback("Generating histogram bins.");
-  create_bins<<<cush::grid_size_2d(dim3(histogram_bins.x, histogram_bins.y)), cush::block_size_2d()>>>(
+  create_bins_kernel<<<grid_size_2d(dim3(histogram_bins.x, histogram_bins.y)), block_size_2d()>>>(
     histogram_bins       , 
     histogram_vectors_ptr);
   cudaDeviceSynchronize();
 
   status_callback("Calculating spherical harmonics basis matrix.");
-  cush::calculate_matrix<<<cush::grid_size_2d(dim3(histogram_bin_count, coefficient_count)), cush::block_size_2d()>>>(
+  calculate_matrix<<<grid_size_2d(dim3(histogram_bin_count, coefficient_count)), block_size_2d()>>>(
     histogram_bin_count  , 
     coefficient_count    ,
     histogram_vectors_ptr, 
-    basis_matrix_ptr     );
+    basis_matrix_ptr     ,
+    even_only            );
   cudaDeviceSynchronize();
 
   status_callback("Calculating SVD work buffer size.");
@@ -344,8 +252,9 @@ void calculate_odfs(
     buffer_ptr          ,
     buffer_size         ,
     &complex_buffer_size,
-    info_ptr);
+    info_ptr            );
   cudaDeviceSynchronize();
+  buffer.clear();
 
   status_callback("Transposing U to U^T.");
   cublasSgeam(
@@ -363,13 +272,14 @@ void calculate_odfs(
     UT_ptr              ,
     histogram_bin_count);
   cudaDeviceSynchronize();
+  U.clear();
 
   status_callback("Inverting E to E^-1.");
   thrust::transform(
     E.begin(),
     E.end  (),
     E.begin(),
-    [] COMMON (float& entry) -> float
+    [] __host__ __device__(float& entry) -> float
     {
       if (int(entry) == 0)
         return 0;
@@ -390,6 +300,8 @@ void calculate_odfs(
     EI_UT_ptr          ,
     coefficient_count);
   cudaDeviceSynchronize();
+  UT.clear();
+  E .clear();
 
   status_callback("Computing V * E^-1 U^T.");
   cublasSgemm(
@@ -408,112 +320,83 @@ void calculate_odfs(
     V_EI_UT_ptr        ,
     coefficient_count);
   cudaDeviceSynchronize();
+  VT   .clear();
+  EI_UT.clear();
 
   status_callback("Accumulating histograms and projecting via V E^-1 U^T * h. This might take a while.");
-  for (unsigned x = 0; x < dimensions.x; x++)
-  {
-    for (unsigned y = 0; y < dimensions.y; y++)
-    {
-      for (unsigned z = 0; z < dimensions.z; z++)
-      {
-        auto volume_index        = z + dimensions.z * (y + dimensions.y * x);
-        auto coefficients_offset = volume_index * coefficient_count;
-
-        fill(histogram_magnitudes.begin(), histogram_magnitudes.end(), 0.0F);
-
-        uint3 offset {
-          vectors_size.x * x,
-          vectors_size.y * y,
-          vectors_size.z * z};
-        uint3 size  {
-          vectors_size.x * dimensions.x,
-          vectors_size.y * dimensions.y,
-          vectors_size.z * dimensions.z};
-
-        accumulate<<<cush::grid_size_3d(vectors_size), cush::block_size_3d()>>>(
-          vectors_size         ,
-          offset               ,
-          size                 ,
-          vectors_ptr          ,
-          histogram_bins       , 
-          histogram_vectors_ptr, 
-          histogram_magnitudes_ptr);
-
-        cublasSgemv(
-          cublas                                        ,
-          CUBLAS_OP_N                                   ,
-          coefficient_count                             ,
-          histogram_bin_count                           ,
-          &alpha                                        ,
-          V_EI_UT_ptr                                   ,
-          coefficient_count                             ,
-          histogram_magnitudes_ptr                      , 
-          1                                             ,
-          &beta                                         ,
-          coefficient_vectors_ptr  + coefficients_offset,
-          1                                             );
-
-        cudaDeviceSynchronize();
-
-        //status_callback(std::to_string(volume_index + 1) + "/" + std::to_string(voxel_count));
-      }
-    }
-  }
+  quantify_and_project_kernel<<<dimensions, dim3(1, 1, 1)>>> (
+    dimensions             ,
+    vectors_dimensions     ,
+    gpu_vectors_ptr        ,
+    histogram_bin_count    ,
+    histogram_vectors_ptr  ,
+    maximum_degree         ,
+    V_EI_UT_ptr            ,
+    gpu_coefficients_ptr   );
+  cudaDeviceSynchronize();
+  gpu_vectors      .clear();
+  histogram_vectors.clear();
 
   status_callback("Copying coefficients to CPU.");
-  cudaMemcpy(coefficients, coefficient_vectors_ptr, sizeof(float) * total_coefficient_count, cudaMemcpyDeviceToHost);
+  cudaMemcpy(coefficients, gpu_coefficients_ptr, sizeof(float) * total_coefficient_count, cudaMemcpyDeviceToHost);
 
-  auto total_end = std::chrono::system_clock::now();
-  std::chrono::duration<double> total_elapsed_seconds = total_end - total_start;
-  status_callback("Cuda ODF calculation operations took " + std::to_string(total_elapsed_seconds.count()) + " seconds.");
+  auto end_time = std::chrono::system_clock::now();
+  std::chrono::duration<double> elapsed_time = end_time - start_time;
+  status_callback("Cuda ODF calculation operations took " + std::to_string(elapsed_time.count()) + " seconds.");
 }
 
 void sample_odfs(
   const uint3&       dimensions       ,
-  const unsigned     coefficient_count,
+  const unsigned     maximum_degree   ,
   const float*       coefficients     ,
   const uint2&       tessellations    ,
-  const float3&      vector_spacing   ,
   const uint3&       vector_dimensions,
   const float        scale            ,
         float3*      points           ,
-        float4*      colors           ,
+        float3*      directions       ,
         unsigned*    indices          ,
+        bool         hierarchical     ,
         bool         clustering       ,
         float        cluster_threshold,
         std::function<void(const std::string&)> status_callback)
 {
-  auto total_start = std::chrono::system_clock::now();
-  
+  auto start_time = std::chrono::system_clock::now();
+
   auto base_voxel_count = dimensions.x * dimensions.y * dimensions.z;
   auto dimension_count  = dimensions.z > 1 ? 3 : 2;
   auto min_dimension    = min(dimensions.x, dimensions.y);
   if (dimension_count == 3)
-    min_dimension = min(min_dimension, dimensions.z);
+    min_dimension       = min(min_dimension, dimensions.z);
   auto max_layer        = int(log(min_dimension) / log(2));
-  auto voxel_count      = unsigned(base_voxel_count * 
-    ((1.0 - pow(1.0 / pow(2, dimension_count), max_layer + 1)) / 
-     (1.0 -     1.0 / pow(2, dimension_count))));
 
+  auto voxel_count = base_voxel_count;
+  if (hierarchical)
+    voxel_count = unsigned(base_voxel_count * 
+      ((1.0 - pow(1.0 / pow(2, dimension_count), max_layer + 1)) / 
+       (1.0 -     1.0 / pow(2, dimension_count))));
+
+  auto coefficient_count  = pli::coefficient_count(maximum_degree);
   auto tessellation_count = tessellations.x * tessellations.y;
   auto point_count        = voxel_count * tessellation_count;
 
   status_callback("Allocating and copying the leaf spherical harmonics coefficients.");
   thrust::device_vector<float> coefficient_vectors(voxel_count * coefficient_count);
-  copy_n(coefficients, base_voxel_count * coefficient_count, coefficient_vectors.begin());
   auto coefficients_ptr = raw_pointer_cast(&coefficient_vectors[0]);
 
+  copy_n(coefficients, base_voxel_count * coefficient_count, coefficient_vectors.begin());
+  cudaDeviceSynchronize();
+
   auto layer_offset     = 0;
   auto layer_dimensions = dimensions;
-  for (auto layer = max_layer; layer >= 0; layer--)
+  for (auto layer = max_layer; layer >= (hierarchical ? 0 : max_layer); layer--)
   {
     if (layer != max_layer)
     {
       status_callback("Calculating the layer " + std::to_string(int(layer)) + " coefficients.");
-      sample_odf_layer<<<cush::grid_size_3d(layer_dimensions), cush::block_size_3d()>>>(
+      sum_and_cluster_kernel<<<grid_size_3d(layer_dimensions), block_size_3d()>>>(
         layer_dimensions    ,
         layer_offset        ,
-        coefficient_count   ,
+        maximum_degree      ,
         coefficients_ptr    ,
         dimension_count == 2,
         clustering          ,
@@ -531,10 +414,10 @@ void sample_odfs(
   
   layer_offset     = 0;
   layer_dimensions = dimensions;
-  for (auto layer = max_layer; layer >= 0; layer--)
+  for (auto layer = max_layer; layer >= (hierarchical ? 0 : max_layer); layer--)
   {
     status_callback("Sampling sums of the layer " + std::to_string(int(layer)) + " coefficients.");
-    cush::sample_sums<<<cush::grid_size_3d(layer_dimensions), cush::block_size_3d()>>>(
+    sample_sums<<<grid_size_3d(layer_dimensions), block_size_3d()>>>(
       layer_dimensions ,
       coefficient_count,
       tessellations    ,
@@ -558,29 +441,20 @@ void sample_odfs(
     points,
     points + point_count,
     points,
-    [] COMMON (const float3& point)
+    [] __host__ __device__(const float3& point)
     {
-      return cush::to_cartesian_coords(point);
+      return to_cartesian_coords(point);
     });
   cudaDeviceSynchronize();
 
-  status_callback("Assigning colors.");
-  thrust::transform(
-    thrust::device,
-    points,
-    points + point_count,
-    colors,
-    [] COMMON (const float3& point)
-    {
-      // return make_float4(abs(point.x), abs(point.y), abs(point.z), 1.0); // Default
-      return make_float4(abs(point.x), abs(point.z), abs(point.y), 1.0); // DMRI
-    });
+  status_callback("Assigning directions.");
+  thrust::copy(thrust::device, points, points + point_count, directions);
   cudaDeviceSynchronize();
 
   status_callback("Translating and scaling the points.");
   layer_offset     = 0;
   layer_dimensions = dimensions;
-  for (auto layer = max_layer; layer >= 0; layer--)
+  for (auto layer = max_layer; layer >= (hierarchical ? 0 : max_layer); layer--)
   {
     auto layer_point_offset = 
       layer_offset * 
@@ -590,27 +464,31 @@ void sample_odfs(
       layer_dimensions.y * 
       layer_dimensions.z * 
       tessellation_count;
-
+  
     uint3 layer_vectors_size {
       vector_dimensions.x * unsigned(pow(2, max_layer - layer)),
       vector_dimensions.y * unsigned(pow(2, max_layer - layer)),
       vector_dimensions.z * unsigned(pow(2, max_layer - layer)) };
     float3 layer_position {
-      vector_spacing.x * (layer_vectors_size.x - 1.0F) * 0.5F,
-      vector_spacing.y * (layer_vectors_size.y - 1.0F) * 0.5F,
-      dimension_count == 3 ? vector_spacing.z * (layer_vectors_size.z - 1) * 0.5F : 0.0F };
+      (layer_vectors_size.x - 1.0F) * 0.5F,
+      (layer_vectors_size.y - 1.0F) * 0.5F,
+      dimension_count == 3 ? (layer_vectors_size.z - 1) * 0.5F : 0.0F };
     float3 layer_spacing {
-      vector_spacing.x * layer_vectors_size.x,
-      vector_spacing.y * layer_vectors_size.y,
-      dimension_count == 3 ? vector_spacing.z * layer_vectors_size.z : 1.0F };
-    auto layer_scale = scale * min(min(layer_spacing.x, layer_spacing.y), layer_spacing.z) * 0.5F;
-
+      layer_vectors_size.x,
+      layer_vectors_size.y,
+      dimension_count == 3 ? layer_vectors_size.z : 1.0F };
+    float  min_spacing = min(layer_spacing.x, layer_spacing.y);
+    if(dimension_count == 3)
+      min_spacing = min(min_spacing, layer_spacing.z);
+  
+    auto layer_scale = scale * min_spacing  * 0.5F;
+  
     thrust::transform(
       thrust::device,
       points + layer_point_offset,
       points + layer_point_offset + layer_point_count,
       points + layer_point_offset,
-      [=] COMMON (const float3& point)
+      [=] __host__ __device__(const float3& point)
       {
         auto output = layer_scale * point;
         auto index  = int((&point - (points + layer_point_offset)) / tessellation_count);
@@ -620,7 +498,7 @@ void sample_odfs(
         return output;
       });
     cudaDeviceSynchronize();
-
+  
     layer_offset += layer_dimensions.x * layer_dimensions.y * layer_dimensions.z;
     layer_dimensions = {
       layer_dimensions.x / 2,
@@ -629,86 +507,62 @@ void sample_odfs(
     };
   }
 
-  status_callback("Inverting Y coordinates.");
-  thrust::transform(
-    thrust::device,
-    points,
-    points + point_count,
-    points,
-    [] COMMON(float3 point)
-  {
-    point.y = -point.y;
-    return point;
-  });
-  cudaDeviceSynchronize();
-
-  auto total_end = std::chrono::system_clock::now();
-  std::chrono::duration<double> total_elapsed_seconds = total_end - total_start;
-  status_callback("Cuda ODF sampling operations took " + std::to_string(total_elapsed_seconds.count()) + " seconds.");
+  auto end_time = std::chrono::system_clock::now();
+  std::chrono::duration<double> elapsed_time = end_time - start_time;
+  status_callback("Cuda ODF sampling operations took " + std::to_string(elapsed_time.count()) + " seconds.");
 }
 
-__global__ void sample_odf_layer(
-  const uint3    layer_dimensions , 
-  const unsigned layer_offset     , 
-  const unsigned coefficient_count, 
-  float*         coefficients     , 
-  bool           is_2d            , 
-  bool           clustering       , 
-  float          cluster_threshold)
+void extract_peaks(
+  const uint3&   dimensions    ,
+  const unsigned maximum_degree,
+  const float*   coefficients  ,
+  const uint2&   tessellations , 
+  const unsigned maxima_count  ,
+        float3*  maxima        ,
+        std::function<void(const std::string&)> status_callback)
 {
-  auto x = blockIdx.x * blockDim.x + threadIdx.x;
-  auto y = blockIdx.y * blockDim.y + threadIdx.y;
-  auto z = blockIdx.z * blockDim.z + threadIdx.z;
+  auto start_time = std::chrono::system_clock::now();
 
-  if (x >= layer_dimensions.x ||
-    y >= layer_dimensions.y ||
-    z >= layer_dimensions.z)
-    return;
+  auto voxel_count       = dimensions.x * dimensions.y * dimensions.z;
+  auto coefficient_count = pli::coefficient_count(maximum_degree);
+
+  status_callback("Allocating and copying the spherical harmonics coefficients.");
+  thrust::device_vector<float> coefficient_vectors(voxel_count * coefficient_count);
+  auto coefficients_ptr = raw_pointer_cast(&coefficient_vectors[0]);
+  copy_n(coefficients, coefficient_vectors.size(), coefficient_vectors.begin());
+  cudaDeviceSynchronize();
 
-  auto dimension_count = is_2d ? 2 : 3;
-  uint3 lower_layer_dimensions = {layer_dimensions.x * 2, layer_dimensions.y * 2, dimension_count == 3 ? layer_dimensions.z * 2 : 1};
-  auto lower_layer_voxel_count = lower_layer_dimensions.x * lower_layer_dimensions.y * lower_layer_dimensions.z;
-  auto lower_layer_offset = layer_offset - lower_layer_voxel_count;
-  auto offset = coefficient_count * (layer_offset + z + layer_dimensions.z * (y + layer_dimensions.y * x));
+  status_callback("Allocating the maxima vectors.");
+  thrust::device_vector<float3> maxima_vectors(voxel_count * maxima_count);
+  auto maxima_ptr = raw_pointer_cast(&maxima_vectors[0]);
+
+  status_callback("Extracting maxima.");
+  extract_maxima<<<grid_size_3d(dimensions), block_size_3d()>>>(
+    dimensions       ,
+    coefficient_count,
+    coefficients_ptr ,
+    tessellations    ,
+    maxima_count     ,
+    maxima_ptr       );
+  cudaDeviceSynchronize();
 
-  // Locate the associated voxels in the lower layer and sum them into this voxel.
-  for (auto i = 0; i < 2; i++)
-    for (auto j = 0; j < 2; j++)
-      for (auto k = 0; k < dimension_count - 1; k++)
-        for (auto c = 0; c < coefficient_count; c++)
-          coefficients[offset + c] += coefficients[
-              coefficient_count *
-              (lower_layer_offset + (2 * z + k) + lower_layer_dimensions.z * ((2 * y + j) + lower_layer_dimensions.y * (2 * x + i))) + c]
-            / powf(2, dimension_count);
+  status_callback("Converting the maxima to Cartesian coordinates.");
+  thrust::transform(
+    thrust::device,
+    maxima_vectors.begin(),
+    maxima_vectors.end  (),
+    maxima_vectors.begin(),
+    [] __host__ __device__(const float3& point)
+    {
+      return to_cartesian_coords(point);
+    });
+  cudaDeviceSynchronize();
 
-  if (clustering)
-  {
-    // Compare this voxel to each associated voxel. 
-    auto is_similar = true;
-    for (auto i = 0; i < 2; i++)
-      for (auto j = 0; j < 2; j++)
-        for (auto k = 0; k < dimension_count - 1; k++)
-        {
-          auto other_offset = coefficient_count * (lower_layer_offset + (2 * z + k) + lower_layer_dimensions.z * ((2 * y + j) + lower_layer_dimensions.y * (2 * x + i)));
-          if (cush::is_zero(coefficient_count, coefficients + other_offset) ||
-            cush::l2_distance(coefficient_count, coefficients + offset, coefficients + other_offset) > cluster_threshold)
-            is_similar = false;
-        }
+  status_callback("Copying maxima to CPU.");
+  cudaMemcpy(maxima, maxima_ptr, sizeof(float3) * maxima_vectors.size(), cudaMemcpyDeviceToHost);
 
-    // If deemed similar, drop the associated voxels' coefficients.
-    if (is_similar)
-      for (auto i = 0; i < 2; i++)
-        for (auto j = 0; j < 2; j++)
-          for (auto k = 0; k < dimension_count - 1; k++)
-          {
-            auto other_offset = coefficient_count * (lower_layer_offset + (2 * z + k) + lower_layer_dimensions.z * ((2 * y + j) + lower_layer_dimensions.y * (2 * x + i)));
-            for (auto c = 0; c < coefficient_count; c++)
-              coefficients[other_offset + c] = 0.0;
-          }
-    // Else, drop this voxel's coefficients.
-    else
-      for (auto c = 0; c < coefficient_count; c++)
-        coefficients[offset + c] = 0.0;
-  }
+  auto end_time = std::chrono::system_clock::now();
+  std::chrono::duration<double> elapsed_time = end_time - start_time;
+  status_callback("Cuda ODF sampling operations took " + std::to_string(elapsed_time.count()) + " seconds.");
 }
 }
diff --git a/source/cuda/polar_plot.cu b/source/cuda/polar_plot.cu
new file mode 100644
index 0000000000000000000000000000000000000000..a06c0eda021dff49a8df7e1e0a42e5fa76caaf6e
--- /dev/null
+++ b/source/cuda/polar_plot.cu
@@ -0,0 +1,136 @@
+#include <pli_vis/cuda/polar_plot.h>
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <array>
+#include <vector>
+
+#include <device_launch_parameters.h>
+#include <host_defines.h>
+#include <thrust/device_vector.h>
+#include <thrust/extrema.h>
+#include <thrust/transform.h>
+
+#include <pli_vis/cuda/sh/launch.h>
+#include <pli_vis/cuda/utility/convert.h>
+#include <pli_vis/cuda/utility/vector_ops.h>
+
+namespace pli
+{
+template<typename vector_type = float3, typename polar_plot_type = float>
+__global__ void calculate_kernel(
+  const vector_type* vectors              ,
+  const uint2        vectors_dimensions   ,
+  const uint2        superpixel_dimensions,
+  const unsigned     superpixel_size      ,
+  const unsigned     angular_partitions   ,
+  const bool         symmetric            ,
+  polar_plot_type*   polar_plots          )
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  if (x >= vectors_dimensions.x || y >= vectors_dimensions.y)
+    return;
+
+  const auto superpixel_x      = x / superpixel_size;
+  const auto superpixel_y      = y / superpixel_size;
+  const auto superpixel_offset = (superpixel_y + superpixel_dimensions.y * superpixel_x) * angular_partitions;
+
+  const auto& vector = vectors[y + vectors_dimensions.y * x];
+  const auto  index  = static_cast<unsigned>(round((vector.y + (vector.y <= 0.0 ? 2.0 * M_PI : 0.0)) * angular_partitions / (2.0 * M_PI))) % angular_partitions;
+
+  atomicAdd(&polar_plots[superpixel_offset + index], polar_plot_type(1));
+  if(symmetric) atomicAdd(&polar_plots[
+    superpixel_offset + index + angular_partitions / 2 < angular_partitions ? 
+    superpixel_offset + index + angular_partitions / 2                      :
+    superpixel_offset + index - angular_partitions / 2], polar_plot_type(1));
+}
+
+template<typename polar_plot_type = float, typename vertex_type = float3, typename direction_type = float3>
+__global__ void generate_kernel(
+  const polar_plot_type* polar_plots          ,
+  const uint2            superpixel_dimensions,
+  const unsigned         superpixel_size      ,
+  const unsigned         angular_partitions   ,
+  vertex_type*           vertices             ,
+  direction_type*        directions           )
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  if (x >= superpixel_dimensions.x || y >= superpixel_dimensions.y)
+    return;
+
+  const auto index             = y + superpixel_dimensions.y * x;
+  const auto superpixel_offset = index * angular_partitions;
+  const auto vertex_offset     = index * angular_partitions * 3;
+  for (auto i = 0, j = 0; j < angular_partitions; i+=3, ++j)
+  {
+    vertex_type spatial_offset {superpixel_size * (0.5 + x) - 0.5, superpixel_size * (0.5 + y) - 0.5, 0.0};
+    vertices  [vertex_offset + i    ] = spatial_offset ,
+    vertices  [vertex_offset + i + 1] = spatial_offset + to_cartesian_coords(vertex_type {polar_plots[superpixel_offset + (j + 1) % angular_partitions] * superpixel_size / 2.0, 2.0 * M_PI * ((j + 1) % angular_partitions) / angular_partitions, M_PI / 2.0});
+    vertices  [vertex_offset + i + 2] = spatial_offset + to_cartesian_coords(vertex_type {polar_plots[superpixel_offset +  j      % angular_partitions] * superpixel_size / 2.0, 2.0 * M_PI *   j                            / angular_partitions, M_PI / 2.0});
+    directions[vertex_offset + i    ] = vertex_type {0, 0, 0};
+    directions[vertex_offset + i + 1] = to_cartesian_coords(vertex_type {1, 2.0 * M_PI * (j + 1) / angular_partitions, M_PI / 2.0});
+    directions[vertex_offset + i + 2] = to_cartesian_coords(vertex_type {1, 2.0 * M_PI *  j      / angular_partitions, M_PI / 2.0});
+  }
+}
+
+std::array<std::vector<float3>, 2> calculate(
+  const std::vector<float3>& vectors           ,
+  const uint2&               vectors_dimensions,
+  const unsigned             superpixel_size   ,
+  const unsigned             angular_partitions,
+  const bool                 symmetric         )
+{
+  const uint2 superpixel_dimensions {vectors_dimensions.x / superpixel_size, vectors_dimensions.y / superpixel_size};
+  const auto  superpixel_count      = superpixel_dimensions.x * superpixel_dimensions.y;
+
+  thrust::device_vector<float3> vectors_gpu(vectors.size());
+  thrust::copy(vectors.begin(), vectors.end(), vectors_gpu.begin());
+  cudaDeviceSynchronize();
+
+  thrust::device_vector<float> polar_plots_gpu(superpixel_count * angular_partitions);
+  calculate_kernel
+    <float3, float>
+    <<<grid_size_2d(dim3(vectors_dimensions.x, vectors_dimensions.y)), block_size_2d()>>>(
+    vectors_gpu    .data().get(),
+    vectors_dimensions          ,
+    superpixel_dimensions       ,
+    superpixel_size             ,
+    angular_partitions          ,
+    symmetric                   ,
+    polar_plots_gpu.data().get());
+  cudaDeviceSynchronize();
+  
+  for (auto i = 0; i < superpixel_count; i++)
+  {
+    const auto start_iterator = polar_plots_gpu.begin() +  i      * angular_partitions;
+    const auto end_iterator   = polar_plots_gpu.begin() + (i + 1) * angular_partitions;
+    const auto max_weight     = *max_element(start_iterator, end_iterator);
+    thrust::transform(start_iterator, end_iterator, start_iterator, 
+    [=] __host__ __device__ (const float& angular_weight) { return angular_weight / max_weight; });
+  }
+  cudaDeviceSynchronize();
+  
+  thrust::device_vector<float3> vertices_gpu  (3 * superpixel_count * angular_partitions);
+  thrust::device_vector<float3> directions_gpu(3 * superpixel_count * angular_partitions);
+  generate_kernel
+    <float, float3, float3>
+    <<<grid_size_2d(dim3(superpixel_dimensions.x, superpixel_dimensions.y)), block_size_2d()>>>(
+    polar_plots_gpu.data().get(),
+    superpixel_dimensions       ,
+    superpixel_size             ,
+    angular_partitions          ,
+    vertices_gpu   .data().get(),
+    directions_gpu .data().get());
+  cudaDeviceSynchronize();
+  
+  std::array<std::vector<float3>, 2> render_data;
+  render_data[0].resize(vertices_gpu  .size());
+  render_data[1].resize(directions_gpu.size());
+  thrust::copy(vertices_gpu  .begin(), vertices_gpu  .end(), render_data[0].begin());
+  thrust::copy(directions_gpu.begin(), directions_gpu.end(), render_data[1].begin());
+  cudaDeviceSynchronize();
+  return render_data;
+}
+}
diff --git a/source/cuda/pt/tracer.cu b/source/cuda/pt/tracer.cu
new file mode 100644
index 0000000000000000000000000000000000000000..238f354a82082f95b1de9c458b489bb463382a83
--- /dev/null
+++ b/source/cuda/pt/tracer.cu
@@ -0,0 +1,85 @@
+#include <pli_vis/cuda/pt/tracer.h>
+
+#include <thrust/device_vector.h>
+#include <device_launch_parameters.h>
+#include <host_defines.h>
+
+#include <pli_vis/cuda/pt/runge_kutta_4_integrator.h>
+#include <pli_vis/cuda/pt/trilinear_interpolator.h>
+#include <pli_vis/cuda/sh/launch.h>
+
+namespace cupt
+{
+// Call on a seed_size 1D grid.
+template<typename data_type, typename integrator_type>
+__global__ void trace_kernel(
+  const std::size_t iteration_count,
+  const float       step_size      ,
+  const uint3       data_dimensions,
+  const float3      data_spacing   ,
+  const data_type*  data           ,
+  const std::size_t seed_size      ,
+  const data_type*  seeds          ,
+        data_type*  traces         )
+{
+  const auto index = blockIdx.x * blockDim.x + threadIdx.x;
+  if (index >= seed_size) return;
+
+  traces[index * iteration_count] = seeds[index];
+  integrator_type integrator(data_dimensions, data_spacing, data);
+  typename integrator_type::integration_step step(seeds[index], step_size);
+  for (auto iteration = 1; iteration < iteration_count; ++iteration)
+  {
+    integrator.compute_step(step);
+    if (step.done())
+    {
+      traces[index * iteration_count + iteration] = step.end_point;
+      step.restart_at(step.end_point);
+    }
+    else
+    {
+      step.end_point = step.start_point;
+      break;
+    }
+  }
+}
+
+std::vector<std::vector<float3>> trace(
+  const std::size_t          iteration_count,
+  const float                step_size      ,
+  const uint3                data_dimensions,
+  const float3               data_spacing   ,
+  const std::vector<float3>& data           ,
+  const std::vector<float3>& seeds          )
+{
+  thrust::device_vector<float3> data_gpu   = data ;
+  thrust::device_vector<float3> seeds_gpu  = seeds;
+  thrust::device_vector<float3> traces_gpu(iteration_count * seeds.size(), float3{0.0f, 0.0f, 0.0f});
+  const auto data_gpu_ptr   = raw_pointer_cast(&data_gpu  [0]);
+  const auto seeds_gpu_ptr  = raw_pointer_cast(&seeds_gpu [0]);
+  const auto traces_gpu_ptr = raw_pointer_cast(&traces_gpu[0]);
+  cudaDeviceSynchronize();
+
+  trace_kernel
+    <float3, runge_kutta_4_integrator<float3, trilinear_interpolator<float3>>>
+    <<<pli::grid_size_1d(seeds.size()), pli::block_size_1d()>>>(
+    iteration_count,
+    step_size      ,
+    data_dimensions,
+    data_spacing   ,
+    data_gpu_ptr   ,
+    seeds.size()   ,
+    seeds_gpu_ptr  ,
+    traces_gpu_ptr );
+  cudaDeviceSynchronize();
+
+  std::vector<float3> traces_linear(traces_gpu.size());
+  thrust::copy (traces_gpu.begin(), traces_gpu.end (), traces_linear.begin());
+  cudaDeviceSynchronize();
+
+  std::vector<std::vector<float3>> traces(seeds.size());
+  for(auto i = 0; i < traces.size(); ++i)
+    traces[i] = std::vector<float3>(traces_linear.begin() + i * iteration_count, traces_linear.begin() + (i + 1) * iteration_count);
+  return traces;
+}
+}
\ No newline at end of file
diff --git a/source/cuda/vector_field.cu b/source/cuda/vector_field.cu
deleted file mode 100644
index 67ecd0109553881446be7b30f0e0c42289a28b93..0000000000000000000000000000000000000000
--- a/source/cuda/vector_field.cu
+++ /dev/null
@@ -1,82 +0,0 @@
-#include /* implements */ <cuda/vector_field.h>
-
-#include <chrono>
-#include <iostream>
-
-#include <thrust/device_vector.h>
-
-#include <sh/launch.h>
-
-namespace pli
-{
-void create_vector_field(
-  const uint3&  dimensions  ,
-  const float*  directions  ,
-  const float*  inclinations,
-  const float3& spacing     ,
-  const float&  scale       ,
-        float3* points      ,
-        float4* colors      ,
-  std::function<void(const std::string&)> status_callback)
-{
-  auto total_start = std::chrono::system_clock::now();
-
-  status_callback("Allocating and copying directions and inclinations.");
-  auto voxel_count = dimensions.x * dimensions.y * dimensions.z;
-  thrust::device_vector<float> directions_vector  (voxel_count);
-  thrust::device_vector<float> inclinations_vector(voxel_count);
-  copy_n(directions  , voxel_count, directions_vector  .begin());
-  copy_n(inclinations, voxel_count, inclinations_vector.begin());
-  auto directions_ptr   = raw_pointer_cast(&directions_vector  [0]);
-  auto inclinations_ptr = raw_pointer_cast(&inclinations_vector[0]);
-  cudaDeviceSynchronize();
-  
-  status_callback("Creating vectors.");
-  create_vector_field_internal<<<cush::grid_size_3d(dimensions), cush::block_size_3d()>>>(
-    dimensions      , 
-    directions_ptr  , 
-    inclinations_ptr,
-    spacing         , 
-    scale           , 
-    points          ,
-    colors          );
-  cudaDeviceSynchronize();
-
-  auto total_end = std::chrono::system_clock::now();
-  std::chrono::duration<double> total_elapsed_seconds = total_end - total_start;
-  status_callback("Cuda operations took " + std::to_string(total_elapsed_seconds.count()) + " seconds.");
-}
-
-void create_vector_field(
-  const uint3&  dimensions  , 
-  const float3* unit_vectors, 
-  const float3& spacing     , 
-  const float&  scale       , 
-  float3*       points      , 
-  float4*       colors      , 
-  std::function<void(const std::string&)> status_callback)
-{
-  auto total_start = std::chrono::system_clock::now();
-  
-  status_callback("Allocating and copying directions and inclinations.");
-  auto voxel_count = dimensions.x * dimensions.y * dimensions.z;
-  thrust::device_vector<float3> unit_vectors_vector(voxel_count);
-  copy_n(unit_vectors, voxel_count, unit_vectors_vector.begin());
-  auto unit_vectors_ptr = raw_pointer_cast(&unit_vectors_vector[0]);
-  cudaDeviceSynchronize();
-  
-  status_callback("Creating vectors.");
-  create_vector_field_internal<<<cush::grid_size_3d(dimensions), cush::block_size_3d()>>>(
-    dimensions      , 
-    unit_vectors_ptr,
-    spacing         , 
-    scale           , 
-    points          ,
-    colors          );
-  cudaDeviceSynchronize();
-
-  auto total_end = std::chrono::system_clock::now();
-  std::chrono::duration<double> total_elapsed_seconds = total_end - total_start;
-  status_callback("Cuda operations took " + std::to_string(total_elapsed_seconds.count()) + " seconds.");
-}
-}
diff --git a/source/cuda/zernike/launch.cu b/source/cuda/zernike/launch.cu
new file mode 100644
index 0000000000000000000000000000000000000000..e2d6c28d352518172782def3124a200c1729c005
--- /dev/null
+++ b/source/cuda/zernike/launch.cu
@@ -0,0 +1,328 @@
+#include <pli_vis/cuda/zernike/launch.h>
+
+#include <cublas_v2.h>
+#include <cusolverDn.h>
+#include <thrust/extrema.h>
+
+#include <pli_vis/cuda/utility/vector_ops.h>
+#include <pli_vis/cuda/zernike/disk.h>
+#include <pli_vis/cuda/zernike/zernike.h>
+
+namespace zer
+{
+__host__ thrust::device_vector<float> pseudoinverse(const uint2& size, thrust::device_vector<float>& data)
+{
+  cublasHandle_t     cublas  ;
+  cusolverDnHandle_t cusolver;
+  cusolverDnCreate(&cusolver);
+  cublasCreate    (&cublas  );
+
+  int buffer_size;
+  cusolverDnSgesvd_bufferSize(cusolver, size.x, size.y, &buffer_size);
+  cudaDeviceSynchronize      ();
+  auto complex_buffer_size = static_cast<float>(buffer_size);
+  
+  thrust::device_vector<float> buffer (buffer_size, 0.0);
+  thrust::device_vector<int>   info   (1);
+  thrust::device_vector<float> u      (size.x * size.x);
+  thrust::device_vector<float> e      (size.y);
+  thrust::device_vector<float> vt     (size.y * size.y);
+  thrust::device_vector<float> ut     (size.x * size.x);
+  thrust::device_vector<float> ei_ut  (size.x * size.y);
+  thrust::device_vector<float> v_ei_ut(size.x * size.y);
+  auto alpha = 1.0F;
+  auto beta  = 0.0F;
+  
+  cusolverDnSgesvd(
+    cusolver            ,
+    'A'                 ,
+    'A'                 ,
+    size.x              ,
+    size.y              ,
+    data.data().get()   ,
+    size.x              ,
+    e.data().get()      ,
+    u.data().get()      ,
+    size.x              ,
+    vt.data().get()     ,
+    size.y              ,
+    buffer.data().get() ,
+    buffer_size         ,
+    &complex_buffer_size,
+    info.data().get()   );
+  cudaDeviceSynchronize();
+  buffer.clear();
+
+  cublasSgeam(
+    cublas         ,
+    CUBLAS_OP_T    ,
+    CUBLAS_OP_N    ,
+    size.x         ,
+    size.x         ,
+    &alpha         ,
+    u.data().get() ,
+    size.x         ,
+    &beta          ,
+    nullptr        ,
+    size.x         ,
+    ut.data().get(),
+    size.x         );
+  cudaDeviceSynchronize();
+  u.clear();
+  
+  thrust::transform(
+    e.begin(),
+    e.end  (),
+    e.begin(),
+    [] __host__ __device__(float& entry) -> float
+    {
+      if (int(entry) == 0)
+        return 0;
+      return entry = 1.0F / entry;
+    });
+  cudaDeviceSynchronize();
+
+  cublasSdgmm(
+    cublas            ,
+    CUBLAS_SIDE_LEFT  ,
+    size.y            ,
+    size.x            ,
+    ut.data().get()   ,
+    size.x            ,
+    e.data().get()    ,
+    1                 ,
+    ei_ut.data().get(),
+    size.y            );
+  cudaDeviceSynchronize();
+  ut.clear();
+  e .clear();
+
+  cublasSgemm(
+    cublas              ,
+    CUBLAS_OP_T         ,
+    CUBLAS_OP_N         ,
+    size.y              ,
+    size.x              ,            
+    size.y              ,
+    &alpha              ,
+    vt.data().get()     ,
+    size.y              ,
+    ei_ut.data().get()  ,
+    size.y              ,
+    &beta               ,
+    v_ei_ut.data().get(),
+    size.y              );
+  cudaDeviceSynchronize();
+  vt   .clear();
+  ei_ut.clear();
+
+  cusolverDnDestroy(cusolver);
+  cublasDestroy    (cublas  );
+
+  return v_ei_ut;
+}
+
+__global__ void accumulate(
+  const uint2    vectors_size         ,
+  const float3*  vectors              ,
+  const uint2    disk_partitions      ,
+  const float2*  disk_samples         ,
+  const uint2    superpixel_size      ,
+  const uint2    superpixel_dimensions,
+        float*   intermediates        ,
+        bool     symmetric            )
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  if (x >= vectors_size.x || y >= vectors_size.y)
+    return;
+
+  const auto& vector = vectors[y + vectors_size.y * x];
+
+  const auto superpixel_x        = x / superpixel_size.x;
+  const auto superpixel_y        = y / superpixel_size.y;
+  const auto superpixel_index    = superpixel_y + superpixel_dimensions.y * superpixel_x;
+  const auto sample_count        = disk_partitions.x * disk_partitions.y;
+  const auto intermediate_offset = sample_count * superpixel_index;
+
+  auto min_distance = 2.0F;
+  auto min_index    = 0   ;
+  for(auto i = 0; i < sample_count; i++)
+  {
+    const auto temp_distance = sqrt(pow(cos(vector.z), 2) + pow(disk_samples[i].x, 2) - 2.0F * cos(vector.z) * disk_samples[i].x * cos(vector.y - disk_samples[i].y));
+    if (temp_distance < min_distance)
+    {
+      min_distance = temp_distance;
+      min_index    = i;
+    }
+  }
+  atomicAdd(&intermediates[intermediate_offset + min_index], 1.0f);
+  
+  // Change this to Freitag's method of sampling points in proportion to the current radius.
+  if(symmetric)
+  {
+    auto symmetric_distance = 2.0F;
+    auto symmetric_index    = 0   ;
+    for(auto i = 0; i < sample_count; i++)
+    {
+      const auto temp_distance = sqrt(pow(disk_samples[min_index].x, 2) + pow(disk_samples[i].x, 2) - 2.0F * disk_samples[min_index].x * disk_samples[i].x * cos((disk_samples[min_index].y + M_PI) - disk_samples[i].y));
+      if (temp_distance < symmetric_distance)
+      {
+        symmetric_distance = temp_distance;
+        symmetric_index    = i;
+      }
+    }
+    atomicAdd(&intermediates[intermediate_offset + symmetric_index], -1.0f);
+  }
+}
+
+__global__ void project(
+  const uint2    dimensions           ,
+  const float*   basis_matrix         ,
+  const unsigned sample_count         ,
+  const float*   intermediates        ,
+  const unsigned coefficient_count    ,
+        float*   coefficients         )
+{
+  const auto x = blockIdx.x * blockDim.x + threadIdx.x;
+  const auto y = blockIdx.y * blockDim.y + threadIdx.y;
+  if (x >= dimensions.x || y >= dimensions.y)
+    return;
+
+  const auto linear_index        = y + dimensions.y * x;
+  const auto intermediate_offset = sample_count      * linear_index;
+  const auto coefficient_offset  = coefficient_count * linear_index;
+
+  cublasHandle_t cublas;
+  cublasCreate(&cublas);
+  auto alpha = 1.0F;
+  auto beta  = 0.0F;
+  cublasSgemv(
+    cublas                             ,
+    CUBLAS_OP_N                        ,
+    coefficient_count                  ,
+    sample_count                       ,
+    &alpha                             ,
+    basis_matrix                       ,
+    coefficient_count                  ,
+    &intermediates[intermediate_offset],
+    1                                  ,
+    &beta                              ,
+    &coefficients [coefficient_offset ],
+    1                                  );
+  cublasDestroy(cublas);
+}
+
+thrust::device_vector<float> launch(
+  const thrust::device_vector<float3>& vectors        ,
+  const uint2&                         vectors_size   ,
+  const uint2&                         superpixel_size,
+  const uint2&                         disk_partitions,
+  const unsigned                       maximum_degree ,
+  bool                                 symmetric      ,
+  bool                                 normalize      ,
+  bool                                 even_only      ,
+  bool                                 edge_only      )
+{
+  const auto superpixel_dimensions = uint2{vectors_size.x / superpixel_size.x, vectors_size.y / superpixel_size.y};
+  const auto superpixel_count      = superpixel_dimensions.x * superpixel_dimensions.y;
+  const auto sample_count          = disk_partitions.x * disk_partitions.y;
+  const auto coefficient_count     = expansion_size(maximum_degree);
+
+  // Sample a unit disk.
+  thrust::device_vector<float2> disk_samples(sample_count);
+  sample_disk<<<grid_size_2d(dim3(disk_partitions.x, disk_partitions.y)), block_size_2d()>>>(
+    disk_partitions          , 
+    disk_samples.data().get(),
+    false                    );
+  cudaDeviceSynchronize();
+
+  // Compute Zernike basis for the samples.
+  thrust::device_vector<float> basis_matrix(sample_count * coefficient_count);
+  compute_basis<<<grid_size_2d(dim3(sample_count, coefficient_count)), block_size_2d()>>>(
+    sample_count              , 
+    disk_samples.data().get() , 
+    coefficient_count         ,
+    basis_matrix.data().get() ,
+    even_only                 ,
+    edge_only                 );
+  cudaDeviceSynchronize();
+
+  // Compute the inverse of the basis matrix.
+  auto inverse_basis_matrix = pseudoinverse({sample_count, coefficient_count}, basis_matrix);
+  
+  // Accumulate vectors into superpixels.
+  thrust::device_vector<float> intermediates(superpixel_count * sample_count);
+  accumulate<<<grid_size_2d(dim3(vectors_size.x, vectors_size.y)), block_size_2d()>>> (
+    vectors_size              ,
+    vectors      .data().get(),
+    disk_partitions           ,
+    disk_samples .data().get(),
+    superpixel_size           ,
+    superpixel_dimensions     ,
+    intermediates.data().get(),
+    symmetric                 );
+  cudaDeviceSynchronize();
+
+  // Project superpixels to the Zernike basis.
+  thrust::device_vector<float> coefficients(superpixel_count * coefficient_count);
+  project<<<grid_size_2d(dim3(superpixel_dimensions.x, superpixel_dimensions.y)), block_size_2d()>>> (
+    superpixel_dimensions            ,
+    inverse_basis_matrix.data().get(),
+    sample_count                     ,
+    intermediates.data().get()       ,
+    coefficient_count                ,
+    coefficients.data().get())       ;
+  cudaDeviceSynchronize();
+
+  // Normalize.
+  if(normalize)
+  {
+    for(auto i = 0; i < superpixel_count; i++)
+    {
+      const auto start_iterator  = coefficients.begin() +  i      * coefficient_count;
+      const auto end_iterator    = coefficients.begin() + (i + 1) * coefficient_count;
+      const auto max_coefficient = abs(*max_element(start_iterator, end_iterator, 
+      [=] __host__ __device__(float lhs, float rhs)
+      {
+        return std::abs(lhs) < std::abs(rhs);
+      }));
+      thrust::transform(start_iterator, end_iterator, start_iterator, 
+      [=] __host__ __device__(const float& coefficient)
+      {
+        return coefficient / max_coefficient;
+      });
+    }
+  }
+
+  return coefficients;
+}
+
+std::vector<float> launch(
+  const std::vector<float3>&           vectors        ,
+  const uint2&                         vectors_size   ,
+  const uint2&                         superpixel_size,
+  const uint2&                         disk_partitions,
+  const unsigned                       maximum_degree ,
+  bool                                 symmetric      ,
+  bool                                 normalize      ,
+  bool                                 even_only      ,
+  bool                                 edge_only      )
+{
+  thrust::device_vector<float3> vectors_gpu(vectors.size());
+  thrust::copy(vectors.begin(), vectors.end(), vectors_gpu.begin());
+  auto coefficients = zer::launch(
+    vectors_gpu    ,
+    vectors_size   ,
+    superpixel_size,
+    disk_partitions,
+    maximum_degree ,
+    symmetric      ,
+    normalize      ,
+    even_only      ,
+    edge_only      );
+  std::vector<float> coefficients_cpu(coefficients.size());
+  thrust::copy(coefficients.begin(), coefficients.end(), coefficients_cpu.begin());
+  return coefficients_cpu;
+}
+}
diff --git a/source/main.cpp b/source/main.cpp
index 4cc1d26ee5f872c7d88bc45e441ad9385a7eb782..891feb18c15e0cc4ff9be3e4a01fb3e1baa8660b 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -6,12 +6,16 @@ extern "C"
 }
 #endif
 
+#include <omp.h>
 #include <QApplication>
+#include <QSurfaceFormat>
 
-#include <ui/window.hpp>
+#include <pli_vis/ui/application.hpp>
 
 int main(int argc, char** argv)
 {
+  omp_set_num_threads(4);
+
   QSurfaceFormat format;
   format.setProfile     (QSurfaceFormat::CompatibilityProfile);
   format.setSwapBehavior(QSurfaceFormat::DoubleBuffer        );
@@ -21,7 +25,7 @@ int main(int argc, char** argv)
 
   QApplication application(argc, argv);
   
-  pli::window window;
+  pli::application window;
   
   application.exec();
 
diff --git a/source/third_party/glew/glew.c b/source/third_party/glew/glew.c
index f5e695f0770e26da223b3b2323f0fab1522bfef4..b52af01bd55d39712c41dd4a297ac851a6e92d53 100644
--- a/source/third_party/glew/glew.c
+++ b/source/third_party/glew/glew.c
@@ -30,17 +30,17 @@
 ** THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#include <third_party/glew/GL/glew.h>
+#include <glew/GL/glew.h>
 
 #if defined(GLEW_OSMESA)
 #  define GLAPI extern
 #  include <GL/osmesa.h>
 #elif defined(GLEW_EGL)
-#  include <third_party/glew/GL/eglew.h>
+#  include <glew/GL/eglew.h>
 #elif defined(_WIN32)
-#  include <third_party/glew/GL/wglew.h>
+#  include <glew/GL/wglew.h>
 #elif !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && (!defined(__APPLE__) || defined(GLEW_APPLE_GLX))
-#  include <third_party/glew/GL/glxew.h>
+#  include <glew/GL/glxew.h>
 #endif
 
 #include <stddef.h>  /* For size_t */
@@ -16928,7 +16928,7 @@ GLboolean __GLXEW_SGIX_swap_barrier = GL_FALSE;
 GLboolean __GLXEW_SGIX_swap_group = GL_FALSE;
 GLboolean __GLXEW_SGIX_video_resize = GL_FALSE;
 GLboolean __GLXEW_SGIX_visual_select_group = GL_FALSE;
-GLboolean __GLXEW_SGI_cushion = GL_FALSE;
+GLboolean __GLXEW_SGI_pliion = GL_FALSE;
 GLboolean __GLXEW_SGI_make_current_read = GL_FALSE;
 GLboolean __GLXEW_SGI_swap_control = GL_FALSE;
 GLboolean __GLXEW_SGI_video_sync = GL_FALSE;
@@ -17400,9 +17400,9 @@ static GLboolean _glewInit_GLX_SGIX_video_resize ()
 
 #endif /* GLX_SGIX_video_resize */
 
-#ifdef GLX_SGI_cushion
+#ifdef GLX_SGI_pliion
 
-static GLboolean _glewInit_GLX_SGI_cushion ()
+static GLboolean _glewInit_GLX_SGI_pliion ()
 {
   GLboolean r = GL_FALSE;
 
@@ -17411,7 +17411,7 @@ static GLboolean _glewInit_GLX_SGI_cushion ()
   return r;
 }
 
-#endif /* GLX_SGI_cushion */
+#endif /* GLX_SGI_pliion */
 
 #ifdef GLX_SGI_make_current_read
 
@@ -17748,10 +17748,10 @@ GLenum glxewInit ()
 #ifdef GLX_SGIX_visual_select_group
   GLXEW_SGIX_visual_select_group = _glewSearchExtension("GLX_SGIX_visual_select_group", extStart, extEnd);
 #endif /* GLX_SGIX_visual_select_group */
-#ifdef GLX_SGI_cushion
-  GLXEW_SGI_cushion = _glewSearchExtension("GLX_SGI_cushion", extStart, extEnd);
-  if (glewExperimental || GLXEW_SGI_cushion) GLXEW_SGI_cushion = !_glewInit_GLX_SGI_cushion();
-#endif /* GLX_SGI_cushion */
+#ifdef GLX_SGI_pliion
+  GLXEW_SGI_pliion = _glewSearchExtension("GLX_SGI_pliion", extStart, extEnd);
+  if (glewExperimental || GLXEW_SGI_pliion) GLXEW_SGI_pliion = !_glewInit_GLX_SGI_pliion();
+#endif /* GLX_SGI_pliion */
 #ifdef GLX_SGI_make_current_read
   GLXEW_SGI_make_current_read = _glewSearchExtension("GLX_SGI_make_current_read", extStart, extEnd);
   if (glewExperimental || GLXEW_SGI_make_current_read) GLXEW_SGI_make_current_read = !_glewInit_GLX_SGI_make_current_read();
@@ -23102,10 +23102,10 @@ GLboolean glxewIsSupported (const char* name)
       }
       if (_glewStrSame2(&pos, &len, (const GLubyte*)"SGI_", 4))
       {
-#ifdef GLX_SGI_cushion
-        if (_glewStrSame3(&pos, &len, (const GLubyte*)"cushion", 7))
+#ifdef GLX_SGI_pliion
+        if (_glewStrSame3(&pos, &len, (const GLubyte*)"pliion", 7))
         {
-          ret = GLXEW_SGI_cushion;
+          ret = GLXEW_SGI_pliion;
           continue;
         }
 #endif
diff --git a/source/third_party/qwt/qwt_abstract_legend.cpp b/source/third_party/qwt/qwt_abstract_legend.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fb4f9e36337575d641f1f759abfee2cecf3cb441
--- /dev/null
+++ b/source/third_party/qwt/qwt_abstract_legend.cpp
@@ -0,0 +1,38 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_abstract_legend.h"
+
+/*!
+  Constructor
+
+  \param parent Parent widget
+*/
+QwtAbstractLegend::QwtAbstractLegend( QWidget *parent ):
+    QFrame( parent )
+{
+}
+
+//! Destructor
+QwtAbstractLegend::~QwtAbstractLegend()
+{
+}
+
+/*!
+   Return the extent, that is needed for elements to scroll
+   the legend ( usually scrollbars ),
+
+   \param orientation Orientation
+   \return Extent of the corresponding scroll element
+*/
+int QwtAbstractLegend::scrollExtent( Qt::Orientation orientation ) const
+{
+    Q_UNUSED( orientation );
+    return 0;
+}
diff --git a/source/third_party/qwt/qwt_abstract_scale.cpp b/source/third_party/qwt/qwt_abstract_scale.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fae30693417804e0da6a78c042db88e4773141ef
--- /dev/null
+++ b/source/third_party/qwt/qwt_abstract_scale.cpp
@@ -0,0 +1,449 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_abstract_scale.h"
+#include "qwt/qwt_scale_engine.h"
+#include "qwt/qwt_scale_draw.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_interval.h"
+
+class QwtAbstractScale::PrivateData
+{
+public:
+    PrivateData():
+        maxMajor( 5 ),
+        maxMinor( 3 ),
+        stepSize( 0.0 )
+    {
+        scaleEngine = new QwtLinearScaleEngine();
+        scaleDraw = new QwtScaleDraw();
+    }
+
+    ~PrivateData()
+    {
+        delete scaleEngine;
+        delete scaleDraw;
+    }
+
+    QwtScaleEngine *scaleEngine;
+    QwtAbstractScaleDraw *scaleDraw;
+
+    int maxMajor;
+    int maxMinor;
+    double stepSize;
+};
+
+/*!
+  Constructor
+
+  \param parent Parent widget
+
+  Creates a default QwtScaleDraw and a QwtLinearScaleEngine.
+  The initial scale boundaries are set to [ 0.0, 100.0 ]
+
+  The scaleStepSize() is initialized to 0.0, scaleMaxMajor() to 5
+  and scaleMaxMajor to 3.
+*/
+
+QwtAbstractScale::QwtAbstractScale( QWidget *parent ):
+    QWidget( parent )
+{
+    d_data = new PrivateData;
+    rescale( 0.0, 100.0, d_data->stepSize );
+}
+
+//! Destructor
+QwtAbstractScale::~QwtAbstractScale()
+{
+    delete d_data;
+}
+
+/*!
+  Set the lower bound of the scale
+
+  \param value Lower bound
+
+  \sa lowerBound(), setScale(), setUpperBound()
+  \note For inverted scales the lower bound 
+        is greater than the upper bound
+*/
+void QwtAbstractScale::setLowerBound( double value )
+{
+    setScale( value, upperBound() );
+}
+
+/*!
+  \return Lower bound of the scale
+  \sa setLowerBound(), setScale(), upperBound()
+*/
+double QwtAbstractScale::lowerBound() const
+{
+    return d_data->scaleDraw->scaleDiv().lowerBound();
+}
+
+/*!
+  Set the upper bound of the scale
+
+  \param value Upper bound
+
+  \sa upperBound(), setScale(), setLowerBound()
+  \note For inverted scales the lower bound 
+        is greater than the upper bound
+*/
+void QwtAbstractScale::setUpperBound( double value )
+{
+    setScale( lowerBound(), value );
+}
+
+/*!
+  \return Upper bound of the scale
+  \sa setUpperBound(), setScale(), lowerBound()
+*/
+double QwtAbstractScale::upperBound() const
+{
+    return d_data->scaleDraw->scaleDiv().upperBound();
+}
+
+/*!
+  \brief Specify a scale.
+
+  Define a scale by an interval 
+
+  The ticks are calculated using scaleMaxMinor(), 
+  scaleMaxMajor() and scaleStepSize().
+
+  \param lowerBound lower limit of the scale interval
+  \param upperBound upper limit of the scale interval
+
+  \note For inverted scales the lower bound 
+        is greater than the upper bound
+*/
+void QwtAbstractScale::setScale( double lowerBound, double upperBound )
+{
+    rescale( lowerBound, upperBound, d_data->stepSize );
+}
+
+/*!
+  \brief Specify a scale.
+
+  Define a scale by an interval
+
+  The ticks are calculated using scaleMaxMinor(), 
+  scaleMaxMajor() and scaleStepSize().
+
+  \param interval Interval
+*/
+void QwtAbstractScale::setScale( const QwtInterval &interval )
+{
+    setScale( interval.minValue(), interval.maxValue() );
+}
+
+/*!
+  \brief Specify a scale.
+
+  scaleMaxMinor(), scaleMaxMajor() and scaleStepSize() and have no effect.
+
+  \param scaleDiv Scale division
+  \sa setAutoScale()
+*/
+void QwtAbstractScale::setScale( const QwtScaleDiv &scaleDiv )
+{
+    if ( scaleDiv != d_data->scaleDraw->scaleDiv() )
+    {
+#if 1
+        if ( d_data->scaleEngine )
+        {
+            d_data->scaleDraw->setTransformation(
+                d_data->scaleEngine->transformation() );
+        }
+#endif
+
+        d_data->scaleDraw->setScaleDiv( scaleDiv );
+
+        scaleChange();
+    }
+}
+
+/*!
+  \brief Set the maximum number of major tick intervals.
+
+  The scale's major ticks are calculated automatically such that
+  the number of major intervals does not exceed ticks.
+
+  The default value is 5.
+
+  \param ticks Maximal number of major ticks.
+
+  \sa scaleMaxMajor(), setScaleMaxMinor(),
+      setScaleStepSize(), QwtScaleEngine::divideInterval()
+*/
+void QwtAbstractScale::setScaleMaxMajor( int ticks )
+{
+    if ( ticks != d_data->maxMajor )
+    {
+        d_data->maxMajor = ticks;
+        updateScaleDraw();
+    }
+}
+
+/*!
+  \return Maximal number of major tick intervals
+  \sa setScaleMaxMajor(), scaleMaxMinor()
+*/
+int QwtAbstractScale::scaleMaxMajor() const
+{
+    return d_data->maxMajor;
+}
+
+/*!
+  \brief Set the maximum number of minor tick intervals
+
+  The scale's minor ticks are calculated automatically such that
+  the number of minor intervals does not exceed ticks.
+  The default value is 3.
+
+  \param ticks Maximal number of minor ticks.
+
+  \sa scaleMaxMajor(), setScaleMaxMinor(),
+      setScaleStepSize(), QwtScaleEngine::divideInterval()
+*/
+void QwtAbstractScale::setScaleMaxMinor( int ticks )
+{
+    if ( ticks != d_data->maxMinor )
+    {
+        d_data->maxMinor = ticks;
+        updateScaleDraw();
+    }
+}
+
+/*!
+  \return Maximal number of minor tick intervals
+  \sa setScaleMaxMinor(), scaleMaxMajor()
+*/
+int QwtAbstractScale::scaleMaxMinor() const
+{
+    return d_data->maxMinor;
+}
+
+/*!
+   \brief Set the step size used for calculating a scale division
+
+   The step size is hint for calculating the intervals for
+   the major ticks of the scale. A value of 0.0 is interpreted
+   as no hint.
+
+   \param stepSize Hint for the step size of the scale
+
+   \sa scaleStepSize(), QwtScaleEngine::divideScale()
+
+   \note Position and distance between the major ticks also
+         depends on scaleMaxMajor().
+*/
+void QwtAbstractScale::setScaleStepSize( double stepSize )
+{
+    if ( stepSize != d_data->stepSize )
+    {
+        d_data->stepSize = stepSize;
+        updateScaleDraw();
+    }
+}
+
+/*!
+  \return Hint for the step size of the scale
+  \sa setScaleStepSize(), QwtScaleEngine::divideScale()
+*/
+double QwtAbstractScale::scaleStepSize() const
+{
+    return d_data->stepSize;
+}
+
+/*!
+  \brief Set a scale draw
+
+  scaleDraw has to be created with new and will be deleted in
+  the destructor or the next call of setAbstractScaleDraw().
+
+  \sa abstractScaleDraw()
+*/
+void QwtAbstractScale::setAbstractScaleDraw( QwtAbstractScaleDraw *scaleDraw )
+{
+    if ( scaleDraw == NULL || scaleDraw == d_data->scaleDraw )
+        return;
+
+    if ( d_data->scaleDraw != NULL )
+        scaleDraw->setScaleDiv( d_data->scaleDraw->scaleDiv() );
+
+    delete d_data->scaleDraw;
+    d_data->scaleDraw = scaleDraw;
+}
+
+/*!
+    \return Scale draw
+    \sa setAbstractScaleDraw()
+*/
+QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw()
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+    \return Scale draw
+    \sa setAbstractScaleDraw()
+*/
+const QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw() const
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+  \brief Set a scale engine
+
+  The scale engine is responsible for calculating the scale division
+  and provides a transformation between scale and widget coordinates.
+
+  scaleEngine has to be created with new and will be deleted in
+  the destructor or the next call of setScaleEngine.
+*/
+void QwtAbstractScale::setScaleEngine( QwtScaleEngine *scaleEngine )
+{
+    if ( scaleEngine != NULL && scaleEngine != d_data->scaleEngine )
+    {
+        delete d_data->scaleEngine;
+        d_data->scaleEngine = scaleEngine;
+    }
+}
+
+/*!
+  \return Scale engine
+  \sa setScaleEngine()
+*/
+const QwtScaleEngine *QwtAbstractScale::scaleEngine() const
+{
+    return d_data->scaleEngine;
+}
+
+/*!
+  \return Scale engine
+  \sa setScaleEngine()
+*/
+QwtScaleEngine *QwtAbstractScale::scaleEngine()
+{
+    return d_data->scaleEngine;
+}
+
+/*!
+  \return Scale boundaries and positions of the ticks
+
+  The scale division might have been assigned explicitly
+  or calculated implicitly by rescale(). 
+ */
+const QwtScaleDiv &QwtAbstractScale::scaleDiv() const
+{
+    return d_data->scaleDraw->scaleDiv();
+}
+
+/*!
+  \return Map to translate between scale and widget coordinates
+ */
+const QwtScaleMap &QwtAbstractScale::scaleMap() const
+{
+    return d_data->scaleDraw->scaleMap();
+}
+
+/*!
+  Translate a scale value into a widget coordinate
+
+  \param value Scale value 
+  \return Corresponding widget coordinate for value
+  \sa scaleMap(), invTransform()
+ */
+int QwtAbstractScale::transform( double value ) const
+{
+    return qRound( d_data->scaleDraw->scaleMap().transform( value ) );
+}
+
+/*!
+  Translate a widget coordinate into a scale value
+
+  \param value Widget coordinate
+  \return Corresponding scale coordinate for value
+  \sa scaleMap(), transform()
+ */
+double QwtAbstractScale::invTransform( int value ) const
+{
+    return d_data->scaleDraw->scaleMap().invTransform( value );
+}
+
+/*!
+  \return True, when the scale is increasing in opposite direction
+          to the widget coordinates
+ */
+bool QwtAbstractScale::isInverted() const
+{
+    return d_data->scaleDraw->scaleMap().isInverting();
+}
+
+/*!
+  \return The boundary with the smaller value
+  \sa maximum(), lowerBound(), upperBound()
+ */
+double QwtAbstractScale::minimum() const
+{
+    return qMin( d_data->scaleDraw->scaleDiv().lowerBound(),
+        d_data->scaleDraw->scaleDiv().upperBound() );
+}
+
+/*!
+  \return The boundary with the larger value
+  \sa minimum(), lowerBound(), upperBound()
+ */
+double QwtAbstractScale::maximum() const
+{
+    return qMax( d_data->scaleDraw->scaleDiv().lowerBound(),
+        d_data->scaleDraw->scaleDiv().upperBound() );
+}
+
+//! Notify changed scale
+void QwtAbstractScale::scaleChange()
+{
+}
+
+/*!
+  Recalculate the scale division and update the scale.
+
+  \param lowerBound Lower limit of the scale interval
+  \param upperBound Upper limit of the scale interval
+  \param stepSize Major step size
+
+  \sa scaleChange()
+*/
+void QwtAbstractScale::rescale( 
+    double lowerBound, double upperBound, double stepSize )
+{
+    const QwtScaleDiv scaleDiv = d_data->scaleEngine->divideScale(
+        lowerBound, upperBound, d_data->maxMajor, d_data->maxMinor, stepSize );
+
+    if ( scaleDiv != d_data->scaleDraw->scaleDiv() )
+    {
+#if 1
+        d_data->scaleDraw->setTransformation(
+            d_data->scaleEngine->transformation() );
+#endif
+
+        d_data->scaleDraw->setScaleDiv( scaleDiv );
+        scaleChange();
+    }
+}
+
+void QwtAbstractScale::updateScaleDraw()
+{
+    rescale( d_data->scaleDraw->scaleDiv().lowerBound(),
+        d_data->scaleDraw->scaleDiv().upperBound(), d_data->stepSize );
+}
diff --git a/source/third_party/qwt/qwt_abstract_scale_draw.cpp b/source/third_party/qwt/qwt_abstract_scale_draw.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..743b4c7903a4a28fbedc4cdab4a803215c85cb95
--- /dev/null
+++ b/source/third_party/qwt/qwt_abstract_scale_draw.cpp
@@ -0,0 +1,420 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_abstract_scale_draw.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_map.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qmap.h>
+#include <qlocale.h>
+
+class QwtAbstractScaleDraw::PrivateData
+{
+public:
+    PrivateData():
+        spacing( 4.0 ),
+        penWidth( 0 ),
+        minExtent( 0.0 )
+    {
+        components = QwtAbstractScaleDraw::Backbone 
+            | QwtAbstractScaleDraw::Ticks 
+            | QwtAbstractScaleDraw::Labels;
+
+        tickLength[QwtScaleDiv::MinorTick] = 4.0;
+        tickLength[QwtScaleDiv::MediumTick] = 6.0;
+        tickLength[QwtScaleDiv::MajorTick] = 8.0;
+    }
+
+    ScaleComponents components;
+
+    QwtScaleMap map;
+    QwtScaleDiv scaleDiv;
+
+    double spacing;
+    double tickLength[QwtScaleDiv::NTickTypes];
+    int penWidth;
+
+    double minExtent;
+
+    QMap<double, QwtText> labelCache;
+};
+
+/*!
+  \brief Constructor
+
+  The range of the scale is initialized to [0, 100],
+  The spacing (distance between ticks and labels) is
+  set to 4, the tick lengths are set to 4,6 and 8 pixels
+*/
+QwtAbstractScaleDraw::QwtAbstractScaleDraw()
+{
+    d_data = new QwtAbstractScaleDraw::PrivateData;
+}
+
+//! Destructor
+QwtAbstractScaleDraw::~QwtAbstractScaleDraw()
+{
+    delete d_data;
+}
+
+/*!
+  En/Disable a component of the scale
+
+  \param component Scale component
+  \param enable On/Off
+
+  \sa hasComponent()
+*/
+void QwtAbstractScaleDraw::enableComponent(
+    ScaleComponent component, bool enable )
+{
+    if ( enable )
+        d_data->components |= component;
+    else
+        d_data->components &= ~component;
+}
+
+/*!
+  Check if a component is enabled
+
+  \param component Component type
+  \return true, when component is enabled
+  \sa enableComponent()
+*/
+bool QwtAbstractScaleDraw::hasComponent( ScaleComponent component ) const
+{
+    return ( d_data->components & component );
+}
+
+/*!
+  Change the scale division
+  \param scaleDiv New scale division
+*/
+void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &scaleDiv )
+{
+    d_data->scaleDiv = scaleDiv;
+    d_data->map.setScaleInterval( scaleDiv.lowerBound(), scaleDiv.upperBound() );
+    d_data->labelCache.clear();
+}
+
+/*!
+  Change the transformation of the scale
+  \param transformation New scale transformation
+*/
+void QwtAbstractScaleDraw::setTransformation(
+    QwtTransform *transformation )
+{
+    d_data->map.setTransformation( transformation );
+}
+
+//! \return Map how to translate between scale and pixel values
+const QwtScaleMap &QwtAbstractScaleDraw::scaleMap() const
+{
+    return d_data->map;
+}
+
+//! \return Map how to translate between scale and pixel values
+QwtScaleMap &QwtAbstractScaleDraw::scaleMap()
+{
+    return d_data->map;
+}
+
+//! \return scale division
+const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const
+{
+    return d_data->scaleDiv;
+}
+
+/*!
+  \brief Specify the width of the scale pen
+  \param width Pen width
+  \sa penWidth()
+*/
+void QwtAbstractScaleDraw::setPenWidth( int width )
+{
+    if ( width < 0 )
+        width = 0;
+
+    if ( width != d_data->penWidth )
+        d_data->penWidth = width;
+}
+
+/*!
+    \return Scale pen width
+    \sa setPenWidth()
+*/
+int QwtAbstractScaleDraw::penWidth() const
+{
+    return d_data->penWidth;
+}
+
+/*!
+  \brief Draw the scale
+
+  \param painter    The painter
+
+  \param palette    Palette, text color is used for the labels,
+                    foreground color for ticks and backbone
+*/
+void QwtAbstractScaleDraw::draw( QPainter *painter,
+    const QPalette& palette ) const
+{
+    painter->save();
+
+    QPen pen = painter->pen();
+    pen.setWidth( d_data->penWidth );
+    pen.setCosmetic( false );
+    painter->setPen( pen );
+
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
+    {
+        painter->save();
+        painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style
+
+        const QList<double> &majorTicks =
+            d_data->scaleDiv.ticks( QwtScaleDiv::MajorTick );
+
+        for ( int i = 0; i < majorTicks.count(); i++ )
+        {
+            const double v = majorTicks[i];
+            if ( d_data->scaleDiv.contains( v ) )
+                drawLabel( painter, v );
+        }
+
+        painter->restore();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+    {
+        painter->save();
+
+        QPen pen = painter->pen();
+        pen.setColor( palette.color( QPalette::WindowText ) );
+        pen.setCapStyle( Qt::FlatCap );
+
+        painter->setPen( pen );
+
+        for ( int tickType = QwtScaleDiv::MinorTick;
+            tickType < QwtScaleDiv::NTickTypes; tickType++ )
+        {
+            const double tickLen = d_data->tickLength[tickType];
+            if ( tickLen <= 0.0 )
+                continue;
+
+            const QList<double> &ticks = d_data->scaleDiv.ticks( tickType );
+            for ( int i = 0; i < ticks.count(); i++ )
+            {
+                const double v = ticks[i];
+                if ( d_data->scaleDiv.contains( v ) )
+                    drawTick( painter, v, tickLen );
+            }
+        }
+
+        painter->restore();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
+    {
+        painter->save();
+
+        QPen pen = painter->pen();
+        pen.setColor( palette.color( QPalette::WindowText ) );
+        pen.setCapStyle( Qt::FlatCap );
+
+        painter->setPen( pen );
+
+        drawBackbone( painter );
+
+        painter->restore();
+    }
+
+    painter->restore();
+}
+
+/*!
+  \brief Set the spacing between tick and labels
+
+  The spacing is the distance between ticks and labels.
+  The default spacing is 4 pixels.
+
+  \param spacing Spacing
+
+  \sa spacing()
+*/
+void QwtAbstractScaleDraw::setSpacing( double spacing )
+{
+    if ( spacing < 0 )
+        spacing = 0;
+
+    d_data->spacing = spacing;
+}
+
+/*!
+  \brief Get the spacing
+
+  The spacing is the distance between ticks and labels.
+  The default spacing is 4 pixels.
+
+  \return Spacing
+  \sa setSpacing()
+*/
+double QwtAbstractScaleDraw::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+  \brief Set a minimum for the extent
+
+  The extent is calculated from the components of the
+  scale draw. In situations, where the labels are
+  changing and the layout depends on the extent (f.e scrolling
+  a scale), setting an upper limit as minimum extent will
+  avoid jumps of the layout.
+
+  \param minExtent Minimum extent
+
+  \sa extent(), minimumExtent()
+*/
+void QwtAbstractScaleDraw::setMinimumExtent( double minExtent )
+{
+    if ( minExtent < 0.0 )
+        minExtent = 0.0;
+
+    d_data->minExtent = minExtent;
+}
+
+/*!
+  Get the minimum extent
+  \return Minimum extent
+  \sa extent(), setMinimumExtent()
+*/
+double QwtAbstractScaleDraw::minimumExtent() const
+{
+    return d_data->minExtent;
+}
+
+/*!
+  Set the length of the ticks
+
+  \param tickType Tick type
+  \param length New length
+
+  \warning the length is limited to [0..1000]
+*/
+void QwtAbstractScaleDraw::setTickLength(
+    QwtScaleDiv::TickType tickType, double length )
+{
+    if ( tickType < QwtScaleDiv::MinorTick ||
+        tickType > QwtScaleDiv::MajorTick )
+    {
+        return;
+    }
+
+    if ( length < 0.0 )
+        length = 0.0;
+
+    const double maxTickLen = 1000.0;
+    if ( length > maxTickLen )
+        length = maxTickLen;
+
+    d_data->tickLength[tickType] = length;
+}
+
+/*!
+    \return Length of the ticks
+    \sa setTickLength(), maxTickLength()
+*/
+double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const
+{
+    if ( tickType < QwtScaleDiv::MinorTick ||
+        tickType > QwtScaleDiv::MajorTick )
+    {
+        return 0;
+    }
+
+    return d_data->tickLength[tickType];
+}
+
+/*!
+   \return Length of the longest tick
+
+   Useful for layout calculations
+   \sa tickLength(), setTickLength()
+*/
+double QwtAbstractScaleDraw::maxTickLength() const
+{
+    double length = 0.0;
+    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+        length = qMax( length, d_data->tickLength[i] );
+
+    return length;
+}
+
+/*!
+  \brief Convert a value into its representing label
+
+  The value is converted to a plain text using
+  QLocale().toString(value).
+  This method is often overloaded by applications to have individual
+  labels.
+
+  \param value Value
+  \return Label string.
+*/
+QwtText QwtAbstractScaleDraw::label( double value ) const
+{
+    return QLocale().toString( value );
+}
+
+/*!
+   \brief Convert a value into its representing label and cache it.
+
+   The conversion between value and label is called very often
+   in the layout and painting code. Unfortunately the
+   calculation of the label sizes might be slow (really slow
+   for rich text in Qt4), so it's necessary to cache the labels.
+
+   \param font Font
+   \param value Value
+
+   \return Tick label
+*/
+const QwtText &QwtAbstractScaleDraw::tickLabel(
+    const QFont &font, double value ) const
+{
+    QMap<double, QwtText>::const_iterator it = d_data->labelCache.find( value );
+    if ( it == d_data->labelCache.end() )
+    {
+        QwtText lbl = label( value );
+        lbl.setRenderFlags( 0 );
+        lbl.setLayoutAttribute( QwtText::MinimumLayout );
+
+        ( void )lbl.textSize( font ); // initialize the internal cache
+
+        it = d_data->labelCache.insert( value, lbl );
+    }
+
+    return ( *it );
+}
+
+/*!
+   Invalidate the cache used by tickLabel()
+
+   The cache is invalidated, when a new QwtScaleDiv is set. If
+   the labels need to be changed. while the same QwtScaleDiv is set,
+   invalidateCache() needs to be called manually.
+*/
+void QwtAbstractScaleDraw::invalidateCache()
+{
+    d_data->labelCache.clear();
+}
diff --git a/source/third_party/qwt/qwt_abstract_slider.cpp b/source/third_party/qwt/qwt_abstract_slider.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70d2626600743941c10f675c521d91d6fedfaac9
--- /dev/null
+++ b/source/third_party/qwt/qwt_abstract_slider.cpp
@@ -0,0 +1,822 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_abstract_slider.h"
+#include "qwt/qwt_abstract_scale_draw.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_scale_map.h"
+#include <qevent.h>
+
+#if QT_VERSION < 0x040601
+#define qFabs(x) ::fabs(x)
+#endif
+
+static double qwtAlignToScaleDiv( 
+    const QwtAbstractSlider *slider, double value )
+{
+    const QwtScaleDiv &sd = slider->scaleDiv();
+
+    const int tValue = slider->transform( value );
+
+    if ( tValue == slider->transform( sd.lowerBound() ) )
+        return sd.lowerBound();
+
+    if ( tValue == slider->transform( sd.lowerBound() ) )
+        return sd.upperBound();
+
+    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+    {
+        const QList<double> ticks = sd.ticks( i );
+        for ( int j = 0; j < ticks.size(); j++ )
+        {
+            if ( slider->transform( ticks[ j ] ) == tValue )
+                return ticks[ j ];
+        }
+    }
+
+    return value;
+}
+
+class QwtAbstractSlider::PrivateData
+{
+public:
+    PrivateData():
+        isScrolling( false ),
+        isTracking( true ),
+        pendingValueChanged( false ),
+        readOnly( false ),
+        totalSteps( 100 ),
+        singleSteps( 1 ),
+        pageSteps( 10 ),
+        stepAlignment( true ),
+        isValid( false ),
+        value( 0.0 ),
+        wrapping( false ),
+        invertedControls( false )
+    {
+    }
+
+    bool isScrolling;
+    bool isTracking;
+    bool pendingValueChanged;
+
+    bool readOnly;
+
+    uint totalSteps;
+    uint singleSteps;
+    uint pageSteps;
+    bool stepAlignment;
+
+    bool isValid;
+    double value;
+
+    bool wrapping;
+    bool invertedControls;
+};
+
+/*!
+  \brief Constructor
+
+  The scale is initialized to [0.0, 100.0], the
+  number of steps is set to 100 with 1 and 10 and single
+  an page step sizes. Step alignment is enabled.
+
+  The initial value is invalid.
+
+  \param parent Parent widget
+*/
+QwtAbstractSlider::QwtAbstractSlider( QWidget *parent ):
+    QwtAbstractScale( parent )
+{
+    d_data = new QwtAbstractSlider::PrivateData;
+
+    setScale( 0.0, 100.0 );
+    setFocusPolicy( Qt::StrongFocus );
+}
+
+//! Destructor
+QwtAbstractSlider::~QwtAbstractSlider()
+{
+    delete d_data;
+}
+
+/*! 
+  Set the value to be valid/invalid
+
+  \param on When true, the value is invalidated
+
+  \sa setValue()
+*/
+void QwtAbstractSlider::setValid( bool on )
+{
+    if ( on != d_data->isValid )
+    {
+        d_data->isValid = on;
+        sliderChange();
+
+        Q_EMIT valueChanged( d_data->value );
+    }   
+}   
+
+//! \return True, when the value is invalid
+bool QwtAbstractSlider::isValid() const
+{
+    return d_data->isValid;
+}   
+
+/*!
+  En/Disable read only mode
+
+  In read only mode the slider can't be controlled by mouse
+  or keyboard.
+
+  \param on Enables in case of true
+  \sa isReadOnly()
+
+  \warning The focus policy is set to Qt::StrongFocus or Qt::NoFocus
+*/
+void QwtAbstractSlider::setReadOnly( bool on )
+{
+    if ( d_data->readOnly != on )
+    {
+        d_data->readOnly = on;
+        setFocusPolicy( on ? Qt::StrongFocus : Qt::NoFocus );
+
+        update();
+    }
+}
+
+/*!
+  In read only mode the slider can't be controlled by mouse
+  or keyboard.
+
+  \return true if read only
+  \sa setReadOnly()
+*/
+bool QwtAbstractSlider::isReadOnly() const
+{
+    return d_data->readOnly;
+}
+
+/*!
+  \brief Enables or disables tracking.
+
+  If tracking is enabled, the slider emits the valueChanged() 
+  signal while the movable part of the slider is being dragged. 
+  If tracking is disabled, the slider emits the valueChanged() signal 
+  only when the user releases the slider.
+
+  Tracking is enabled by default.
+  \param on \c true (enable) or \c false (disable) tracking.
+
+  \sa isTracking(), sliderMoved()
+*/
+void QwtAbstractSlider::setTracking( bool on )
+{
+    d_data->isTracking = on;
+}
+
+/*!
+  \return True, when tracking has been enabled
+  \sa setTracking()
+*/
+bool QwtAbstractSlider::isTracking() const
+{
+    return d_data->isTracking;
+}
+
+/*!
+   Mouse press event handler
+   \param event Mouse event
+*/
+void QwtAbstractSlider::mousePressEvent( QMouseEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( !d_data->isValid || lowerBound() == upperBound() )
+        return;
+
+    d_data->isScrolling = isScrollPosition( event->pos() );
+
+    if ( d_data->isScrolling )
+    {
+        d_data->pendingValueChanged = false;
+
+        Q_EMIT sliderPressed();
+    }
+}
+
+/*!
+   Mouse Move Event handler
+   \param event Mouse event
+*/
+void QwtAbstractSlider::mouseMoveEvent( QMouseEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( d_data->isValid && d_data->isScrolling )
+    {
+        double value = scrolledTo( event->pos() );
+        if ( value != d_data->value )
+        {
+            value = boundedValue( value );
+
+            if ( d_data->stepAlignment )
+            {
+                value = alignedValue( value );
+            }
+            else
+            {
+                value = qwtAlignToScaleDiv( this, value );
+            }
+
+            if ( value != d_data->value )
+            {
+                d_data->value = value;
+
+                sliderChange();
+
+                Q_EMIT sliderMoved( d_data->value );
+
+                if ( d_data->isTracking )
+                    Q_EMIT valueChanged( d_data->value );
+                else
+                    d_data->pendingValueChanged = true;
+            }
+        }
+    }
+}
+
+/*!
+   Mouse Release Event handler
+   \param event Mouse event
+*/
+void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( d_data->isScrolling && d_data->isValid )
+    {
+        d_data->isScrolling = false;
+
+        if ( d_data->pendingValueChanged )
+            Q_EMIT valueChanged( d_data->value );
+
+        Q_EMIT sliderReleased();
+    }
+}
+
+/*!
+   Wheel Event handler
+
+   In/decreases the value by s number of steps. The direction 
+   depends on the invertedControls() property.
+
+   When the control or shift modifier is pressed the wheel delta
+   ( divided by 120 ) is mapped to an increment according to
+   pageSteps(). Otherwise it is mapped to singleSteps().
+
+   \param event Wheel event
+*/
+void QwtAbstractSlider::wheelEvent( QWheelEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( !d_data->isValid || d_data->isScrolling )
+        return;
+
+    int numSteps = 0;
+
+    if ( ( event->modifiers() & Qt::ControlModifier) ||
+        ( event->modifiers() & Qt::ShiftModifier ) )
+    {
+        // one page regardless of delta
+        numSteps = d_data->pageSteps;
+        if ( event->delta() < 0 )
+            numSteps = -numSteps;
+    }
+    else
+    {
+        const int numTurns = ( event->delta() / 120 );
+        numSteps = numTurns * d_data->singleSteps;
+    }
+
+    if ( d_data->invertedControls )
+        numSteps = -numSteps;
+
+    const double value = incrementedValue( d_data->value, numSteps );
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        sliderChange();
+
+        Q_EMIT sliderMoved( d_data->value );
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+/*!
+  Handles key events
+
+  QwtAbstractSlider handles the following keys:
+
+  - Qt::Key_Left\n
+    Add/Subtract singleSteps() in direction to lowerBound();
+  - Qt::Key_Right\n
+    Add/Subtract singleSteps() in direction to upperBound();
+  - Qt::Key_Down\n
+    Subtract singleSteps(), when invertedControls() is false
+  - Qt::Key_Up\n
+    Add singleSteps(), when invertedControls() is false
+  - Qt::Key_PageDown\n
+    Subtract pageSteps(), when invertedControls() is false
+  - Qt::Key_PageUp\n
+    Add pageSteps(), when invertedControls() is false
+  - Qt::Key_Home\n
+    Set the value to the minimum()
+  - Qt::Key_End\n
+    Set the value to the maximum()
+
+  \param event Key event
+  \sa isReadOnly()
+*/
+void QwtAbstractSlider::keyPressEvent( QKeyEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( !d_data->isValid || d_data->isScrolling )
+        return;
+
+    int numSteps = 0;
+    double value = d_data->value;
+
+    switch ( event->key() )
+    {
+        case Qt::Key_Left:
+        {
+            numSteps = -static_cast<int>( d_data->singleSteps );
+            if ( isInverted() )
+                numSteps = -numSteps;
+
+            break;
+        }
+        case Qt::Key_Right:
+        {
+            numSteps = d_data->singleSteps;
+            if ( isInverted() )
+                numSteps = -numSteps;
+
+            break;
+        }
+        case Qt::Key_Down:
+        {
+            numSteps = -static_cast<int>( d_data->singleSteps );
+            if ( d_data->invertedControls )
+                numSteps = -numSteps;
+            break;
+        }
+        case Qt::Key_Up:
+        {
+            numSteps = d_data->singleSteps;
+            if ( d_data->invertedControls )
+                numSteps = -numSteps;
+
+            break;
+        }
+        case Qt::Key_PageUp:
+        {
+            numSteps = d_data->pageSteps;
+            if ( d_data->invertedControls )
+                numSteps = -numSteps;
+            break;
+        }
+        case Qt::Key_PageDown:
+        {
+            numSteps = -static_cast<int>( d_data->pageSteps );
+            if ( d_data->invertedControls )
+                numSteps = -numSteps;
+            break;
+        }
+        case Qt::Key_Home:
+        {
+            value = minimum();
+            break;
+        }
+        case Qt::Key_End:
+        {
+            value = maximum();
+            break;
+        }
+        default:;
+        {
+            event->ignore();
+        }
+    }
+
+    if ( numSteps != 0 )
+    {
+        value = incrementedValue( d_data->value, numSteps );
+    }
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        sliderChange();
+
+        Q_EMIT sliderMoved( d_data->value );
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+/*!
+  \brief Set the number of steps
+
+  The range of the slider is divided into a number of steps from
+  which the value increments according to user inputs depend. 
+
+  The default setting is 100.
+
+  \param stepCount Number of steps
+
+  \sa totalSteps(), setSingleSteps(), setPageSteps()
+ */
+void QwtAbstractSlider::setTotalSteps( uint stepCount )
+{
+    d_data->totalSteps = stepCount;
+}
+
+/*!
+  \return Number of steps
+  \sa setTotalSteps(), singleSteps(), pageSteps()
+ */
+uint QwtAbstractSlider::totalSteps() const
+{
+    return d_data->totalSteps;
+}
+
+/*!
+  \brief Set the number of steps for a single increment
+
+  The range of the slider is divided into a number of steps from
+  which the value increments according to user inputs depend. 
+
+  \param stepCount Number of steps
+
+  \sa singleSteps(), setTotalSteps(), setPageSteps()
+ */
+
+void QwtAbstractSlider::setSingleSteps( uint stepCount )
+{
+    d_data->singleSteps = stepCount;
+}   
+
+/*!
+  \return Number of steps
+  \sa setSingleSteps(), totalSteps(), pageSteps()
+ */
+uint QwtAbstractSlider::singleSteps() const
+{
+    return d_data->singleSteps;
+}   
+
+/*! 
+  \brief Set the number of steps for a page increment
+    
+  The range of the slider is divided into a number of steps from
+  which the value increments according to user inputs depend. 
+
+  \param stepCount Number of steps
+
+  \sa pageSteps(), setTotalSteps(), setSingleSteps()
+ */
+
+void QwtAbstractSlider::setPageSteps( uint stepCount )
+{
+    d_data->pageSteps = stepCount;
+}
+
+/*!
+  \return Number of steps
+  \sa setPageSteps(), totalSteps(), singleSteps()
+ */
+uint QwtAbstractSlider::pageSteps() const
+{
+    return d_data->pageSteps;
+}
+
+/*!
+  \brief Enable step alignment
+
+  When step alignment is enabled values resulting from slider
+  movements are aligned to the step size.
+
+  \param on Enable step alignment when true
+  \sa stepAlignment()
+*/
+void QwtAbstractSlider::setStepAlignment( bool on )
+{   
+    if ( on != d_data->stepAlignment )
+    {
+        d_data->stepAlignment = on;
+    }
+}   
+    
+/*!
+  \return True, when step alignment is enabled
+  \sa setStepAlignment()
+ */
+bool QwtAbstractSlider::stepAlignment() const
+{
+    return d_data->stepAlignment;
+}
+
+/*!
+  Set the slider to the specified value
+
+  \param value New value
+  \sa setValid(), sliderChange(), valueChanged()
+*/
+void QwtAbstractSlider::setValue( double value )
+{
+    value = qBound( minimum(), value, maximum() );
+
+    const bool changed = ( d_data->value != value ) || !d_data->isValid;
+
+    d_data->value = value;
+    d_data->isValid = true;
+
+    if ( changed )
+    {
+        sliderChange();
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+//! Returns the current value.
+double QwtAbstractSlider::value() const
+{
+    return d_data->value;
+}
+
+/*!
+  If wrapping is true stepping up from upperBound() value will 
+  take you to the minimum() value and vice versa. 
+
+  \param on En/Disable wrapping
+  \sa wrapping()
+*/
+void QwtAbstractSlider::setWrapping( bool on )
+{
+    d_data->wrapping = on;
+}   
+
+/*!
+  \return True, when wrapping is set
+  \sa setWrapping()
+ */ 
+bool QwtAbstractSlider::wrapping() const
+{
+    return d_data->wrapping;
+}
+
+/*!
+  Invert wheel and key events
+
+  Usually scrolling the mouse wheel "up" and using keys like page 
+  up will increase the slider's value towards its maximum. 
+  When invertedControls() is enabled the value is scrolled
+  towards its minimum.
+
+  Inverting the controls might be f.e. useful for a vertical slider
+  with an inverted scale ( decreasing from top to bottom ).
+
+  \param on Invert controls, when true
+
+  \sa invertedControls(), keyEvent(), wheelEvent()
+ */
+void QwtAbstractSlider::setInvertedControls( bool on )
+{
+    d_data->invertedControls = on;
+}
+
+/*!
+ \return True, when the controls are inverted
+ \sa setInvertedControls()
+ */
+bool QwtAbstractSlider::invertedControls() const
+{
+    return d_data->invertedControls;
+}
+
+/*!
+  Increment the slider
+
+  The step size depends on the number of totalSteps()
+
+  \param stepCount Number of steps
+  \sa setTotalSteps(), incrementedValue()
+ */
+void QwtAbstractSlider::incrementValue( int stepCount )
+{
+    const double value = incrementedValue( 
+        d_data->value, stepCount );
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        sliderChange();
+    }
+}
+
+/*!
+  Increment a value 
+
+  \param value Value 
+  \param stepCount Number of steps
+
+  \return Incremented value
+ */
+double QwtAbstractSlider::incrementedValue( 
+    double value, int stepCount ) const
+{
+    if ( d_data->totalSteps == 0 )
+        return value;
+
+    const QwtTransform *transformation =
+        scaleMap().transformation();
+
+    if ( transformation == NULL )
+    {
+        const double range = maximum() - minimum();
+        value += stepCount * range / d_data->totalSteps;
+    }
+    else
+    {
+        QwtScaleMap map = scaleMap();
+        map.setPaintInterval( 0, d_data->totalSteps );
+
+        // we need equidant steps according to
+        // paint device coordinates
+        const double range = transformation->transform( maximum() ) 
+            - transformation->transform( minimum() );
+
+        const double stepSize = range / d_data->totalSteps;
+
+        double v = transformation->transform( value );
+
+        v = qRound( v / stepSize ) * stepSize; 
+        v += stepCount * range / d_data->totalSteps;
+
+        value = transformation->invTransform( v );
+    }
+
+    value = boundedValue( value );
+
+    if ( d_data->stepAlignment )
+        value = alignedValue( value );
+
+    return value;
+}
+
+double QwtAbstractSlider::boundedValue( double value ) const
+{
+    const double vmin = minimum();
+    const double vmax = maximum();
+
+    if ( d_data->wrapping && vmin != vmax )
+    {
+        const int fullCircle = 360 * 16;
+
+        const double pd = scaleMap().pDist();
+        if ( int( pd / fullCircle ) * fullCircle == pd )
+        {
+            // full circle scales: min and max are the same
+            const double range = vmax - vmin;
+
+            if ( value < vmin )
+            {
+                value += ::ceil( ( vmin - value ) / range ) * range;
+            }
+            else if ( value > vmax )
+            {
+                value -= ::ceil( ( value - vmax ) / range ) * range;
+            }
+        }
+        else
+        {
+            if ( value < vmin )
+                value = vmax;
+            else if ( value > vmax )
+                value = vmin;
+        }
+    }
+    else
+    {
+        value = qBound( vmin, value, vmax );
+    }
+
+    return value;
+}
+
+double QwtAbstractSlider::alignedValue( double value ) const
+{
+    if ( d_data->totalSteps == 0 )
+        return value;
+
+    double stepSize;
+
+    if ( scaleMap().transformation() == NULL )
+    {
+        stepSize = ( maximum() - minimum() ) / d_data->totalSteps;
+        if ( stepSize > 0.0 )
+        {
+            value = lowerBound() + 
+                qRound( ( value - lowerBound() ) / stepSize ) * stepSize;
+        }
+    }
+    else
+    {
+        stepSize = ( scaleMap().p2() - scaleMap().p1() ) / d_data->totalSteps;
+
+        if ( stepSize > 0.0 )
+        {
+            double v = scaleMap().transform( value );
+
+            v = scaleMap().p1() +
+                qRound( ( v - scaleMap().p1() ) / stepSize ) * stepSize;
+
+            value = scaleMap().invTransform( v );
+        }
+    }
+
+    if ( qAbs( stepSize ) > 1e-12 )
+    {
+        if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+        {
+            // correct rounding error if value = 0
+            value = 0.0;
+        }
+        else
+        {
+            // correct rounding error at the border
+            if ( qFuzzyCompare( value, upperBound() ) )
+                value = upperBound();
+            else if ( qFuzzyCompare( value, lowerBound() ) )
+                value = lowerBound();
+        }
+    }
+
+    return value;
+}
+
+/*!
+  Update the slider according to modifications of the scale
+ */
+void QwtAbstractSlider::scaleChange()
+{
+    const double value = qBound( minimum(), d_data->value, maximum() );
+
+    const bool changed = ( value != d_data->value );
+    if ( changed )
+    {
+        d_data->value = value;
+    }
+
+    if ( d_data->isValid || changed )
+        Q_EMIT valueChanged( d_data->value );
+
+    updateGeometry();
+    update();
+}
+
+//! Calling update()
+void QwtAbstractSlider::sliderChange()
+{
+    update();
+}
diff --git a/source/third_party/qwt/qwt_analog_clock.cpp b/source/third_party/qwt/qwt_analog_clock.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..03c482b3fa2fd06d06855b9cfa17ad1ff4051947
--- /dev/null
+++ b/source/third_party/qwt/qwt_analog_clock.cpp
@@ -0,0 +1,244 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_analog_clock.h"
+#include "qwt/qwt_round_scale_draw.h"
+#include <qmath.h>
+#include <qlocale.h>
+
+class QwtAnalogClockScaleDraw: public QwtRoundScaleDraw
+{
+public:
+    QwtAnalogClockScaleDraw()
+    {
+        setSpacing( 8 );
+
+        enableComponent( QwtAbstractScaleDraw::Backbone, false );
+
+        setTickLength( QwtScaleDiv::MinorTick, 2 );
+        setTickLength( QwtScaleDiv::MediumTick, 4 );
+        setTickLength( QwtScaleDiv::MajorTick, 8 );
+
+        setPenWidth( 1 );
+    }
+
+    virtual QwtText label( double value ) const
+    {
+        if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+            value = 60.0 * 60.0 * 12.0;
+
+        return QLocale().toString( qRound( value / ( 60.0 * 60.0 ) ) );
+    }
+};
+
+/*!
+  Constructor
+  \param parent Parent widget
+*/
+QwtAnalogClock::QwtAnalogClock( QWidget *parent ):
+    QwtDial( parent )
+{
+    setWrapping( true );
+    setReadOnly( true );
+
+    setOrigin( 270.0 );
+    setScaleDraw( new QwtAnalogClockScaleDraw() );
+
+    setTotalSteps( 60 );
+
+    const int secondsPerHour = 60.0 * 60.0; 
+
+    QList<double> majorTicks;
+    QList<double> minorTicks;
+
+    for ( int i = 0; i < 12; i++ )
+    {
+        majorTicks += i * secondsPerHour;
+
+        for ( int j = 1; j < 5; j++ )
+            minorTicks += i * secondsPerHour + j * secondsPerHour / 5.0;
+    }
+
+    QwtScaleDiv scaleDiv;
+    scaleDiv.setInterval( 0.0, 12.0 * secondsPerHour );
+    scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
+    setScale( scaleDiv );
+
+    QColor knobColor = palette().color( QPalette::Active, QPalette::Text );
+    knobColor = knobColor.dark( 120 );
+
+    QColor handColor;
+    int width;
+
+    for ( int i = 0; i < NHands; i++ )
+    {
+        if ( i == SecondHand )
+        {
+            width = 2;
+            handColor = knobColor.dark( 120 );
+        }
+        else
+        {
+            width = 8;
+            handColor = knobColor;
+        }
+
+        QwtDialSimpleNeedle *hand = new QwtDialSimpleNeedle(
+            QwtDialSimpleNeedle::Arrow, true, handColor, knobColor );
+        hand->setWidth( width );
+
+        d_hand[i] = NULL;
+        setHand( static_cast<Hand>( i ), hand );
+    }
+}
+
+//! Destructor
+QwtAnalogClock::~QwtAnalogClock()
+{
+    for ( int i = 0; i < NHands; i++ )
+        delete d_hand[i];
+}
+
+/*!
+  Nop method, use setHand() instead
+  \sa setHand()
+*/
+void QwtAnalogClock::setNeedle( QwtDialNeedle * )
+{
+    // no op
+    return;
+}
+
+/*!
+   Set a clock hand
+   \param hand Specifies the type of hand
+   \param needle Hand
+   \sa hand()
+*/
+void QwtAnalogClock::setHand( Hand hand, QwtDialNeedle *needle )
+{
+    if ( hand >= 0 && hand < NHands )
+    {
+        delete d_hand[hand];
+        d_hand[hand] = needle;
+    }
+}
+
+/*!
+  \return Clock hand
+  \param hd Specifies the type of hand
+  \sa setHand()
+*/
+QwtDialNeedle *QwtAnalogClock::hand( Hand hd )
+{
+    if ( hd < 0 || hd >= NHands )
+        return NULL;
+
+    return d_hand[hd];
+}
+
+/*!
+  \return Clock hand
+  \param hd Specifies the type of hand
+  \sa setHand()
+*/
+const QwtDialNeedle *QwtAnalogClock::hand( Hand hd ) const
+{
+    return const_cast<QwtAnalogClock *>( this )->hand( hd );
+}
+
+/*!
+  \brief Set the current time
+*/
+void QwtAnalogClock::setCurrentTime()
+{
+    setTime( QTime::currentTime() );
+}
+
+/*!
+  Set a time
+  \param time Time to display
+*/
+void QwtAnalogClock::setTime( const QTime &time )
+{
+    if ( time.isValid() )
+    {
+        setValue( ( time.hour() % 12 ) * 60.0 * 60.0
+            + time.minute() * 60.0 + time.second() );
+    }
+    else
+        setValid( false );
+}
+
+/*!
+  \brief Draw the needle
+
+  A clock has no single needle but three hands instead. drawNeedle()
+  translates value() into directions for the hands and calls
+  drawHand().
+
+  \param painter Painter
+  \param center Center of the clock
+  \param radius Maximum length for the hands
+  \param dir Dummy, not used.
+  \param colorGroup ColorGroup
+
+  \sa drawHand()
+*/
+void QwtAnalogClock::drawNeedle( QPainter *painter, const QPointF &center,
+    double radius, double dir, QPalette::ColorGroup colorGroup ) const
+{
+    Q_UNUSED( dir );
+
+    if ( isValid() )
+    {
+        const double hours = value() / ( 60.0 * 60.0 );
+        const double minutes = 
+            ( value() - qFloor(hours) * 60.0 * 60.0 ) / 60.0;
+        const double seconds = value() - qFloor(hours) * 60.0 * 60.0
+            - qFloor(minutes) * 60.0;
+
+        double angle[NHands];
+        angle[HourHand] = 360.0 * hours / 12.0;
+        angle[MinuteHand] = 360.0 * minutes / 60.0;
+        angle[SecondHand] = 360.0 * seconds / 60.0;
+
+        for ( int hand = 0; hand < NHands; hand++ )
+        {
+            const double d = 360.0 - angle[hand] - origin();
+            drawHand( painter, static_cast<Hand>( hand ), 
+                center, radius, d, colorGroup );
+        }
+    }
+}
+
+/*!
+  Draw a clock hand
+
+  \param painter Painter
+  \param hd Specify the type of hand
+  \param center Center of the clock
+  \param radius Maximum length for the hands
+  \param direction Direction of the hand in degrees, counter clockwise
+  \param cg ColorGroup
+*/
+void QwtAnalogClock::drawHand( QPainter *painter, Hand hd,
+    const QPointF &center, double radius, double direction,
+    QPalette::ColorGroup cg ) const
+{
+    const QwtDialNeedle *needle = hand( hd );
+    if ( needle )
+    {
+        if ( hd == HourHand )
+            radius = qRound( 0.8 * radius );
+
+        needle->draw( painter, center, radius, direction, cg );
+    }
+}
diff --git a/source/third_party/qwt/qwt_arrow_button.cpp b/source/third_party/qwt/qwt_arrow_button.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2759b07d427e0bd53cfb25d458d073f3a021ee5f
--- /dev/null
+++ b/source/third_party/qwt/qwt_arrow_button.cpp
@@ -0,0 +1,333 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_arrow_button.h"
+#include "qwt/qwt_math.h"
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qevent.h>
+#include <qapplication.h>
+
+static const int MaxNum = 3;
+static const int Margin = 2;
+static const int Spacing = 1;
+
+class QwtArrowButton::PrivateData
+{
+public:
+    int num;
+    Qt::ArrowType arrowType;
+};
+
+static QStyleOptionButton styleOpt( const QwtArrowButton* btn )
+{
+    QStyleOptionButton option;
+    option.init( btn );
+    option.features = QStyleOptionButton::None;
+    if ( btn->isFlat() )
+        option.features |= QStyleOptionButton::Flat;
+    if ( btn->menu() )
+        option.features |= QStyleOptionButton::HasMenu;
+    if ( btn->autoDefault() || btn->isDefault() )
+        option.features |= QStyleOptionButton::AutoDefaultButton;
+    if ( btn->isDefault() )
+        option.features |= QStyleOptionButton::DefaultButton;
+    if ( btn->isDown() )
+        option.state |= QStyle::State_Sunken;
+    if ( !btn->isFlat() && !btn->isDown() )
+        option.state |= QStyle::State_Raised;
+
+    return option;
+}
+
+/*!
+  \param num Number of arrows
+  \param arrowType see Qt::ArrowType in the Qt docs.
+  \param parent Parent widget
+*/
+QwtArrowButton::QwtArrowButton( int num,
+        Qt::ArrowType arrowType, QWidget *parent ):
+    QPushButton( parent )
+{
+    d_data = new PrivateData;
+    d_data->num = qBound( 1, num, MaxNum );
+    d_data->arrowType = arrowType;
+
+    setAutoRepeat( true );
+    setAutoDefault( false );
+
+    switch ( d_data->arrowType )
+    {
+        case Qt::LeftArrow:
+        case Qt::RightArrow:
+            setSizePolicy( QSizePolicy::Expanding,
+                QSizePolicy::Fixed );
+            break;
+        default:
+            setSizePolicy( QSizePolicy::Fixed,
+                QSizePolicy::Expanding );
+    }
+}
+
+//! Destructor
+QwtArrowButton::~QwtArrowButton()
+{
+    delete d_data;
+    d_data = NULL;
+}
+
+/*!
+  \brief The direction of the arrows
+*/
+Qt::ArrowType QwtArrowButton::arrowType() const
+{
+    return d_data->arrowType;
+}
+
+/*!
+  \brief The number of arrows
+*/
+int QwtArrowButton::num() const
+{
+    return d_data->num;
+}
+
+/*!
+  \return the bounding rectangle for the label
+*/
+QRect QwtArrowButton::labelRect() const
+{
+    const int m = Margin;
+
+    QRect r = rect();
+    r.setRect( r.x() + m, r.y() + m,
+        r.width() - 2 * m, r.height() - 2 * m );
+
+    if ( isDown() )
+    {
+        QStyleOptionButton option = styleOpt( this );
+        const int ph = style()->pixelMetric(
+            QStyle::PM_ButtonShiftHorizontal, &option, this );
+        const int pv = style()->pixelMetric(
+            QStyle::PM_ButtonShiftVertical, &option, this );
+
+        r.translate( ph, pv );
+    }
+
+    return r;
+}
+
+/*!
+   Paint event handler
+   \param event Paint event
+*/
+void QwtArrowButton::paintEvent( QPaintEvent *event )
+{
+    QPushButton::paintEvent( event );
+    QPainter painter( this );
+    drawButtonLabel( &painter );
+}
+
+/*!
+  \brief Draw the button label
+
+  \param painter Painter
+  \sa The Qt Manual for QPushButton
+*/
+void QwtArrowButton::drawButtonLabel( QPainter *painter )
+{
+    const bool isVertical = d_data->arrowType == Qt::UpArrow ||
+        d_data->arrowType == Qt::DownArrow;
+
+    const QRect r = labelRect();
+    QSize boundingSize = labelRect().size();
+    if ( isVertical )
+        boundingSize.transpose();
+
+    const int w =
+        ( boundingSize.width() - ( MaxNum - 1 ) * Spacing ) / MaxNum;
+
+    QSize arrow = arrowSize( Qt::RightArrow,
+        QSize( w, boundingSize.height() ) );
+
+    if ( isVertical )
+        arrow.transpose();
+
+    QRect contentsSize; // aligned rect where to paint all arrows
+    if ( d_data->arrowType == Qt::LeftArrow || d_data->arrowType == Qt::RightArrow )
+    {
+        contentsSize.setWidth( d_data->num * arrow.width()
+            + ( d_data->num - 1 ) * Spacing );
+        contentsSize.setHeight( arrow.height() );
+    }
+    else
+    {
+        contentsSize.setWidth( arrow.width() );
+        contentsSize.setHeight( d_data->num * arrow.height()
+            + ( d_data->num - 1 ) * Spacing );
+    }
+
+    QRect arrowRect( contentsSize );
+    arrowRect.moveCenter( r.center() );
+    arrowRect.setSize( arrow );
+
+    painter->save();
+    for ( int i = 0; i < d_data->num; i++ )
+    {
+        drawArrow( painter, arrowRect, d_data->arrowType );
+
+        int dx = 0;
+        int dy = 0;
+
+        if ( isVertical )
+            dy = arrow.height() + Spacing;
+        else
+            dx = arrow.width() + Spacing;
+
+        arrowRect.translate( dx, dy );
+    }
+    painter->restore();
+
+    if ( hasFocus() )
+    {
+        QStyleOptionFocusRect option;
+        option.init( this );
+        option.backgroundColor = palette().color( QPalette::Window );
+
+        style()->drawPrimitive( QStyle::PE_FrameFocusRect,
+            &option, painter, this );
+    }
+}
+
+/*!
+    Draw an arrow int a bounding rectangle
+
+    \param painter Painter
+    \param r Rectangle where to paint the arrow
+    \param arrowType Arrow type
+*/
+void QwtArrowButton::drawArrow( QPainter *painter,
+    const QRect &r, Qt::ArrowType arrowType ) const
+{
+    QPolygon pa( 3 );
+
+    switch ( arrowType )
+    {
+        case Qt::UpArrow:
+            pa.setPoint( 0, r.bottomLeft() );
+            pa.setPoint( 1, r.bottomRight() );
+            pa.setPoint( 2, r.center().x(), r.top() );
+            break;
+        case Qt::DownArrow:
+            pa.setPoint( 0, r.topLeft() );
+            pa.setPoint( 1, r.topRight() );
+            pa.setPoint( 2, r.center().x(), r.bottom() );
+            break;
+        case Qt::RightArrow:
+            pa.setPoint( 0, r.topLeft() );
+            pa.setPoint( 1, r.bottomLeft() );
+            pa.setPoint( 2, r.right(), r.center().y() );
+            break;
+        case Qt::LeftArrow:
+            pa.setPoint( 0, r.topRight() );
+            pa.setPoint( 1, r.bottomRight() );
+            pa.setPoint( 2, r.left(), r.center().y() );
+            break;
+        default:
+            break;
+    }
+
+    painter->save();
+
+    painter->setRenderHint( QPainter::Antialiasing, true );
+    painter->setPen( Qt::NoPen );
+    painter->setBrush( palette().brush( QPalette::ButtonText ) );
+    painter->drawPolygon( pa );
+
+    painter->restore();
+}
+
+/*!
+  \return a size hint
+*/
+QSize QwtArrowButton::sizeHint() const
+{
+    const QSize hint = minimumSizeHint();
+    return hint.expandedTo( QApplication::globalStrut() );
+}
+
+/*!
+  \brief Return a minimum size hint
+*/
+QSize QwtArrowButton::minimumSizeHint() const
+{
+    const QSize asz = arrowSize( Qt::RightArrow, QSize() );
+
+    QSize sz(
+        2 * Margin + ( MaxNum - 1 ) * Spacing + MaxNum * asz.width(),
+        2 * Margin + asz.height()
+    );
+
+    if ( d_data->arrowType == Qt::UpArrow || d_data->arrowType == Qt::DownArrow )
+        sz.transpose();
+
+    QStyleOption styleOption;
+    styleOption.init( this );
+
+    sz = style()->sizeFromContents( QStyle::CT_PushButton,
+        &styleOption, sz, this );
+
+    return sz;
+}
+
+/*!
+   Calculate the size for a arrow that fits into a rectangle of a given size
+
+   \param arrowType Arrow type
+   \param boundingSize Bounding size
+   \return Size of the arrow
+*/
+QSize QwtArrowButton::arrowSize( Qt::ArrowType arrowType,
+    const QSize &boundingSize ) const
+{
+    QSize bs = boundingSize;
+    if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow )
+        bs.transpose();
+
+    const int MinLen = 2;
+    const QSize sz = bs.expandedTo(
+        QSize( MinLen, 2 * MinLen - 1 ) ); // minimum
+
+    int w = sz.width();
+    int h = 2 * w - 1;
+
+    if ( h > sz.height() )
+    {
+        h = sz.height();
+        w = ( h + 1 ) / 2;
+    }
+
+    QSize arrSize( w, h );
+    if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow )
+        arrSize.transpose();
+
+    return arrSize;
+}
+
+/*!
+  \brief autoRepeat for the space keys
+*/
+void QwtArrowButton::keyPressEvent( QKeyEvent *event )
+{
+    if ( event->isAutoRepeat() && event->key() == Qt::Key_Space )
+        Q_EMIT clicked();
+
+    QPushButton::keyPressEvent( event );
+}
diff --git a/source/third_party/qwt/qwt_clipper.cpp b/source/third_party/qwt/qwt_clipper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..044da6e48642ad2ec458377047b863a0518d629c
--- /dev/null
+++ b/source/third_party/qwt/qwt_clipper.cpp
@@ -0,0 +1,510 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_clipper.h"
+#include "qwt/qwt_point_polar.h"
+#include <qrect.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if QT_VERSION < 0x040601
+#define qAtan(x) ::atan(x)
+#endif
+
+namespace QwtClip
+{
+    // some templates used for inlining
+    template <class Point, typename T> class LeftEdge;
+    template <class Point, typename T> class RightEdge;
+    template <class Point, typename T> class TopEdge;
+    template <class Point, typename T> class BottomEdge;
+
+    template <class Point> class PointBuffer;
+}
+
+template <class Point, typename Value>
+class QwtClip::LeftEdge
+{
+public:
+    inline LeftEdge( Value x1, Value, Value, Value ):
+        d_x1( x1 )
+    {
+    }
+
+    inline bool isInside( const Point &p  ) const
+    {
+        return p.x() >= d_x1;
+    }
+
+    inline Point intersection( const Point &p1, const Point &p2 ) const
+    {
+        double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() );
+        return Point( d_x1, static_cast< Value >( p2.y() + ( d_x1 - p2.x() ) * dy ) );
+    }
+private:
+    const Value d_x1;
+};
+
+template <class Point, typename Value>
+class QwtClip::RightEdge
+{
+public:
+    inline RightEdge( Value, Value x2, Value, Value ):
+        d_x2( x2 )
+    {
+    }
+
+    inline bool isInside( const Point &p  ) const
+    {
+        return p.x() <= d_x2;
+    }
+
+    inline Point intersection( const Point &p1, const Point &p2 ) const
+    {
+        double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() );
+        return Point( d_x2, static_cast<Value>( p2.y() + ( d_x2 - p2.x() ) * dy ) );
+    }
+
+private:
+    const Value d_x2;
+};
+
+template <class Point, typename Value>
+class QwtClip::TopEdge
+{
+public:
+    inline TopEdge( Value, Value, Value y1, Value ):
+        d_y1( y1 )
+    {
+    }
+
+    inline bool isInside( const Point &p  ) const
+    {
+        return p.y() >= d_y1;
+    }
+
+    inline Point intersection( const Point &p1, const Point &p2 ) const
+    {
+        double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() );
+        return Point( static_cast<Value>( p2.x() + ( d_y1 - p2.y() ) * dx ), d_y1 );
+    }
+
+private:
+    const Value d_y1;
+};
+
+template <class Point, typename Value>
+class QwtClip::BottomEdge
+{
+public:
+    inline BottomEdge( Value, Value, Value, Value y2 ):
+        d_y2( y2 )
+    {
+    }
+
+    inline bool isInside( const Point &p ) const
+    {
+        return p.y() <= d_y2;
+    }
+
+    inline Point intersection( const Point &p1, const Point &p2 ) const
+    {
+        double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() );
+        return Point( static_cast<Value>( p2.x() + ( d_y2 - p2.y() ) * dx ), d_y2 );
+    }
+
+private:
+    const Value d_y2;
+};
+
+template<class Point>
+class QwtClip::PointBuffer
+{
+public:
+    PointBuffer( int capacity = 0 ):
+        m_capacity( 0 ),
+        m_size( 0 ),
+        m_buffer( NULL )
+    {
+        if ( capacity > 0 )
+            reserve( capacity );
+    }
+
+    ~PointBuffer()
+    {
+        if ( m_buffer )
+            ::free( m_buffer );
+    }
+
+    inline void setPoints( int numPoints, const Point *points )
+    {
+        reserve( numPoints );
+
+        m_size = numPoints;
+        ::memcpy( m_buffer, points, m_size * sizeof( Point ) );
+    }
+
+    inline void reset() 
+    { 
+        m_size = 0; 
+    }
+
+    inline int size() const 
+    { 
+        return m_size; 
+    }
+
+    inline Point *data() const 
+    { 
+        return m_buffer; 
+    }
+
+    inline Point &operator[]( int i ) 
+    { 
+        return m_buffer[i]; 
+    }
+
+    inline const Point &operator[]( int i ) const 
+    { 
+        return m_buffer[i]; 
+    }
+
+    inline void add( const Point &point )
+    {
+        if ( m_capacity <= m_size )
+            reserve( m_size + 1 );
+
+        m_buffer[m_size++] = point;
+    }
+
+private:
+    inline void reserve( int size )
+    {
+        if ( m_capacity == 0 )
+            m_capacity = 1;
+
+        while ( m_capacity < size )
+            m_capacity *= 2;
+
+        m_buffer = static_cast<Point *>( 
+            ::realloc( m_buffer, m_capacity * sizeof( Point ) ) );
+    }
+
+    int m_capacity;
+    int m_size;
+    Point *m_buffer;
+};
+
+using namespace QwtClip;
+
+template <class Polygon, class Rect, class Point, typename T>
+class QwtPolygonClipper
+{
+public:
+    QwtPolygonClipper( const Rect &clipRect ):
+        d_clipRect( clipRect )
+    {
+    }
+
+    Polygon clipPolygon( const Polygon &polygon, bool closePolygon ) const
+    {
+#if 0
+        if ( d_clipRect.contains( polygon.boundingRect() ) )
+            return polygon;
+#endif
+
+        PointBuffer<Point> points1;
+        PointBuffer<Point> points2( qMin( 256, polygon.size() ) );
+
+        points1.setPoints( polygon.size(), polygon.data() );
+
+        clipEdge< LeftEdge<Point, T> >( closePolygon, points1, points2 );
+        clipEdge< RightEdge<Point, T> >( closePolygon, points2, points1 );
+        clipEdge< TopEdge<Point, T> >( closePolygon, points1, points2 );
+        clipEdge< BottomEdge<Point, T> >( closePolygon, points2, points1 );
+
+        Polygon p;
+        p.resize( points1.size() );
+        ::memcpy( p.data(), points1.data(), points1.size() * sizeof( Point ) );
+
+        return p;
+    }
+
+private:
+    template <class Edge>
+    inline void clipEdge( bool closePolygon,
+        PointBuffer<Point> &points, PointBuffer<Point> &clippedPoints ) const
+    {
+        clippedPoints.reset();
+
+        if ( points.size() < 2 )
+        {
+            if ( points.size() == 1 )
+                clippedPoints.add( points[0] );
+            return;
+        }
+
+        const Edge edge( d_clipRect.x(), d_clipRect.x() + d_clipRect.width(),
+            d_clipRect.y(), d_clipRect.y() + d_clipRect.height() );
+
+        int lastPos, start;
+        if ( closePolygon )
+        {
+            start = 0;
+            lastPos = points.size() - 1;
+        }
+        else
+        {
+            start = 1;
+            lastPos = 0;
+
+            if ( edge.isInside( points[0] ) )
+                clippedPoints.add( points[0] );
+        }
+
+        const uint nPoints = points.size();
+        for ( uint i = start; i < nPoints; i++ )
+        {
+            const Point &p1 = points[i];
+            const Point &p2 = points[lastPos];
+
+            if ( edge.isInside( p1 ) )
+            {
+                if ( edge.isInside( p2 ) )
+                {
+                    clippedPoints.add( p1 );
+                }
+                else
+                {
+                    clippedPoints.add( edge.intersection( p1, p2 ) );
+                    clippedPoints.add( p1 );
+                }
+            }
+            else
+            {
+                if ( edge.isInside( p2 ) )
+                {
+                    clippedPoints.add( edge.intersection( p1, p2 ) );
+                }
+            }
+            lastPos = i;
+        }
+    }
+
+    const Rect d_clipRect;
+};
+
+class QwtCircleClipper
+{
+public:
+    QwtCircleClipper( const QRectF &r );
+    QVector<QwtInterval> clipCircle( const QPointF &, double radius ) const;
+
+private:
+    enum Edge
+    {
+        Left,
+        Top,
+        Right,
+        Bottom,
+
+        NEdges
+    };
+
+    QList<QPointF> cuttingPoints(
+        Edge, const QPointF &pos, double radius ) const;
+
+    double toAngle( const QPointF &, const QPointF & ) const;
+
+    const QRectF d_rect;
+};
+
+
+QwtCircleClipper::QwtCircleClipper( const QRectF &r ):
+    d_rect( r )
+{
+}
+
+QVector<QwtInterval> QwtCircleClipper::clipCircle(
+    const QPointF &pos, double radius ) const
+{
+    QList<QPointF> points;
+    for ( int edge = 0; edge < NEdges; edge++ )
+        points += cuttingPoints( static_cast<Edge>(edge), pos, radius );
+
+    QVector<QwtInterval> intv;
+    if ( points.size() <= 0 )
+    {
+        QRectF cRect( 0, 0, 2 * radius, 2 * radius );
+        cRect.moveCenter( pos );
+        if ( d_rect.contains( cRect ) )
+            intv += QwtInterval( 0.0, 2 * M_PI );
+    }
+    else
+    {
+        QList<double> angles;
+        for ( int i = 0; i < points.size(); i++ )
+            angles += toAngle( pos, points[i] );
+        qSort( angles );
+
+        const int in = d_rect.contains( qwtPolar2Pos( pos, radius,
+            angles[0] + ( angles[1] - angles[0] ) / 2 ) );
+
+        if ( in )
+        {
+            for ( int i = 0; i < angles.size() - 1; i += 2 )
+                intv += QwtInterval( angles[i], angles[i+1] );
+        }
+        else
+        {
+            for ( int i = 1; i < angles.size() - 1; i += 2 )
+                intv += QwtInterval( angles[i], angles[i+1] );
+            intv += QwtInterval( angles.last(), angles.first() );
+        }
+    }
+
+    return intv;
+}
+
+double QwtCircleClipper::toAngle(
+    const QPointF &from, const QPointF &to ) const
+{
+    if ( from.x() == to.x() )
+        return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0;
+
+    const double m = qAbs( ( to.y() - from.y() ) / ( to.x() - from.x() ) );
+
+    double angle = qAtan( m );
+    if ( to.x() > from.x() )
+    {
+        if ( to.y() > from.y() )
+            angle = 2 * M_PI - angle;
+    }
+    else
+    {
+        if ( to.y() > from.y() )
+            angle = M_PI + angle;
+        else
+            angle = M_PI - angle;
+    }
+
+    return angle;
+}
+
+QList<QPointF> QwtCircleClipper::cuttingPoints(
+    Edge edge, const QPointF &pos, double radius ) const
+{
+    QList<QPointF> points;
+
+    if ( edge == Left || edge == Right )
+    {
+        const double x = ( edge == Left ) ? d_rect.left() : d_rect.right();
+        if ( qAbs( pos.x() - x ) < radius )
+        {
+            const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.x() - x ) );
+            const double m_y1 = pos.y() + off;
+            if ( m_y1 >= d_rect.top() && m_y1 <= d_rect.bottom() )
+                points += QPointF( x, m_y1 );
+
+            const double m_y2 = pos.y() - off;
+            if ( m_y2 >= d_rect.top() && m_y2 <= d_rect.bottom() )
+                points += QPointF( x, m_y2 );
+        }
+    }
+    else
+    {
+        const double y = ( edge == Top ) ? d_rect.top() : d_rect.bottom();
+        if ( qAbs( pos.y() - y ) < radius )
+        {
+            const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.y() - y ) );
+            const double x1 = pos.x() + off;
+            if ( x1 >= d_rect.left() && x1 <= d_rect.right() )
+                points += QPointF( x1, y );
+
+            const double m_x2 = pos.x() - off;
+            if ( m_x2 >= d_rect.left() && m_x2 <= d_rect.right() )
+                points += QPointF( m_x2, y );
+        }
+    }
+    return points;
+}
+
+/*!
+   Sutherland-Hodgman polygon clipping
+
+   \param clipRect Clip rectangle
+   \param polygon Polygon
+   \param closePolygon True, when the polygon is closed
+
+   \return Clipped polygon
+*/
+QPolygon QwtClipper::clipPolygon(
+    const QRectF &clipRect, const QPolygon &polygon, bool closePolygon )
+{
+    const int minX = qCeil( clipRect.left() );
+    const int maxX = qFloor( clipRect.right() );
+    const int minY = qCeil( clipRect.top() );
+    const int maxY = qFloor( clipRect.bottom() );
+
+    const QRect r( minX, minY, maxX - minX, maxY - minY );
+
+    QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( r );
+    return clipper.clipPolygon( polygon, closePolygon );
+}
+/*!
+   Sutherland-Hodgman polygon clipping
+
+   \param clipRect Clip rectangle
+   \param polygon Polygon
+   \param closePolygon True, when the polygon is closed
+
+   \return Clipped polygon
+*/
+QPolygon QwtClipper::clipPolygon(
+    const QRect &clipRect, const QPolygon &polygon, bool closePolygon )
+{
+    QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( clipRect );
+    return clipper.clipPolygon( polygon, closePolygon );
+}
+
+/*!
+   Sutherland-Hodgman polygon clipping
+
+   \param clipRect Clip rectangle
+   \param polygon Polygon
+   \param closePolygon True, when the polygon is closed
+
+   \return Clipped polygon
+*/
+QPolygonF QwtClipper::clipPolygonF(
+    const QRectF &clipRect, const QPolygonF &polygon, bool closePolygon )
+{
+    QwtPolygonClipper<QPolygonF, QRectF, QPointF, double> clipper( clipRect );
+    return clipper.clipPolygon( polygon, closePolygon );
+}
+
+/*!
+   Circle clipping
+
+   clipCircle() divides a circle into intervals of angles representing arcs
+   of the circle. When the circle is completely inside the clip rectangle
+   an interval [0.0, 2 * M_PI] is returned.
+
+   \param clipRect Clip rectangle
+   \param center Center of the circle
+   \param radius Radius of the circle
+
+   \return Arcs of the circle
+*/
+QVector<QwtInterval> QwtClipper::clipCircle( const QRectF &clipRect,
+    const QPointF &center, double radius )
+{
+    QwtCircleClipper clipper( clipRect );
+    return clipper.clipCircle( center, radius );
+}
diff --git a/source/third_party/qwt/qwt_color_map.cpp b/source/third_party/qwt/qwt_color_map.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d0a9eae1f3d8a1a0b53746e9847c05f76e911e5e
--- /dev/null
+++ b/source/third_party/qwt/qwt_color_map.cpp
@@ -0,0 +1,499 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_color_map.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_interval.h"
+#include <qnumeric.h>
+
+class QwtLinearColorMap::ColorStops
+{
+public:
+    ColorStops():
+        d_doAlpha( false )
+    {
+        d_stops.reserve( 256 );
+    }
+
+    void insert( double pos, const QColor &color );
+    QRgb rgb( QwtLinearColorMap::Mode, double pos ) const;
+
+    QVector<double> stops() const;
+
+private:
+
+    class ColorStop
+    {
+    public:
+        ColorStop():
+            pos( 0.0 ),
+            rgb( 0 )
+        {
+        };
+
+        ColorStop( double p, const QColor &c ):
+            pos( p ),
+            rgb( c.rgba() )
+        {
+            r = qRed( rgb );
+            g = qGreen( rgb );
+            b = qBlue( rgb );
+            a = qAlpha( rgb );
+
+            /* 
+                when mapping a value to rgb we will have to calcualate: 
+                   - const int v = int( ( s1.v0 + ratio * s1.vStep ) + 0.5 );
+
+                Thus adding 0.5 ( for rounding ) can be done in advance
+             */
+            r0 = r + 0.5;
+            g0 = g + 0.5;
+            b0 = b + 0.5;
+            a0 = a + 0.5;
+
+            rStep = gStep = bStep = aStep = 0.0;
+            posStep = 0.0;
+        }
+
+        void updateSteps( const ColorStop &nextStop )
+        {
+            rStep = nextStop.r - r;
+            gStep = nextStop.g - g;
+            bStep = nextStop.b - b;
+            aStep = nextStop.a - a;
+            posStep = nextStop.pos - pos;
+        }
+
+        double pos;
+        QRgb rgb;
+        int r, g, b, a;
+
+        // precalculated values
+        double rStep, gStep, bStep, aStep;
+        double r0, g0, b0, a0;
+        double posStep;
+    };
+
+    inline int findUpper( double pos ) const;
+    QVector<ColorStop> d_stops;
+    bool d_doAlpha;
+};
+
+void QwtLinearColorMap::ColorStops::insert( double pos, const QColor &color )
+{
+    // Lookups need to be very fast, insertions are not so important.
+    // Anyway, a balanced tree is what we need here. TODO ...
+
+    if ( pos < 0.0 || pos > 1.0 )
+        return;
+
+    int index;
+    if ( d_stops.size() == 0 )
+    {
+        index = 0;
+        d_stops.resize( 1 );
+    }
+    else
+    {
+        index = findUpper( pos );
+        if ( index == d_stops.size() ||
+                qAbs( d_stops[index].pos - pos ) >= 0.001 )
+        {
+            d_stops.resize( d_stops.size() + 1 );
+            for ( int i = d_stops.size() - 1; i > index; i-- )
+                d_stops[i] = d_stops[i-1];
+        }
+    }
+
+    d_stops[index] = ColorStop( pos, color );
+    if ( color.alpha() != 255 )
+        d_doAlpha = true;
+
+    if ( index > 0 )
+        d_stops[index-1].updateSteps( d_stops[index] );
+
+    if ( index < d_stops.size() - 1 )
+        d_stops[index].updateSteps( d_stops[index+1] );
+}
+
+inline QVector<double> QwtLinearColorMap::ColorStops::stops() const
+{
+    QVector<double> positions( d_stops.size() );
+    for ( int i = 0; i < d_stops.size(); i++ )
+        positions[i] = d_stops[i].pos;
+    return positions;
+}
+
+inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const
+{
+    int index = 0;
+    int n = d_stops.size();
+
+    const ColorStop *stops = d_stops.data();
+
+    while ( n > 0 )
+    {
+        const int half = n >> 1;
+        const int middle = index + half;
+
+        if ( stops[middle].pos <= pos )
+        {
+            index = middle + 1;
+            n -= half + 1;
+        }
+        else
+            n = half;
+    }
+
+    return index;
+}
+
+inline QRgb QwtLinearColorMap::ColorStops::rgb(
+    QwtLinearColorMap::Mode mode, double pos ) const
+{
+    if ( pos <= 0.0 )
+        return d_stops[0].rgb;
+    if ( pos >= 1.0 )
+        return d_stops[ d_stops.size() - 1 ].rgb;
+
+    const int index = findUpper( pos );
+    if ( mode == FixedColors )
+    {
+        return d_stops[index-1].rgb;
+    }
+    else
+    {
+        const ColorStop &s1 = d_stops[index-1];
+
+        const double ratio = ( pos - s1.pos ) / ( s1.posStep );
+
+        const int r = int( s1.r0 + ratio * s1.rStep );
+        const int g = int( s1.g0 + ratio * s1.gStep );
+        const int b = int( s1.b0 + ratio * s1.bStep );
+
+        if ( d_doAlpha )
+        {
+            if ( s1.aStep )
+            {
+                const int a = int( s1.a0 + ratio * s1.aStep );
+                return qRgba( r, g, b, a );
+            }
+            else
+            {
+                return qRgba( r, g, b, s1.a );
+            }
+        }
+        else
+        {
+            return qRgb( r, g, b );
+        }
+    }
+}
+
+//! Constructor
+QwtColorMap::QwtColorMap( Format format ):
+    d_format( format )
+{
+}
+
+//! Destructor
+QwtColorMap::~QwtColorMap()
+{
+}
+
+/*!
+   Build and return a color map of 256 colors
+
+   The color table is needed for rendering indexed images in combination
+   with using colorIndex().
+
+   \param interval Range for the values
+   \return A color table, that can be used for a QImage
+*/
+QVector<QRgb> QwtColorMap::colorTable( const QwtInterval &interval ) const
+{
+    QVector<QRgb> table( 256 );
+
+    if ( interval.isValid() )
+    {
+        const double step = interval.width() / ( table.size() - 1 );
+        for ( int i = 0; i < table.size(); i++ )
+            table[i] = rgb( interval, interval.minValue() + step * i );
+    }
+
+    return table;
+}
+
+class QwtLinearColorMap::PrivateData
+{
+public:
+    ColorStops colorStops;
+    QwtLinearColorMap::Mode mode;
+};
+
+/*!
+   Build a color map with two stops at 0.0 and 1.0. The color
+   at 0.0 is Qt::blue, at 1.0 it is Qt::yellow.
+
+   \param format Preferred format of the color map
+*/
+QwtLinearColorMap::QwtLinearColorMap( QwtColorMap::Format format ):
+    QwtColorMap( format )
+{
+    d_data = new PrivateData;
+    d_data->mode = ScaledColors;
+
+    setColorInterval( Qt::blue, Qt::yellow );
+}
+
+/*!
+   Build a color map with two stops at 0.0 and 1.0.
+
+   \param color1 Color used for the minimum value of the value interval
+   \param color2 Color used for the maximum value of the value interval
+   \param format Preferred format for the color map
+*/
+QwtLinearColorMap::QwtLinearColorMap( const QColor &color1,
+        const QColor &color2, QwtColorMap::Format format ):
+    QwtColorMap( format )
+{
+    d_data = new PrivateData;
+    d_data->mode = ScaledColors;
+    setColorInterval( color1, color2 );
+}
+
+//! Destructor
+QwtLinearColorMap::~QwtLinearColorMap()
+{
+    delete d_data;
+}
+
+/*!
+   \brief Set the mode of the color map
+
+   FixedColors means the color is calculated from the next lower
+   color stop. ScaledColors means the color is calculated
+   by interpolating the colors of the adjacent stops.
+
+   \sa mode()
+*/
+void QwtLinearColorMap::setMode( Mode mode )
+{
+    d_data->mode = mode;
+}
+
+/*!
+   \return Mode of the color map
+   \sa setMode()
+*/
+QwtLinearColorMap::Mode QwtLinearColorMap::mode() const
+{
+    return d_data->mode;
+}
+
+/*!
+   Set the color range
+
+   Add stops at 0.0 and 1.0.
+
+   \param color1 Color used for the minimum value of the value interval
+   \param color2 Color used for the maximum value of the value interval
+
+   \sa color1(), color2()
+*/
+void QwtLinearColorMap::setColorInterval(
+    const QColor &color1, const QColor &color2 )
+{
+    d_data->colorStops = ColorStops();
+    d_data->colorStops.insert( 0.0, color1 );
+    d_data->colorStops.insert( 1.0, color2 );
+}
+
+/*!
+   Add a color stop
+
+   The value has to be in the range [0.0, 1.0].
+   F.e. a stop at position 17.0 for a range [10.0,20.0] must be
+   passed as: (17.0 - 10.0) / (20.0 - 10.0)
+
+   \param value Value between [0.0, 1.0]
+   \param color Color stop
+*/
+void QwtLinearColorMap::addColorStop( double value, const QColor& color )
+{
+    if ( value >= 0.0 && value <= 1.0 )
+        d_data->colorStops.insert( value, color );
+}
+
+/*!
+   \return Positions of color stops in increasing order
+*/
+QVector<double> QwtLinearColorMap::colorStops() const
+{
+    return d_data->colorStops.stops();
+}
+
+/*!
+  \return the first color of the color range
+  \sa setColorInterval()
+*/
+QColor QwtLinearColorMap::color1() const
+{
+    return QColor( d_data->colorStops.rgb( d_data->mode, 0.0 ) );
+}
+
+/*!
+  \return the second color of the color range
+  \sa setColorInterval()
+*/
+QColor QwtLinearColorMap::color2() const
+{
+    return QColor( d_data->colorStops.rgb( d_data->mode, 1.0 ) );
+}
+
+/*!
+  Map a value of a given interval into a RGB value
+
+  \param interval Range for all values
+  \param value Value to map into a RGB value
+
+  \return RGB value for value
+*/
+QRgb QwtLinearColorMap::rgb(
+    const QwtInterval &interval, double value ) const
+{
+    if ( qIsNaN(value) )
+        return 0u;
+
+    const double width = interval.width();
+    if ( width <= 0.0 )
+        return 0u;
+
+    const double ratio = ( value - interval.minValue() ) / width;
+    return d_data->colorStops.rgb( d_data->mode, ratio );
+}
+
+/*!
+  \brief Map a value of a given interval into a color index
+
+  \param interval Range for all values
+  \param value Value to map into a color index
+
+  \return Index, between 0 and 255
+*/
+unsigned char QwtLinearColorMap::colorIndex(
+    const QwtInterval &interval, double value ) const
+{
+    const double width = interval.width();
+
+    if ( qIsNaN(value) || width <= 0.0 || value <= interval.minValue() )
+        return 0;
+
+    if ( value >= interval.maxValue() )
+        return 255;
+
+    const double ratio = ( value - interval.minValue() ) / width;
+
+    unsigned char index;
+    if ( d_data->mode == FixedColors )
+        index = static_cast<unsigned char>( ratio * 255 ); // always floor
+    else
+        index = static_cast<unsigned char>( ratio * 255 + 0.5 );
+
+    return index;
+}
+
+class QwtAlphaColorMap::PrivateData
+{
+public:
+    QColor color;
+    QRgb rgb;
+    QRgb rgbMax;
+};
+
+
+/*!
+   Constructor
+   \param color Color of the map
+*/
+QwtAlphaColorMap::QwtAlphaColorMap( const QColor &color ):
+    QwtColorMap( QwtColorMap::RGB )
+{
+    d_data = new PrivateData;
+    setColor( color );
+}
+
+//! Destructor
+QwtAlphaColorMap::~QwtAlphaColorMap()
+{
+    delete d_data;
+}
+
+/*!
+   Set the color
+
+   \param color Color
+   \sa color()
+*/
+void QwtAlphaColorMap::setColor( const QColor &color )
+{
+    d_data->color = color;
+    d_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 );
+    d_data->rgbMax = d_data->rgb | ( 255 << 24 );
+}
+
+/*!
+  \return the color
+  \sa setColor()
+*/
+QColor QwtAlphaColorMap::color() const
+{
+    return d_data->color;
+}
+
+/*!
+  \brief Map a value of a given interval into a alpha value
+
+  alpha := (value - interval.minValue()) / interval.width();
+
+  \param interval Range for all values
+  \param value Value to map into a RGB value
+  \return RGB value, with an alpha value
+*/
+QRgb QwtAlphaColorMap::rgb( const QwtInterval &interval, double value ) const
+{
+    if ( qIsNaN(value) )
+        return 0u;
+
+    const double width = interval.width();
+    if ( width <= 0.0 )
+        return 0u;
+
+    if ( value <= interval.minValue() )
+        return d_data->rgb;
+
+    if ( value >= interval.maxValue() )
+        return d_data->rgbMax;
+
+    const double ratio = ( value - interval.minValue() ) / width;
+    return d_data->rgb | ( qRound( 255 * ratio ) << 24 );
+}
+
+/*!
+  Dummy function, needed to be implemented as it is pure virtual
+  in QwtColorMap. Color indices make no sense in combination with
+  an alpha channel.
+
+  \return Always 0
+*/
+unsigned char QwtAlphaColorMap::colorIndex(
+    const QwtInterval &, double ) const
+{
+    return 0;
+}
diff --git a/source/third_party/qwt/qwt_column_symbol.cpp b/source/third_party/qwt/qwt_column_symbol.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..978054d22a0ed9fe3207308ec8bcc7ca5aaaf838
--- /dev/null
+++ b/source/third_party/qwt/qwt_column_symbol.cpp
@@ -0,0 +1,293 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_column_symbol.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+#include <qpalette.h>
+
+static void qwtDrawBox( QPainter *p, const QRectF &rect,
+    const QPalette &pal, double lw )
+{
+    if ( lw > 0.0 )
+    {
+        if ( rect.width() == 0.0 )
+        {
+            p->setPen( pal.dark().color() );
+            p->drawLine( rect.topLeft(), rect.bottomLeft() );
+            return;
+        }
+
+        if ( rect.height() == 0.0 )
+        {
+            p->setPen( pal.dark().color() );
+            p->drawLine( rect.topLeft(), rect.topRight() );
+            return;
+        }
+
+        lw = qMin( lw, rect.height() / 2.0 - 1.0 );
+        lw = qMin( lw, rect.width() / 2.0 - 1.0 );
+
+        const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 );
+        QPolygonF polygon( outerRect );
+
+        if ( outerRect.width() > 2 * lw &&
+                outerRect.height() > 2 * lw )
+        {
+            const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw );
+            polygon = polygon.subtracted( innerRect );
+        }
+
+        p->setPen( Qt::NoPen );
+
+        p->setBrush( pal.dark() );
+        p->drawPolygon( polygon );
+    }
+
+    const QRectF windowRect = rect.adjusted( lw, lw, -lw + 1, -lw + 1 );
+    if ( windowRect.isValid() )
+        p->fillRect( windowRect, pal.window() );
+}
+
+static void qwtDrawPanel( QPainter *painter, const QRectF &rect,
+    const QPalette &pal, double lw )
+{
+    if ( lw > 0.0 )
+    {
+        if ( rect.width() == 0.0 )
+        {
+            painter->setPen( pal.window().color() );
+            painter->drawLine( rect.topLeft(), rect.bottomLeft() );
+            return;
+        }
+
+        if ( rect.height() == 0.0 )
+        {
+            painter->setPen( pal.window().color() );
+            painter->drawLine( rect.topLeft(), rect.topRight() );
+            return;
+        }
+
+        lw = qMin( lw, rect.height() / 2.0 - 1.0 );
+        lw = qMin( lw, rect.width() / 2.0 - 1.0 );
+
+        const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 );
+        const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw );
+
+        QPolygonF lines[2];
+
+        lines[0] += outerRect.bottomLeft();
+        lines[0] += outerRect.topLeft();
+        lines[0] += outerRect.topRight();
+        lines[0] += innerRect.topRight();
+        lines[0] += innerRect.topLeft();
+        lines[0] += innerRect.bottomLeft();
+
+        lines[1] += outerRect.topRight();
+        lines[1] += outerRect.bottomRight();
+        lines[1] += outerRect.bottomLeft();
+        lines[1] += innerRect.bottomLeft();
+        lines[1] += innerRect.bottomRight();
+        lines[1] += innerRect.topRight();
+
+        painter->setPen( Qt::NoPen );
+
+        painter->setBrush( pal.light() );
+        painter->drawPolygon( lines[0] );
+        painter->setBrush( pal.dark() );
+        painter->drawPolygon( lines[1] );
+    }
+
+    painter->fillRect( rect.adjusted( lw, lw, -lw + 1, -lw + 1 ), pal.window() );
+}
+
+class QwtColumnSymbol::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtColumnSymbol::Box ),
+        frameStyle( QwtColumnSymbol::Raised ),
+        lineWidth( 2 )
+    {
+        palette = QPalette( Qt::gray );
+    }
+
+    QwtColumnSymbol::Style style;
+    QwtColumnSymbol::FrameStyle frameStyle;
+
+    QPalette palette;
+    int lineWidth;
+};
+
+/*!
+  Constructor
+
+  \param style Style of the symbol
+  \sa setStyle(), style(), Style
+*/
+QwtColumnSymbol::QwtColumnSymbol( Style style )
+{
+    d_data = new PrivateData();
+    d_data->style = style;
+}
+
+//! Destructor
+QwtColumnSymbol::~QwtColumnSymbol()
+{
+    delete d_data;
+}
+
+/*!
+  Specify the symbol style
+
+  \param style Style
+  \sa style(), setPalette()
+*/
+void QwtColumnSymbol::setStyle( Style style )
+{
+    d_data->style = style;
+}
+
+/*!
+  \return Current symbol style
+  \sa setStyle()
+*/
+QwtColumnSymbol::Style QwtColumnSymbol::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  Assign a palette for the symbol
+
+  \param palette Palette
+  \sa palette(), setStyle()
+*/
+void QwtColumnSymbol::setPalette( const QPalette &palette )
+{
+    d_data->palette = palette;
+}
+
+/*!
+  \return Current palette
+  \sa setPalette()
+*/
+const QPalette& QwtColumnSymbol::palette() const
+{
+    return d_data->palette;
+}
+
+/*!
+   Set the frame, that is used for the Box style.
+
+   \param frameStyle Frame style
+   \sa frameStyle(), setLineWidth(), setStyle()
+*/
+void QwtColumnSymbol::setFrameStyle( FrameStyle frameStyle )
+{
+    d_data->frameStyle = frameStyle;
+}
+
+/*!
+  \return Current frame style, that is used for the Box style.
+  \sa setFrameStyle(), lineWidth(), setStyle()
+*/
+QwtColumnSymbol::FrameStyle QwtColumnSymbol::frameStyle() const
+{
+    return d_data->frameStyle;
+}
+
+/*!
+   Set the line width of the frame, that is used for the Box style.
+
+   \param width Width
+   \sa lineWidth(), setFrameStyle()
+*/
+void QwtColumnSymbol::setLineWidth( int width )
+{
+    if ( width < 0 )
+        width = 0;
+
+    d_data->lineWidth = width;
+}
+
+/*!
+  \return Line width of the frame, that is used for the Box style.
+  \sa setLineWidth(), frameStyle(), setStyle()
+*/
+int QwtColumnSymbol::lineWidth() const
+{
+    return d_data->lineWidth;
+}
+
+/*!
+  Draw the symbol depending on its style.
+
+  \param painter Painter
+  \param rect Directed rectangle
+
+  \sa drawBox()
+*/
+void QwtColumnSymbol::draw( QPainter *painter,
+    const QwtColumnRect &rect ) const
+{
+    painter->save();
+
+    switch ( d_data->style )
+    {
+        case QwtColumnSymbol::Box:
+        {
+            drawBox( painter, rect );
+            break;
+        }
+        default:;
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw the symbol when it is in Box style.
+
+  \param painter Painter
+  \param rect Directed rectangle
+
+  \sa draw()
+*/
+void QwtColumnSymbol::drawBox( QPainter *painter,
+    const QwtColumnRect &rect ) const
+{
+    QRectF r = rect.toRect();
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        r.setLeft( qRound( r.left() ) );
+        r.setRight( qRound( r.right() ) );
+        r.setTop( qRound( r.top() ) );
+        r.setBottom( qRound( r.bottom() ) );
+    }
+
+    switch ( d_data->frameStyle )
+    {
+        case QwtColumnSymbol::Raised:
+        {
+            qwtDrawPanel( painter, r, d_data->palette, d_data->lineWidth );
+            break;
+        }
+        case QwtColumnSymbol::Plain:
+        {
+            qwtDrawBox( painter, r, d_data->palette, d_data->lineWidth );
+            break;
+        }
+        default:
+        {
+            painter->fillRect( r, d_data->palette.window() );
+        }
+    }
+}
diff --git a/source/third_party/qwt/qwt_compass.cpp b/source/third_party/qwt/qwt_compass.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9305f031f1fea5e712267e2131276461911adb67
--- /dev/null
+++ b/source/third_party/qwt/qwt_compass.cpp
@@ -0,0 +1,308 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_compass.h"
+#include "qwt/qwt_compass_rose.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_scale_draw.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_dial_needle.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qevent.h>
+
+/*! 
+  \brief Constructor
+
+  Initializes a label map for multiples of 45 degrees
+ */
+QwtCompassScaleDraw::QwtCompassScaleDraw()
+{
+    enableComponent( QwtAbstractScaleDraw::Backbone, false );
+    enableComponent( QwtAbstractScaleDraw::Ticks, false );
+
+    d_labelMap.insert( 0.0, QString::fromLatin1( "N" ) );
+    d_labelMap.insert( 45.0, QString::fromLatin1( "NE" ) );
+    d_labelMap.insert( 90.0, QString::fromLatin1( "E" ) );
+    d_labelMap.insert( 135.0, QString::fromLatin1( "SE" ) );
+    d_labelMap.insert( 180.0, QString::fromLatin1( "S" ) );
+    d_labelMap.insert( 225.0, QString::fromLatin1( "SW" ) );
+    d_labelMap.insert( 270.0, QString::fromLatin1( "W" ) );
+    d_labelMap.insert( 315.0, QString::fromLatin1( "NW" ) );
+
+#if 0
+    d_labelMap.insert( 22.5, QString::fromLatin1( "NNE" ) );
+    d_labelMap.insert( 67.5, QString::fromLatin1( "NEE" ) );
+    d_labelMap.insert( 112.5, QString::fromLatin1( "SEE" ) );
+    d_labelMap.insert( 157.5, QString::fromLatin1( "SSE" ) );
+    d_labelMap.insert( 202.5, QString::fromLatin1( "SSW" ) );
+    d_labelMap.insert( 247.5, QString::fromLatin1( "SWW" ) );
+    d_labelMap.insert( 292.5, QString::fromLatin1( "NWW" ) );
+    d_labelMap.insert( 337.5, QString::fromLatin1( "NNW" ) );
+#endif
+}
+
+/*! 
+  \brief Constructor
+
+  \param map Value to label map
+ */
+QwtCompassScaleDraw::QwtCompassScaleDraw( const QMap<double, QString> &map ):
+    d_labelMap( map )
+{
+    enableComponent( QwtAbstractScaleDraw::Backbone, false );
+    enableComponent( QwtAbstractScaleDraw::Ticks, false );
+}
+
+/*!
+  \brief Set a map, mapping values to labels
+  \param map Value to label map
+
+  The values of the major ticks are found by looking into this
+  map. The default map consists of the labels N, NE, E, SE, S, SW, W, NW.
+
+  \warning The map will have no effect for values that are no major
+           tick values. Major ticks can be changed by QwtScaleDraw::setScale
+
+  \sa labelMap(), scaleDraw(), setScale()
+*/
+void QwtCompassScaleDraw::setLabelMap( const QMap<double, QString> &map )
+{
+    d_labelMap = map;
+}
+
+
+/*!
+  \return map, mapping values to labels
+  \sa setLabelMap()
+*/
+QMap<double, QString> QwtCompassScaleDraw::labelMap() const
+{
+    return d_labelMap;
+}
+
+/*!
+  Map a value to a corresponding label
+
+  \param value Value that will be mapped
+
+  label() looks in the labelMap() for a corresponding label for value
+  or returns an null text.
+
+  \return Label, or QString::null
+  \sa labelMap(), setLabelMap()
+*/
+
+QwtText QwtCompassScaleDraw::label( double value ) const
+{
+    if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+        value = 0.0;
+
+    if ( value < 0.0 )
+        value += 360.0;
+
+    if ( d_labelMap.contains( value ) )
+        return d_labelMap[value];
+
+    return QwtText();
+}
+
+class QwtCompass::PrivateData
+{
+public:
+    PrivateData():
+        rose( NULL )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete rose;
+    }
+
+    QwtCompassRose *rose;
+};
+
+/*!
+  \brief Constructor
+  \param parent Parent widget
+
+  Create a compass widget with a scale, no needle and no rose.
+  The default origin is 270.0 with no valid value. It accepts
+  mouse and keyboard inputs and has no step size. The default mode
+  is QwtDial::RotateNeedle.
+*/
+QwtCompass::QwtCompass( QWidget* parent ):
+    QwtDial( parent )
+{
+    d_data = new PrivateData;
+
+    setScaleDraw( new QwtCompassScaleDraw() );
+
+    setOrigin( 270.0 );
+    setWrapping( true );
+
+    setScaleMaxMajor( 36 );
+    setScaleMaxMinor( 10 );
+
+    setScale( 0.0, 360.0 ); // degrees as default
+    setTotalSteps( 360 );
+}
+
+//!  Destructor
+QwtCompass::~QwtCompass()
+{
+    delete d_data;
+}
+
+
+/*!
+   Draw the contents of the scale
+
+   \param painter Painter
+   \param center Center of the content circle
+   \param radius Radius of the content circle
+*/
+void QwtCompass::drawScaleContents( QPainter *painter,
+    const QPointF &center, double radius ) const
+{
+    QPalette::ColorGroup cg;
+    if ( isEnabled() )
+        cg = hasFocus() ? QPalette::Active : QPalette::Inactive;
+    else
+        cg = QPalette::Disabled;
+
+    double north = origin();
+    if ( isValid() )
+    {
+        if ( mode() == RotateScale )
+            north -= value();
+    }
+
+    const int margin = 4;
+    drawRose( painter, center, radius - margin, 360.0 - north,  cg );
+}
+
+/*!
+  Draw the compass rose
+
+  \param painter Painter
+  \param center Center of the compass
+  \param radius of the circle, where to paint the rose
+  \param north Direction pointing north, in degrees counter clockwise
+  \param cg Color group
+*/
+void QwtCompass::drawRose( QPainter *painter, const QPointF &center,
+    double radius, double north, QPalette::ColorGroup cg ) const
+{
+    if ( d_data->rose )
+        d_data->rose->draw( painter, center, radius, north,  cg );
+}
+
+/*!
+  Set a rose for the compass
+  \param rose Compass rose
+  \warning The rose will be deleted, when a different rose is
+    set or in ~QwtCompass
+  \sa rose()
+*/
+void QwtCompass::setRose( QwtCompassRose *rose )
+{
+    if ( rose != d_data->rose )
+    {
+        if ( d_data->rose )
+            delete d_data->rose;
+
+        d_data->rose = rose;
+        update();
+    }
+}
+
+/*!
+  \return rose
+  \sa setRose()
+*/
+const QwtCompassRose *QwtCompass::rose() const
+{
+    return d_data->rose;
+}
+
+/*!
+  \return rose
+  \sa setRose()
+*/
+QwtCompassRose *QwtCompass::rose()
+{
+    return d_data->rose;
+}
+
+/*!
+  Handles key events
+
+  Beside the keys described in QwtDial::keyPressEvent numbers
+  from 1-9 (without 5) set the direction according to their
+  position on the num pad.
+
+  \sa isReadOnly()
+*/
+void QwtCompass::keyPressEvent( QKeyEvent *kev )
+{
+    if ( isReadOnly() )
+        return;
+
+#if 0
+    if ( kev->key() == Key_5 )
+    {
+        invalidate(); // signal ???
+        return;
+    }
+#endif
+
+    double newValue = value();
+
+    if ( kev->key() >= Qt::Key_1 && kev->key() <= Qt::Key_9 )
+    {
+        if ( mode() != RotateNeedle || kev->key() == Qt::Key_5 )
+            return;
+
+        switch ( kev->key() )
+        {
+            case Qt::Key_6:
+                newValue = 180.0 * 0.0;
+                break;
+            case Qt::Key_3:
+                newValue = 180.0 * 0.25;
+                break;
+            case Qt::Key_2:
+                newValue = 180.0 * 0.5;
+                break;
+            case Qt::Key_1:
+                newValue = 180.0 * 0.75;
+                break;
+            case Qt::Key_4:
+                newValue = 180.0 * 1.0;
+                break;
+            case Qt::Key_7:
+                newValue = 180.0 * 1.25;
+                break;
+            case Qt::Key_8:
+                newValue = 180.0 * 1.5;
+                break;
+            case Qt::Key_9:
+                newValue = 180.0 * 1.75;
+                break;
+        }
+        newValue -= origin();
+        setValue( newValue );
+    }
+    else
+    {
+        QwtDial::keyPressEvent( kev );
+    }
+}
diff --git a/source/third_party/qwt/qwt_compass_rose.cpp b/source/third_party/qwt/qwt_compass_rose.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b81e09181abcb54e8979979dc23b433f046963c3
--- /dev/null
+++ b/source/third_party/qwt/qwt_compass_rose.cpp
@@ -0,0 +1,269 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_compass_rose.h"
+#include "qwt/qwt_point_polar.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+
+static QPointF qwtIntersection( 
+    QPointF p11, QPointF p12, QPointF p21, QPointF p22 )
+{
+    const QLineF line1( p11, p12 );
+    const QLineF line2( p21, p22 );
+
+    QPointF pos;
+    if ( line1.intersect( line2, &pos ) == QLineF::NoIntersection )
+        return QPointF();
+
+    return pos;
+}
+
+class QwtSimpleCompassRose::PrivateData
+{
+public:
+    PrivateData():
+        width( 0.2 ),
+        numThorns( 8 ),
+        numThornLevels( -1 ),
+        shrinkFactor( 0.9 )
+    {
+    }
+
+    double width;
+    int numThorns;
+    int numThornLevels;
+    double shrinkFactor;
+};
+
+/*!
+   Constructor
+
+   \param numThorns Number of thorns
+   \param numThornLevels Number of thorn levels
+*/
+QwtSimpleCompassRose::QwtSimpleCompassRose(
+    int numThorns, int numThornLevels )
+{
+    d_data = new PrivateData();
+    d_data->numThorns = numThorns;
+    d_data->numThornLevels = numThornLevels;
+
+    const QColor dark( 128, 128, 255 );
+    const QColor light( 192, 255, 255 );
+
+    QPalette palette;
+    palette.setColor( QPalette::Dark, dark );
+    palette.setColor( QPalette::Light, light );
+
+    setPalette( palette );
+}
+
+//! Destructor
+QwtSimpleCompassRose::~QwtSimpleCompassRose()
+{
+    delete d_data;
+}
+
+/*!
+  Set the Factor how to shrink the thorns with each level
+  The default value is 0.9.
+
+  \param factor Shrink factor
+  \sa shrinkFactor()
+*/
+void QwtSimpleCompassRose::setShrinkFactor( double factor )
+{
+    d_data->shrinkFactor = factor;
+}
+
+/*!
+  \return Factor how to shrink the thorns with each level
+  \sa setShrinkFactor()
+*/
+double QwtSimpleCompassRose::shrinkFactor() const
+{
+    return d_data->shrinkFactor;
+}
+
+/*!
+   Draw the rose
+
+   \param painter Painter
+   \param center Center point
+   \param radius Radius of the rose
+   \param north Position
+   \param cg Color group
+*/
+void QwtSimpleCompassRose::draw( QPainter *painter, const QPointF &center,
+    double radius, double north, QPalette::ColorGroup cg ) const
+{
+    QPalette pal = palette();
+    pal.setCurrentColorGroup( cg );
+
+    drawRose( painter, pal, center, radius, north, d_data->width,
+        d_data->numThorns, d_data->numThornLevels, d_data->shrinkFactor );
+}
+
+/*!
+   Draw the rose
+
+   \param painter Painter
+   \param palette Palette
+   \param center Center of the rose
+   \param radius Radius of the rose
+   \param north Position pointing to north
+   \param width Width of the rose
+   \param numThorns Number of thorns
+   \param numThornLevels Number of thorn levels
+   \param shrinkFactor Factor to shrink the thorns with each level
+*/
+void QwtSimpleCompassRose::drawRose(
+    QPainter *painter,
+    const QPalette &palette,
+    const QPointF &center, double radius, double north, double width,
+    int numThorns, int numThornLevels, double shrinkFactor )
+{
+    if ( numThorns < 4 )
+        numThorns = 4;
+
+    if ( numThorns % 4 )
+        numThorns += 4 - numThorns % 4;
+
+    if ( numThornLevels <= 0 )
+        numThornLevels = numThorns / 4;
+
+    if ( shrinkFactor >= 1.0 )
+        shrinkFactor = 1.0;
+
+    if ( shrinkFactor <= 0.5 )
+        shrinkFactor = 0.5;
+
+    painter->save();
+
+    painter->setPen( Qt::NoPen );
+
+    for ( int j = 1; j <= numThornLevels; j++ )
+    {
+        double step =  qPow( 2.0, j ) * M_PI / numThorns;
+        if ( step > M_PI_2 )
+            break;
+
+        double r = radius;
+        for ( int k = 0; k < 3; k++ )
+        {
+            if ( j + k < numThornLevels )
+                r *= shrinkFactor;
+        }
+
+        double leafWidth = r * width;
+        if ( 2.0 * M_PI / step > 32 )
+            leafWidth = 16;
+
+        const double origin = qwtRadians( north );
+        for ( double angle = origin;
+            angle < 2.0 * M_PI + origin; angle += step )
+        {
+            const QPointF p = qwtPolar2Pos( center, r, angle );
+            const QPointF p1 = qwtPolar2Pos( center, leafWidth, angle + M_PI_2 );
+            const QPointF p2 = qwtPolar2Pos( center, leafWidth, angle - M_PI_2 );
+            const QPointF p3 = qwtPolar2Pos( center, r, angle + step / 2.0 );
+            const QPointF p4 = qwtPolar2Pos( center, r, angle - step / 2.0 );
+
+            QPainterPath darkPath;
+            darkPath.moveTo( center );
+            darkPath.lineTo( p );
+            darkPath.lineTo( qwtIntersection( center, p3, p1, p ) );
+
+            painter->setBrush( palette.brush( QPalette::Dark ) );
+            painter->drawPath( darkPath );
+
+            QPainterPath lightPath;
+            lightPath.moveTo( center );
+            lightPath.lineTo( p );
+            lightPath.lineTo( qwtIntersection( center, p4, p2, p ) );
+
+            painter->setBrush( palette.brush( QPalette::Light ) );
+            painter->drawPath( lightPath );
+        }
+    }
+    painter->restore();
+}
+
+/*!
+   Set the width of the rose heads. Lower value make thinner heads.
+   The range is limited from 0.03 to 0.4.
+
+   \param width Width
+*/
+void QwtSimpleCompassRose::setWidth( double width )
+{
+    d_data->width = width;
+    if ( d_data->width < 0.03 )
+        d_data->width = 0.03;
+
+    if ( d_data->width > 0.4 )
+        d_data->width = 0.4;
+}
+
+/*! 
+  \return Width of the rose
+  \sa setWidth()
+ */
+double QwtSimpleCompassRose::width() const
+{
+    return d_data->width;
+}
+
+/*!
+  Set the number of thorns on one level
+  The number is aligned to a multiple of 4, with a minimum of 4
+
+  \param numThorns Number of thorns
+  \sa numThorns(), setNumThornLevels()
+*/
+void QwtSimpleCompassRose::setNumThorns( int numThorns )
+{
+    if ( numThorns < 4 )
+        numThorns = 4;
+
+    if ( numThorns % 4 )
+        numThorns += 4 - numThorns % 4;
+
+    d_data->numThorns = numThorns;
+}
+
+/*!
+   \return Number of thorns
+   \sa setNumThorns(), setNumThornLevels()
+*/
+int QwtSimpleCompassRose::numThorns() const
+{
+    return d_data->numThorns;
+}
+
+/*!
+  Set the of thorns levels
+
+  \param numThornLevels Number of thorns levels
+  \sa setNumThorns(), numThornLevels()
+*/
+void QwtSimpleCompassRose::setNumThornLevels( int numThornLevels )
+{
+    d_data->numThornLevels = numThornLevels;
+}
+
+/*!
+   \return Number of thorn levels
+   \sa setNumThorns(), setNumThornLevels()
+*/
+int QwtSimpleCompassRose::numThornLevels() const
+{
+    return d_data->numThornLevels;
+}
diff --git a/source/third_party/qwt/qwt_counter.cpp b/source/third_party/qwt/qwt_counter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cdaa28d54720d6a4b2e7715f1ee7cfd1c247dddb
--- /dev/null
+++ b/source/third_party/qwt/qwt_counter.cpp
@@ -0,0 +1,785 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_arrow_button.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_counter.h"
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qvalidator.h>
+#include <qevent.h>
+#include <qstyle.h>
+
+class QwtCounter::PrivateData
+{
+public:
+    PrivateData():
+        minimum( 0.0 ),
+        maximum( 0.0 ),
+        singleStep( 1.0 ),
+        isValid( false ),
+        value( 0.0 ),
+        wrapping( false )
+    {
+        increment[Button1] = 1;
+        increment[Button2] = 10;
+        increment[Button3] = 100;
+    }
+
+    QwtArrowButton *buttonDown[ButtonCnt];
+    QwtArrowButton *buttonUp[ButtonCnt];
+    QLineEdit *valueEdit;
+
+    int increment[ButtonCnt];
+    int numButtons;
+
+    double minimum;
+    double maximum;
+    double singleStep;
+
+    bool isValid;
+    double value;
+
+    bool wrapping;
+};
+
+/*!
+  The counter is initialized with a range is set to [0.0, 1.0] with 
+  0.01 as single step size. The value is invalid.
+
+  The default number of buttons is set to 2. The default increments are:
+  \li Button 1: 1 step
+  \li Button 2: 10 steps
+  \li Button 3: 100 steps
+
+  \param parent
+ */
+QwtCounter::QwtCounter( QWidget *parent ):
+    QWidget( parent )
+{
+    initCounter();
+}
+
+void QwtCounter::initCounter()
+{
+    d_data = new PrivateData;
+
+    QHBoxLayout *layout = new QHBoxLayout( this );
+    layout->setSpacing( 0 );
+    layout->setMargin( 0 );
+
+    for ( int i = ButtonCnt - 1; i >= 0; i-- )
+    {
+        QwtArrowButton *btn =
+            new QwtArrowButton( i + 1, Qt::DownArrow, this );
+        btn->setFocusPolicy( Qt::NoFocus );
+        btn->installEventFilter( this );
+        layout->addWidget( btn );
+
+        connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) );
+        connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) );
+
+        d_data->buttonDown[i] = btn;
+    }
+
+    d_data->valueEdit = new QLineEdit( this );
+    d_data->valueEdit->setReadOnly( false );
+    d_data->valueEdit->setValidator( new QDoubleValidator( d_data->valueEdit ) );
+    layout->addWidget( d_data->valueEdit );
+
+    connect( d_data->valueEdit, SIGNAL( editingFinished() ),
+         SLOT( textChanged() ) );
+
+    layout->setStretchFactor( d_data->valueEdit, 10 );
+
+    for ( int i = 0; i < ButtonCnt; i++ )
+    {
+        QwtArrowButton *btn =
+            new QwtArrowButton( i + 1, Qt::UpArrow, this );
+        btn->setFocusPolicy( Qt::NoFocus );
+        btn->installEventFilter( this );
+        layout->addWidget( btn );
+
+        connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) );
+        connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) );
+
+        d_data->buttonUp[i] = btn;
+    }
+
+    setNumButtons( 2 );
+    setRange( 0.0, 1.0 );
+    setSingleStep( 0.001 );
+    setValue( 0.0 );
+
+    setSizePolicy(
+        QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
+
+    setFocusProxy( d_data->valueEdit );
+    setFocusPolicy( Qt::StrongFocus );
+}
+
+//! Destructor
+QwtCounter::~QwtCounter()
+{
+    delete d_data;
+}
+
+/*! 
+  Set the counter to be in valid/invalid state
+
+  When the counter is set to invalid, no numbers are displayed and
+  the buttons are disabled.
+
+  \param on If true the counter will be set as valid 
+
+  \sa setValue(), isValid()
+*/
+void QwtCounter::setValid( bool on )
+{
+    if ( on != d_data->isValid )
+    {
+        d_data->isValid = on;
+
+        updateButtons();
+
+        if ( d_data->isValid )
+        {
+            showNumber( value() );
+            Q_EMIT valueChanged( value() );
+        }
+        else
+        {
+            d_data->valueEdit->setText( QString::null );
+        }
+    }   
+}   
+
+/*! 
+  \return True, if the value is valid
+  \sa setValid(), setValue()
+ */
+bool QwtCounter::isValid() const
+{
+    return d_data->isValid;
+}   
+
+/*!
+  \brief Allow/disallow the user to manually edit the value
+
+  \param on True disable editing
+  \sa isReadOnly()
+*/
+void QwtCounter::setReadOnly( bool on )
+{
+    d_data->valueEdit->setReadOnly( on );
+}
+
+/*! 
+   \return True, when the line line edit is read only. (default is no)
+  \sa setReadOnly()
+ */
+bool QwtCounter::isReadOnly() const
+{
+    return d_data->valueEdit->isReadOnly();
+}
+
+/*!
+  \brief Set a new value without adjusting to the step raster
+
+  The state of the counter is set to be valid.
+
+  \param value New value
+
+  \sa isValid(), value(), valueChanged()
+  \warning The value is clipped when it lies outside the range.
+*/
+
+void QwtCounter::setValue( double value )
+{
+    const double vmin = qMin( d_data->minimum, d_data->maximum );
+    const double vmax = qMax( d_data->minimum, d_data->maximum );
+
+    value = qBound( vmin, value, vmax );
+
+    if ( !d_data->isValid || value != d_data->value )
+    {
+        d_data->isValid = true;
+        d_data->value = value;
+
+        showNumber( value );
+        updateButtons();
+
+        Q_EMIT valueChanged( value );
+    }
+}
+
+/*!
+  \return Current value of the counter
+  \sa setValue(), valueChanged()
+ */
+double QwtCounter::value() const
+{
+    return d_data->value;
+}
+
+/*!
+  \brief Set the minimum and maximum values
+
+  The maximum is adjusted if necessary to ensure that the range remains valid.
+  The value might be modified to be inside of the range.
+
+  \param min Minimum value
+  \param max Maximum value
+
+  \sa minimum(), maximum()
+ */
+void QwtCounter::setRange( double min, double max )
+{
+    max = qMax( min, max );
+
+    if ( d_data->maximum == max && d_data->minimum == min )
+        return;
+
+    d_data->minimum = min;
+    d_data->maximum = max;
+
+    setSingleStep( singleStep() );
+
+    const double value = qBound( min, d_data->value, max );
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+
+        if ( d_data->isValid )
+        {
+            showNumber( value );
+            Q_EMIT valueChanged( value );
+        }
+    }
+
+    updateButtons();
+}
+
+/*!
+  Set the minimum value of the range
+
+  \param value Minimum value
+  \sa setRange(), setMaximum(), minimum()
+
+  \note The maximum is adjusted if necessary to ensure that the range remains valid.
+*/
+void QwtCounter::setMinimum( double value )
+{
+    setRange( value, maximum() );
+}
+
+/*!
+  \return The minimum of the range
+  \sa setRange(), setMinimum(), maximum()
+*/
+double QwtCounter::minimum() const
+{
+    return d_data->minimum;
+}
+
+/*!
+  Set the maximum value of the range
+
+  \param value Maximum value
+  \sa setRange(), setMinimum(), maximum()
+*/
+void QwtCounter::setMaximum( double value )
+{
+    setRange( minimum(), value );
+}
+
+/*!
+  \return The maximum of the range
+  \sa setRange(), setMaximum(), minimum()
+*/
+double QwtCounter::maximum() const
+{
+    return d_data->maximum;
+}
+
+/*!
+  \brief Set the step size of the counter
+
+  A value <= 0.0 disables stepping
+
+  \param stepSize Single step size
+  \sa singleStep()
+*/
+void QwtCounter::setSingleStep( double stepSize )
+{
+    d_data->singleStep = qMax( stepSize, 0.0 );
+}
+
+/*!
+  \return Single step size
+  \sa setSingleStep()
+ */
+double QwtCounter::singleStep() const
+{
+    return d_data->singleStep;
+}
+
+/*!
+  \brief En/Disable wrapping
+
+  If wrapping is true stepping up from maximum() value will take 
+  you to the minimum() value and vice versa. 
+
+  \param on En/Disable wrapping
+  \sa wrapping()
+ */
+void QwtCounter::setWrapping( bool on )
+{
+    d_data->wrapping = on;
+}
+
+/*!
+  \return True, when wrapping is set
+  \sa setWrapping()
+ */
+bool QwtCounter::wrapping() const
+{
+    return d_data->wrapping;
+}
+
+/*!
+  Specify the number of buttons on each side of the label
+
+  \param numButtons Number of buttons
+  \sa numButtons()
+*/
+void QwtCounter::setNumButtons( int numButtons )
+{
+    if ( numButtons < 0 || numButtons > QwtCounter::ButtonCnt )
+        return;
+
+    for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
+    {
+        if ( i < numButtons )
+        {
+            d_data->buttonDown[i]->show();
+            d_data->buttonUp[i]->show();
+        }
+        else
+        {
+            d_data->buttonDown[i]->hide();
+            d_data->buttonUp[i]->hide();
+        }
+    }
+
+    d_data->numButtons = numButtons;
+}
+
+/*!
+  \return The number of buttons on each side of the widget.
+  \sa setNumButtons()
+*/
+int QwtCounter::numButtons() const
+{
+    return d_data->numButtons;
+}
+
+/*!
+  Specify the number of steps by which the value
+  is incremented or decremented when a specified button
+  is pushed.
+
+  \param button Button index
+  \param numSteps Number of steps
+
+  \sa incSteps()
+*/
+void QwtCounter::setIncSteps( QwtCounter::Button button, int numSteps )
+{
+    if ( button >= 0 && button < QwtCounter::ButtonCnt )
+        d_data->increment[ button ] = numSteps;
+}
+
+/*!
+  \return The number of steps by which a specified button increments the value
+          or 0 if the button is invalid.
+  \param button Button index
+
+  \sa setIncSteps()
+*/
+int QwtCounter::incSteps( QwtCounter::Button button ) const
+{
+    if ( button >= 0 && button < QwtCounter::ButtonCnt )
+        return d_data->increment[ button ];
+
+    return 0;
+}
+
+
+/*!
+  Set the number of increment steps for button 1
+  \param nSteps Number of steps
+*/
+void QwtCounter::setStepButton1( int nSteps )
+{
+    setIncSteps( QwtCounter::Button1, nSteps );
+}
+
+//! returns the number of increment steps for button 1
+int QwtCounter::stepButton1() const
+{
+    return incSteps( QwtCounter::Button1 );
+}
+
+/*!
+  Set the number of increment steps for button 2
+  \param nSteps Number of steps
+*/
+void QwtCounter::setStepButton2( int nSteps )
+{
+    setIncSteps( QwtCounter::Button2, nSteps );
+}
+
+//! returns the number of increment steps for button 2
+int QwtCounter::stepButton2() const
+{
+    return incSteps( QwtCounter::Button2 );
+}
+
+/*!
+  Set the number of increment steps for button 3
+  \param nSteps Number of steps
+*/
+void QwtCounter::setStepButton3( int nSteps )
+{
+    setIncSteps( QwtCounter::Button3, nSteps );
+}
+
+//! returns the number of increment steps for button 3
+int QwtCounter::stepButton3() const
+{
+    return incSteps( QwtCounter::Button3 );
+}
+
+//! Set from lineedit
+void QwtCounter::textChanged()
+{
+    bool converted = false;
+
+    const double value = d_data->valueEdit->text().toDouble( &converted );
+    if ( converted )
+        setValue( value );
+}
+
+/*!
+   Handle QEvent::PolishRequest events
+   \param event Event
+   \return see QWidget::event()
+*/
+bool QwtCounter::event( QEvent *event )
+{
+    if ( event->type() == QEvent::PolishRequest )
+    {
+        const int w = d_data->valueEdit->fontMetrics().width( "W" ) + 8;
+        for ( int i = 0; i < ButtonCnt; i++ )
+        {
+            d_data->buttonDown[i]->setMinimumWidth( w );
+            d_data->buttonUp[i]->setMinimumWidth( w );
+        }
+    }
+
+    return QWidget::event( event );
+}
+
+/*!
+  Handle key events
+
+  - Ctrl + Qt::Key_Home\n
+    Step to minimum()
+  - Ctrl + Qt::Key_End\n
+    Step to maximum()
+  - Qt::Key_Up\n
+    Increment by incSteps(QwtCounter::Button1)
+  - Qt::Key_Down\n
+    Decrement by incSteps(QwtCounter::Button1)
+  - Qt::Key_PageUp\n
+    Increment by incSteps(QwtCounter::Button2)
+  - Qt::Key_PageDown\n
+    Decrement by incSteps(QwtCounter::Button2)
+  - Shift + Qt::Key_PageUp\n
+    Increment by incSteps(QwtCounter::Button3)
+  - Shift + Qt::Key_PageDown\n
+    Decrement by incSteps(QwtCounter::Button3)
+
+  \param event Key event
+*/
+void QwtCounter::keyPressEvent ( QKeyEvent *event )
+{
+    bool accepted = true;
+
+    switch ( event->key() )
+    {
+        case Qt::Key_Home:
+        {
+            if ( event->modifiers() & Qt::ControlModifier )
+                setValue( minimum() );
+            else
+                accepted = false;
+            break;
+        }
+        case Qt::Key_End:
+        {
+            if ( event->modifiers() & Qt::ControlModifier )
+                setValue( maximum() );
+            else
+                accepted = false;
+            break;
+        }
+        case Qt::Key_Up:
+        {
+            incrementValue( d_data->increment[0] );
+            break;
+        }
+        case Qt::Key_Down:
+        {
+            incrementValue( -d_data->increment[0] );
+            break;
+        }
+        case Qt::Key_PageUp:
+        case Qt::Key_PageDown:
+        {
+            int increment = d_data->increment[0];
+            if ( d_data->numButtons >= 2 )
+                increment = d_data->increment[1];
+            if ( d_data->numButtons >= 3 )
+            {
+                if ( event->modifiers() & Qt::ShiftModifier )
+                    increment = d_data->increment[2];
+            }
+            if ( event->key() == Qt::Key_PageDown )
+                increment = -increment;
+            incrementValue( increment );
+            break;
+        }
+        default:
+        {
+            accepted = false;
+        }
+    }
+
+    if ( accepted )
+    {
+        event->accept();
+        return;
+    }
+
+    QWidget::keyPressEvent ( event );
+}
+
+/*!
+  Handle wheel events
+  \param event Wheel event
+*/
+void QwtCounter::wheelEvent( QWheelEvent *event )
+{
+    event->accept();
+
+    if ( d_data->numButtons <= 0 )
+        return;
+
+    int increment = d_data->increment[0];
+    if ( d_data->numButtons >= 2 )
+    {
+        if ( event->modifiers() & Qt::ControlModifier )
+            increment = d_data->increment[1];
+    }
+    if ( d_data->numButtons >= 3 )
+    {
+        if ( event->modifiers() & Qt::ShiftModifier )
+            increment = d_data->increment[2];
+    }
+
+    for ( int i = 0; i < d_data->numButtons; i++ )
+    {
+        if ( d_data->buttonDown[i]->geometry().contains( event->pos() ) ||
+            d_data->buttonUp[i]->geometry().contains( event->pos() ) )
+        {
+            increment = d_data->increment[i];
+        }
+    }
+
+    const int wheel_delta = 120;
+
+#if 1
+    int delta = event->delta();
+    if ( delta >= 2 * wheel_delta )
+        delta /= 2; // Never saw an abs(delta) < 240
+#endif
+
+    incrementValue( delta / wheel_delta * increment );
+}
+
+void QwtCounter::incrementValue( int numSteps )
+{
+    const double min = d_data->minimum;
+    const double max = d_data->maximum;
+    double stepSize = d_data->singleStep;
+
+    if ( !d_data->isValid || min >= max || stepSize <= 0.0 )
+        return;
+
+
+#if 1
+    stepSize = qMax( stepSize, 1.0e-10 * ( max - min ) );
+#endif
+
+    double value = d_data->value + numSteps * stepSize;
+
+    if ( d_data->wrapping )
+    {
+        const double range = max - min;
+
+        if ( value < min )
+        {
+            value += ::ceil( ( min - value ) / range ) * range;
+        }
+        else if ( value > max )
+        {
+            value -= ::ceil( ( value - max ) / range ) * range;
+        }
+    }
+    else
+    {
+        value = qBound( min, value, max );
+    }
+
+    value = min + qRound( ( value - min ) / stepSize ) * stepSize;
+
+    if ( stepSize > 1e-12 )
+    {
+        if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+        {
+            // correct rounding error if value = 0
+            value = 0.0;
+        }
+        else if ( qFuzzyCompare( value, max ) )
+        {
+            // correct rounding error at the border
+            value = max;
+        }
+    }
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        showNumber( d_data->value );
+        updateButtons();
+
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+
+/*!
+  \brief Update buttons according to the current value
+
+  When the QwtCounter under- or over-flows, the focus is set to the smallest
+  up- or down-button and counting is disabled.
+
+  Counting is re-enabled on a button release event (mouse or space bar).
+*/
+void QwtCounter::updateButtons()
+{
+    if ( d_data->isValid )
+    {
+        // 1. save enabled state of the smallest down- and up-button
+        // 2. change enabled state on under- or over-flow
+
+        for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
+        {
+            d_data->buttonDown[i]->setEnabled( value() > minimum() );
+            d_data->buttonUp[i]->setEnabled( value() < maximum() );
+        }
+    }
+    else
+    {
+        for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
+        {
+            d_data->buttonDown[i]->setEnabled( false );
+            d_data->buttonUp[i]->setEnabled( false );
+        }
+    }
+}
+/*!
+  Display number string
+
+  \param number Number
+*/
+void QwtCounter::showNumber( double number )
+{
+    QString text;
+    text.setNum( number );
+
+    const int cursorPos = d_data->valueEdit->cursorPosition();
+    d_data->valueEdit->setText( text );
+    d_data->valueEdit->setCursorPosition( cursorPos );
+}
+
+//!  Button clicked
+void QwtCounter::btnClicked()
+{
+    for ( int i = 0; i < ButtonCnt; i++ )
+    {
+        if ( d_data->buttonUp[i] == sender() )
+            incrementValue( d_data->increment[i] );
+
+        if ( d_data->buttonDown[i] == sender() )
+            incrementValue( -d_data->increment[i] );
+    }
+}
+
+//!  Button released
+void QwtCounter::btnReleased()
+{
+    Q_EMIT buttonReleased( value() );
+}
+
+//! A size hint
+QSize QwtCounter::sizeHint() const
+{
+    QString tmp;
+
+    int w = tmp.setNum( minimum() ).length();
+    int w1 = tmp.setNum( maximum() ).length();
+    if ( w1 > w )
+        w = w1;
+    w1 = tmp.setNum( minimum() + singleStep() ).length();
+    if ( w1 > w )
+        w = w1;
+    w1 = tmp.setNum( maximum() - singleStep() ).length();
+    if ( w1 > w )
+        w = w1;
+
+    tmp.fill( '9', w );
+
+    QFontMetrics fm( d_data->valueEdit->font() );
+    w = fm.width( tmp ) + 2;
+    if ( d_data->valueEdit->hasFrame() )
+        w += 2 * style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
+
+    // Now we replace default sizeHint contribution of d_data->valueEdit by
+    // what we really need.
+
+    w += QWidget::sizeHint().width() - d_data->valueEdit->sizeHint().width();
+
+    const int h = qMin( QWidget::sizeHint().height(),
+        d_data->valueEdit->minimumSizeHint().height() );
+    return QSize( w, h );
+}
diff --git a/source/third_party/qwt/qwt_curve_fitter.cpp b/source/third_party/qwt/qwt_curve_fitter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b7afe2072a7031c93784e1ce257bce1cf8264078
--- /dev/null
+++ b/source/third_party/qwt/qwt_curve_fitter.cpp
@@ -0,0 +1,453 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_curve_fitter.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_spline.h"
+#include <qstack.h>
+#include <qvector.h>
+
+#if QT_VERSION < 0x040601
+#define qFabs(x) ::fabs(x)
+#endif
+
+//! Constructor
+QwtCurveFitter::QwtCurveFitter()
+{
+}
+
+//! Destructor
+QwtCurveFitter::~QwtCurveFitter()
+{
+}
+
+class QwtSplineCurveFitter::PrivateData
+{
+public:
+    PrivateData():
+        fitMode( QwtSplineCurveFitter::Auto ),
+        splineSize( 250 )
+    {
+    }
+
+    QwtSpline spline;
+    QwtSplineCurveFitter::FitMode fitMode;
+    int splineSize;
+};
+
+//! Constructor
+QwtSplineCurveFitter::QwtSplineCurveFitter()
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtSplineCurveFitter::~QwtSplineCurveFitter()
+{
+    delete d_data;
+}
+
+/*!
+  Select the algorithm used for building the spline
+
+  \param mode Mode representing a spline algorithm
+  \sa fitMode()
+*/
+void QwtSplineCurveFitter::setFitMode( FitMode mode )
+{
+    d_data->fitMode = mode;
+}
+
+/*!
+  \return Mode representing a spline algorithm
+  \sa setFitMode()
+*/
+QwtSplineCurveFitter::FitMode QwtSplineCurveFitter::fitMode() const
+{
+    return d_data->fitMode;
+}
+
+/*!
+  Assign a spline
+
+  \param spline Spline
+  \sa spline()
+*/
+void QwtSplineCurveFitter::setSpline( const QwtSpline &spline )
+{
+    d_data->spline = spline;
+    d_data->spline.reset();
+}
+
+/*!
+  \return Spline
+  \sa setSpline()
+*/
+const QwtSpline &QwtSplineCurveFitter::spline() const
+{
+    return d_data->spline;
+}
+
+/*!
+  \return Spline
+  \sa setSpline()
+*/
+QwtSpline &QwtSplineCurveFitter::spline()
+{
+    return d_data->spline;
+}
+
+/*!
+   Assign a spline size ( has to be at least 10 points )
+
+   \param splineSize Spline size
+   \sa splineSize()
+*/
+void QwtSplineCurveFitter::setSplineSize( int splineSize )
+{
+    d_data->splineSize = qMax( splineSize, 10 );
+}
+
+/*!
+  \return Spline size
+  \sa setSplineSize()
+*/
+int QwtSplineCurveFitter::splineSize() const
+{
+    return d_data->splineSize;
+}
+
+/*!
+  Find a curve which has the best fit to a series of data points
+
+  \param points Series of data points
+  \return Curve points
+*/
+QPolygonF QwtSplineCurveFitter::fitCurve( const QPolygonF &points ) const
+{
+    const int size = points.size();
+    if ( size <= 2 )
+        return points;
+
+    FitMode fitMode = d_data->fitMode;
+    if ( fitMode == Auto )
+    {
+        fitMode = Spline;
+
+        const QPointF *p = points.data();
+        for ( int i = 1; i < size; i++ )
+        {
+            if ( p[i].x() <= p[i-1].x() )
+            {
+                fitMode = ParametricSpline;
+                break;
+            }
+        };
+    }
+
+    if ( fitMode == ParametricSpline )
+        return fitParametric( points );
+    else
+        return fitSpline( points );
+}
+
+QPolygonF QwtSplineCurveFitter::fitSpline( const QPolygonF &points ) const
+{
+    d_data->spline.setPoints( points );
+    if ( !d_data->spline.isValid() )
+        return points;
+
+    QPolygonF fittedPoints( d_data->splineSize );
+
+    const double x1 = points[0].x();
+    const double x2 = points[int( points.size() - 1 )].x();
+    const double dx = x2 - x1;
+    const double delta = dx / ( d_data->splineSize - 1 );
+
+    for ( int i = 0; i < d_data->splineSize; i++ )
+    {
+        QPointF &p = fittedPoints[i];
+
+        const double v = x1 + i * delta;
+        const double sv = d_data->spline.value( v );
+
+        p.setX( v );
+        p.setY( sv );
+    }
+    d_data->spline.reset();
+
+    return fittedPoints;
+}
+
+QPolygonF QwtSplineCurveFitter::fitParametric( const QPolygonF &points ) const
+{
+    int i;
+    const int size = points.size();
+
+    QPolygonF fittedPoints( d_data->splineSize );
+    QPolygonF splinePointsX( size );
+    QPolygonF splinePointsY( size );
+
+    const QPointF *p = points.data();
+    QPointF *spX = splinePointsX.data();
+    QPointF *spY = splinePointsY.data();
+
+    double param = 0.0;
+    for ( i = 0; i < size; i++ )
+    {
+        const double x = p[i].x();
+        const double y = p[i].y();
+        if ( i > 0 )
+        {
+            const double delta = qSqrt( qwtSqr( x - spX[i-1].y() )
+                      + qwtSqr( y - spY[i-1].y() ) );
+            param += qMax( delta, 1.0 );
+        }
+        spX[i].setX( param );
+        spX[i].setY( x );
+        spY[i].setX( param );
+        spY[i].setY( y );
+    }
+
+    d_data->spline.setPoints( splinePointsX );
+    if ( !d_data->spline.isValid() )
+        return points;
+
+    const double deltaX =
+        splinePointsX[size - 1].x() / ( d_data->splineSize - 1 );
+    for ( i = 0; i < d_data->splineSize; i++ )
+    {
+        const double dtmp = i * deltaX;
+        fittedPoints[i].setX( d_data->spline.value( dtmp ) );
+    }
+
+    d_data->spline.setPoints( splinePointsY );
+    if ( !d_data->spline.isValid() )
+        return points;
+
+    const double deltaY =
+        splinePointsY[size - 1].x() / ( d_data->splineSize - 1 );
+    for ( i = 0; i < d_data->splineSize; i++ )
+    {
+        const double dtmp = i * deltaY;
+        fittedPoints[i].setY( d_data->spline.value( dtmp ) );
+    }
+
+    return fittedPoints;
+}
+
+class QwtWeedingCurveFitter::PrivateData
+{
+public:
+    PrivateData():
+        tolerance( 1.0 ),
+        chunkSize( 0 )
+    {
+    }
+
+    double tolerance;
+    uint chunkSize;
+};
+
+class QwtWeedingCurveFitter::Line
+{
+public:
+    Line( int i1 = 0, int i2 = 0 ):
+        from( i1 ),
+        to( i2 )
+    {
+    }
+
+    int from;
+    int to;
+};
+
+/*!
+   Constructor
+
+   \param tolerance Tolerance
+   \sa setTolerance(), tolerance()
+*/
+QwtWeedingCurveFitter::QwtWeedingCurveFitter( double tolerance )
+{
+    d_data = new PrivateData;
+    setTolerance( tolerance );
+}
+
+//! Destructor
+QwtWeedingCurveFitter::~QwtWeedingCurveFitter()
+{
+    delete d_data;
+}
+
+/*!
+ Assign the tolerance
+
+ The tolerance is the maximum distance, that is acceptable
+ between the original curve and the smoothed curve.
+
+ Increasing the tolerance will reduce the number of the
+ resulting points.
+
+ \param tolerance Tolerance
+
+ \sa tolerance()
+*/
+void QwtWeedingCurveFitter::setTolerance( double tolerance )
+{
+    d_data->tolerance = qMax( tolerance, 0.0 );
+}
+
+/*!
+  \return Tolerance
+  \sa setTolerance()
+*/
+double QwtWeedingCurveFitter::tolerance() const
+{
+    return d_data->tolerance;
+}
+
+/*!
+ Limit the number of points passed to a run of the algorithm
+
+ The runtime of the Douglas Peucker algorithm increases non linear
+ with the number of points. For a chunk size > 0 the polygon
+ is split into pieces passed to the algorithm one by one.
+
+ \param numPoints Maximum for the number of points passed to the algorithm
+
+ \sa chunkSize()
+*/
+void QwtWeedingCurveFitter::setChunkSize( uint numPoints )
+{
+    if ( numPoints > 0 )
+        numPoints = qMax( numPoints, 3U );
+
+    d_data->chunkSize = numPoints;
+}
+
+/*!
+  
+  \return Maximum for the number of points passed to a run 
+          of the algorithm - or 0, when unlimited
+  \sa setChunkSize()
+*/
+uint QwtWeedingCurveFitter::chunkSize() const
+{
+    return d_data->chunkSize;
+}
+
+/*!
+  \param points Series of data points
+  \return Curve points
+*/
+QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF &points ) const
+{
+    QPolygonF fittedPoints;
+
+    if ( d_data->chunkSize == 0 )
+    {
+        fittedPoints = simplify( points );
+    }
+    else
+    {
+        for ( int i = 0; i < points.size(); i += d_data->chunkSize )
+        {
+            const QPolygonF p = points.mid( i, d_data->chunkSize );
+            fittedPoints += simplify( p );
+        }
+    }
+
+    return fittedPoints;
+}
+
+QPolygonF QwtWeedingCurveFitter::simplify( const QPolygonF &points ) const
+{
+    const double toleranceSqr = d_data->tolerance * d_data->tolerance;
+
+    QStack<Line> stack;
+    stack.reserve( 500 );
+
+    const QPointF *p = points.data();
+    const int nPoints = points.size();
+
+    QVector<bool> usePoint( nPoints, false );
+
+    stack.push( Line( 0, nPoints - 1 ) );
+
+    while ( !stack.isEmpty() )
+    {
+        const Line r = stack.pop();
+
+        // initialize line segment
+        const double vecX = p[r.to].x() - p[r.from].x();
+        const double vecY = p[r.to].y() - p[r.from].y();
+
+        const double vecLength = qSqrt( vecX * vecX + vecY * vecY );
+
+        const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0;
+        const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0;
+
+        double maxDistSqr = 0.0;
+        int nVertexIndexMaxDistance = r.from + 1;
+        for ( int i = r.from + 1; i < r.to; i++ )
+        {
+            //compare to anchor
+            const double fromVecX = p[i].x() - p[r.from].x();
+            const double fromVecY = p[i].y() - p[r.from].y();
+
+            double distToSegmentSqr;
+            if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 )
+            {
+                distToSegmentSqr = fromVecX * fromVecX + fromVecY * fromVecY;
+            }
+            else
+            {
+                const double toVecX = p[i].x() - p[r.to].x();
+                const double toVecY = p[i].y() - p[r.to].y();
+                const double toVecLength = toVecX * toVecX + toVecY * toVecY;
+
+                const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY );
+                if ( s < 0.0 )
+                {
+                    distToSegmentSqr = toVecLength;
+                }
+                else
+                {
+                    distToSegmentSqr = qFabs( toVecLength - s * s );
+                }
+            }
+
+            if ( maxDistSqr < distToSegmentSqr )
+            {
+                maxDistSqr = distToSegmentSqr;
+                nVertexIndexMaxDistance = i;
+            }
+        }
+        if ( maxDistSqr <= toleranceSqr )
+        {
+            usePoint[r.from] = true;
+            usePoint[r.to] = true;
+        }
+        else
+        {
+            stack.push( Line( r.from, nVertexIndexMaxDistance ) );
+            stack.push( Line( nVertexIndexMaxDistance, r.to ) );
+        }
+    }
+
+    QPolygonF stripped;
+    for ( int i = 0; i < nPoints; i++ )
+    {
+        if ( usePoint[i] )
+            stripped += p[i];
+    }
+
+    return stripped;
+}
diff --git a/source/third_party/qwt/qwt_date.cpp b/source/third_party/qwt/qwt_date.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c52ab49d04cd8a11e9ebfc560dd26f8a4768d3f8
--- /dev/null
+++ b/source/third_party/qwt/qwt_date.cpp
@@ -0,0 +1,760 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_date.h"
+#include <qdebug.h>
+#include <qlocale.h>
+#include <math.h>
+#include <limits>
+#include <limits.h>
+
+#if QT_VERSION >= 0x050000
+
+typedef qint64 QwtJulianDay;
+static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 );
+static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 );
+
+#else
+
+// QDate stores the Julian day as unsigned int, but
+// but it is QDate::fromJulianDay( int ). That's why
+// we have the range [ 1, INT_MAX ]
+typedef int QwtJulianDay;
+static const QwtJulianDay minJulianDayD = 1;
+static const QwtJulianDay maxJulianDayD = std::numeric_limits<int>::max();
+
+#endif
+
+static QString qwtExpandedFormat( const QString & format, 
+    const QDateTime &dateTime, QwtDate::Week0Type week0Type )
+{
+    const int week = QwtDate::weekNumber( dateTime.date(), week0Type );
+
+    QString weekNo;
+    weekNo.setNum( week );
+
+    QString weekNoWW;
+    if ( weekNo.length() == 1 )
+        weekNoWW += "0";
+
+    weekNoWW += weekNo;
+
+    QString fmt = format;
+    fmt.replace( "ww", weekNoWW );
+    fmt.replace( "w", weekNo );
+
+    if ( week == 1 && dateTime.date().month() != 1 )
+    {
+        // in case of week 1, we might need to increment the year
+
+        static QString s_yyyy = "yyyy";
+        static QString s_yy = "yy";
+
+        // week 1 might start in the previous year
+
+        bool doReplaceYear = fmt.contains( s_yy );
+
+        if ( doReplaceYear )
+        {
+            if ( fmt.contains( 'M' ) )
+            {
+                // in case of also having 'M' we have a conflict about
+                // which year to show
+
+                doReplaceYear = false;
+            }
+            else
+            {
+                // in case of also having 'd' or 'dd' we have a conflict about
+                // which year to show
+
+                int numD = 0;
+
+                for ( int i = 0; i < fmt.size(); i++ )
+                {
+                    if ( fmt[i] == 'd' )
+                    {
+                        numD++;
+                    }
+                    else
+                    {
+                        if ( numD > 0 && numD <= 2 )
+                            break;
+
+                        numD = 0;
+                    }
+                }
+
+                if ( numD > 0 && numD <= 2 )
+                    doReplaceYear = false;
+            }
+        }
+
+        if ( doReplaceYear )
+        {
+            const QDate dt( dateTime.date().year() + 1, 1, 1 );
+
+            if ( fmt.contains( s_yyyy ) )
+            {
+                fmt.replace( s_yyyy, dt.toString( s_yyyy ) );
+            }
+            else
+            {
+                fmt.replace( s_yy, dt.toString( s_yyyy ) );
+            }
+        }
+    }
+
+    return fmt;
+}
+
+static inline Qt::DayOfWeek qwtFirstDayOfWeek()
+{
+#if QT_VERSION >= 0x040800
+    return QLocale().firstDayOfWeek();
+#else
+
+    switch( QLocale().country() )
+    {
+        case QLocale::Maldives:
+            return Qt::Friday;
+
+        case QLocale::Afghanistan:
+        case QLocale::Algeria:
+        case QLocale::Bahrain:
+        case QLocale::Djibouti:
+        case QLocale::Egypt:
+        case QLocale::Eritrea:
+        case QLocale::Ethiopia:
+        case QLocale::Iran:
+        case QLocale::Iraq:
+        case QLocale::Jordan:
+        case QLocale::Kenya:
+        case QLocale::Kuwait:
+        case QLocale::LibyanArabJamahiriya:
+        case QLocale::Morocco:
+        case QLocale::Oman:
+        case QLocale::Qatar:
+        case QLocale::SaudiArabia:
+        case QLocale::Somalia:
+        case QLocale::Sudan:
+        case QLocale::Tunisia:
+        case QLocale::Yemen:
+            return Qt::Saturday;
+
+        case QLocale::AmericanSamoa:
+        case QLocale::Argentina:
+        case QLocale::Azerbaijan:
+        case QLocale::Botswana:
+        case QLocale::Canada:
+        case QLocale::China:
+        case QLocale::FaroeIslands:
+        case QLocale::Georgia:
+        case QLocale::Greenland:
+        case QLocale::Guam:
+        case QLocale::HongKong:
+        case QLocale::Iceland:
+        case QLocale::India:
+        case QLocale::Ireland:
+        case QLocale::Israel:
+        case QLocale::Jamaica:
+        case QLocale::Japan:
+        case QLocale::Kyrgyzstan:
+        case QLocale::Lao:
+        case QLocale::Malta:
+        case QLocale::MarshallIslands:
+        case QLocale::Macau:
+        case QLocale::Mongolia:
+        case QLocale::NewZealand:
+        case QLocale::NorthernMarianaIslands:
+        case QLocale::Pakistan:
+        case QLocale::Philippines:
+        case QLocale::RepublicOfKorea:
+        case QLocale::Singapore:
+        case QLocale::SyrianArabRepublic:
+        case QLocale::Taiwan:
+        case QLocale::Thailand:
+        case QLocale::TrinidadAndTobago:
+        case QLocale::UnitedStates:
+        case QLocale::UnitedStatesMinorOutlyingIslands:
+        case QLocale::USVirginIslands:
+        case QLocale::Uzbekistan:
+        case QLocale::Zimbabwe:
+            return Qt::Sunday;
+
+        default:
+            return Qt::Monday;
+    }
+#endif
+}
+
+static inline void qwtFloorTime( 
+    QwtDate::IntervalType intervalType, QDateTime &dt )
+{
+    // when dt is inside the special hour where DST is ending
+    // an hour is no unique. Therefore we have to
+    // use UTC time.
+
+    const Qt::TimeSpec timeSpec = dt.timeSpec();
+
+    if ( timeSpec == Qt::LocalTime )
+        dt = dt.toTimeSpec( Qt::UTC );
+
+    const QTime t = dt.time();
+    switch( intervalType )
+    {
+        case QwtDate::Second:
+        {
+            dt.setTime( QTime( t.hour(), t.minute(), t.second() ) );
+            break;
+        }
+        case QwtDate::Minute:
+        {
+            dt.setTime( QTime( t.hour(), t.minute(), 0 ) );
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            dt.setTime( QTime( t.hour(), 0, 0 ) );
+            break;
+        }   
+        default:
+            break;
+    }
+
+    if ( timeSpec == Qt::LocalTime )
+        dt = dt.toTimeSpec( Qt::LocalTime );
+}
+
+static inline QDateTime qwtToTimeSpec( 
+    const QDateTime &dt, Qt::TimeSpec spec )
+{
+    if ( dt.timeSpec() == spec )
+        return dt;
+
+    const qint64 jd = dt.date().toJulianDay();
+    if ( jd < 0 || jd >= INT_MAX )
+    {
+        // the conversion between local time and UTC
+        // is internally limited. To avoid
+        // overflows we simply ignore the difference
+        // for those dates
+
+        QDateTime dt2 = dt;
+        dt2.setTimeSpec( spec );
+        return dt2;
+    }
+
+    return dt.toTimeSpec( spec );
+}
+
+static inline double qwtToJulianDay( int year, int month, int day )
+{
+    // code from QDate but using doubles to avoid overflows
+    // for large values
+
+    const int m1 = ( month - 14 ) / 12;
+    const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
+    const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
+
+    return ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
+            - ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
+}
+
+static inline qint64 qwtFloorDiv64( qint64 a, int b )
+{
+    if ( a < 0 )
+        a -= b - 1;
+
+    return a / b;
+}
+
+static inline qint64 qwtFloorDiv( int a, int b )
+{
+    if ( a < 0 )
+        a -= b - 1;
+        
+    return a / b;
+}   
+
+static inline QDate qwtToDate( int year, int month = 1, int day = 1 )
+{
+#if QT_VERSION >= 0x050000
+    return QDate( year, month, day );
+#else
+    if ( year > 100000 )
+    {
+        // code from QDate but using doubles to avoid overflows
+        // for large values
+
+        const int m1 = ( month - 14 ) / 12;
+        const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
+        const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
+
+        const double jd = ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
+            - ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
+
+        if ( jd > maxJulianDayD )
+        {
+            qWarning() << "qwtToDate: overflow";
+            return QDate();
+        }
+
+        return QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
+    }
+    else
+    {
+        return QDate( year, month, day );
+    }
+#endif
+}
+
+/*!
+  Translate from double to QDateTime
+
+  \param value Number of milliseconds since the epoch, 
+               1970-01-01T00:00:00 UTC
+  \param timeSpec Time specification
+  \return Datetime value
+
+  \sa toDouble(), QDateTime::setMSecsSinceEpoch()
+  \note The return datetime for Qt::OffsetFromUTC will be Qt::UTC
+ */
+QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec )
+{
+    const int msecsPerDay = 86400000;
+
+    const double days = static_cast<qint64>( ::floor( value / msecsPerDay ) );
+
+    const double jd = QwtDate::JulianDayForEpoch + days;
+    if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) )
+    {
+        qWarning() << "QwtDate::toDateTime: overflow";
+        return QDateTime();
+    }
+
+    const QDate d = QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
+
+    const int msecs = static_cast<int>( value - days * msecsPerDay );
+
+    static const QTime timeNull( 0, 0, 0, 0 );
+
+    QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC );
+
+    if ( timeSpec == Qt::LocalTime )
+        dt = qwtToTimeSpec( dt, timeSpec );
+
+    return dt;
+}
+
+/*!
+  Translate from QDateTime to double
+
+  \param dateTime Datetime value
+  \return Number of milliseconds since 1970-01-01T00:00:00 UTC has passed.
+
+  \sa toDateTime(), QDateTime::toMSecsSinceEpoch()
+  \warning For values very far below or above 1970-01-01 UTC rounding errors
+           will happen due to the limited significance of a double.
+ */
+double QwtDate::toDouble( const QDateTime &dateTime )
+{
+    const int msecsPerDay = 86400000;
+
+    const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC );
+
+    const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch;
+
+    const QTime time = dt.time();
+    const double secs = 3600.0 * time.hour() + 
+        60.0 * time.minute() + time.second();
+
+    return days * msecsPerDay + time.msec() + 1000.0 * secs;
+}
+
+/*!
+  Ceil a datetime according the interval type
+
+  \param dateTime Datetime value
+  \param intervalType Interval type, how to ceil. 
+                      F.e. when intervalType = QwtDate::Months, the result
+                      will be ceiled to the next beginning of a month
+  \return Ceiled datetime
+  \sa floor()
+ */
+QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType )
+{
+    if ( dateTime.date() >= QwtDate::maxDate() )
+        return dateTime;
+
+    QDateTime dt = dateTime;
+
+    switch ( intervalType )
+    {
+        case QwtDate::Millisecond:
+        {
+            break;
+        }
+        case QwtDate::Second:
+        {
+            qwtFloorTime( QwtDate::Second, dt );
+            if ( dt < dateTime )
+                dt = dt.addSecs( 1 );
+
+            break;
+        }
+        case QwtDate::Minute:
+        {
+            qwtFloorTime( QwtDate::Minute, dt );
+            if ( dt < dateTime )
+                dt = dt.addSecs( 60 );
+
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            qwtFloorTime( QwtDate::Hour, dt );
+            if ( dt < dateTime )
+                dt = dt.addSecs( 3600 );
+
+            break;
+        }
+        case QwtDate::Day:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+            if ( dt < dateTime )
+                dt = dt.addDays( 1 );
+
+            break;
+        }
+        case QwtDate::Week:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+            if ( dt < dateTime )
+                dt = dt.addDays( 1 );
+
+            int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek();
+            if ( days < 0 )
+                days += 7;
+
+            dt = dt.addDays( days );
+
+            break;
+        }
+        case QwtDate::Month:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+            dt.setDate( qwtToDate( dateTime.date().year(), 
+                dateTime.date().month() ) );
+
+            if ( dt < dateTime )
+                dt = dt.addMonths( 1 );
+
+            break;
+        }
+        case QwtDate::Year:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+
+            const QDate d = dateTime.date();
+
+            int year = d.year();
+            if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() )
+                year++;
+
+            if ( year == 0 )
+                year++; // there is no year 0
+
+            dt.setDate( qwtToDate( year ) );
+            break;
+        }
+    }
+
+    return dt;
+}
+
+/*!
+  Floor a datetime according the interval type
+
+  \param dateTime Datetime value
+  \param intervalType Interval type, how to ceil. 
+                      F.e. when intervalType = QwtDate::Months,
+                      the result will be ceiled to the next 
+                      beginning of a month
+  \return Floored datetime
+  \sa floor()
+ */
+QDateTime QwtDate::floor( const QDateTime &dateTime, 
+    IntervalType intervalType )
+{
+    if ( dateTime.date() <= QwtDate::minDate() )
+        return dateTime;
+
+    QDateTime dt = dateTime;
+
+    switch ( intervalType )
+    {
+        case QwtDate::Millisecond:
+        {
+            break;
+        }
+        case QwtDate::Second:
+        case QwtDate::Minute:
+        case QwtDate::Hour:
+        {
+            qwtFloorTime( intervalType, dt );
+            break;
+        }
+        case QwtDate::Day:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+            break;
+        }
+        case QwtDate::Week:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+
+            int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek();
+            if ( days < 0 )
+                days += 7;
+
+            dt = dt.addDays( -days );
+
+            break;
+        }
+        case QwtDate::Month:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+
+            const QDate date = qwtToDate( dt.date().year(), 
+                dt.date().month() );
+            dt.setDate( date );
+
+            break;
+        }
+        case QwtDate::Year:
+        {
+            dt.setTime( QTime( 0, 0 ) );
+
+            const QDate date = qwtToDate( dt.date().year() );
+            dt.setDate( date );
+
+            break;
+        }
+    }
+
+    return dt;
+}
+
+/*!
+  Minimum for the supported date range
+
+  The range of valid dates depends on how QDate stores the 
+  Julian day internally.
+
+  - For Qt4 it is "Tue Jan 2 -4713"
+  - For Qt5 it is "Thu Jan 1 -2147483648"
+
+  \return minimum of the date range
+  \sa maxDate()
+ */
+QDate QwtDate::minDate()
+{
+    static QDate date;
+    if ( !date.isValid() )
+        date = QDate::fromJulianDay( minJulianDayD );
+
+    return date;
+}
+
+/*!
+  Maximum for the supported date range
+
+  The range of valid dates depends on how QDate stores the 
+  Julian day internally.
+
+  - For Qt4 it is "Tue Jun 3 5874898"
+  - For Qt5 it is "Tue Dec 31 2147483647"
+
+  \return maximum of the date range
+  \sa minDate()
+  \note The maximum differs between Qt4 and Qt5
+ */
+QDate QwtDate::maxDate()
+{
+    static QDate date;
+    if ( !date.isValid() )
+        date = QDate::fromJulianDay( maxJulianDayD );
+
+    return date;
+}
+
+/*!
+  \brief Date of the first day of the first week for a year
+
+  The first day of a week depends on the current locale
+  ( QLocale::firstDayOfWeek() ). 
+
+  \param year Year
+  \param type Option how to identify the first week
+  \return First day of week 0
+
+  \sa QLocale::firstDayOfWeek(), weekNumber()
+ */ 
+QDate QwtDate::dateOfWeek0( int year, Week0Type type )
+{
+    const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek();
+
+    QDate dt0( year, 1, 1 );
+
+    // floor to the first day of the week
+    int days = dt0.dayOfWeek() - firstDayOfWeek;
+    if ( days < 0 )
+        days += 7;
+
+    dt0 = dt0.addDays( -days );
+
+    if ( type == QwtDate::FirstThursday )
+    {
+        // according to ISO 8601 the first week is defined
+        // by the first thursday. 
+
+        int d = Qt::Thursday - firstDayOfWeek;
+        if ( d < 0 )
+            d += 7;
+
+        if ( dt0.addDays( d ).year() < year )
+            dt0 = dt0.addDays( 7 );
+    }
+
+    return dt0;
+}
+
+/*!
+  Find the week number of a date
+
+  - QwtDate::FirstThursday\n
+    Corresponding to ISO 8601 ( see QDate::weekNumber() ). 
+
+  - QwtDate::FirstDay\n
+    Number of weeks that have begun since dateOfWeek0().
+
+  \param date Date
+  \param type Option how to identify the first week
+
+  \return Week number, starting with 1
+ */
+int QwtDate::weekNumber( const QDate &date, Week0Type type )
+{
+    int weekNo;
+
+    if ( type == QwtDate::FirstDay )
+    {
+        QDate day0;
+
+        if ( date.month() == 12 && date.day() >= 24 )
+        {
+            // week 1 usually starts in the previous years.
+            // and we have to check if we are already there
+
+            day0 = dateOfWeek0( date.year() + 1, type );
+            if ( day0.daysTo( date ) < 0 )
+                day0 = dateOfWeek0( date.year(), type );
+        }
+        else
+        {
+            day0 = dateOfWeek0( date.year(), type );
+        }
+
+        weekNo = day0.daysTo( date ) / 7 + 1;
+    }
+    else
+    {
+        weekNo = date.weekNumber();
+    }
+
+    return weekNo;
+}
+
+/*!
+   Offset in seconds from Coordinated Universal Time
+
+   The offset depends on the time specification of dateTime:
+
+   - Qt::UTC
+     0, dateTime has no offset
+   - Qt::OffsetFromUTC
+     returns dateTime.utcOffset()
+   - Qt::LocalTime:
+     number of seconds from the UTC
+
+   For Qt::LocalTime the offset depends on the timezone and
+   daylight savings.
+
+   \param dateTime Datetime value
+   \return Offset in seconds
+ */
+int QwtDate::utcOffset( const QDateTime &dateTime )
+{
+    int seconds = 0;
+
+    switch( dateTime.timeSpec() )
+    {
+        case Qt::UTC:
+        {
+            break;
+        }
+        case Qt::OffsetFromUTC:
+        {
+            seconds = dateTime.utcOffset();
+            break;
+        }
+        default:
+        {
+            const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC );
+            seconds = dateTime.secsTo( dt1 );
+        }
+    }
+
+    return seconds;
+}
+
+/*!
+  Translate a datetime into a string
+
+  Beside the format expressions documented in QDateTime::toString()
+  the following expressions are supported:
+
+  - w\n
+    week number: ( 1 - 53 )
+  - ww\n
+    week number with a leading zero ( 01 - 53 )
+
+  As week 1 usually starts in the previous year a special rule 
+  is applied for formats, where the year is expected to match the
+  week number - even if the date belongs to the previous year.
+
+  \param dateTime Datetime value
+  \param format Format string
+  \param week0Type Specification of week 0
+
+  \return Datetime string
+  \sa QDateTime::toString(), weekNumber(), QwtDateScaleDraw
+ */
+QString QwtDate::toString( const QDateTime &dateTime,
+    const QString & format, Week0Type week0Type )
+{
+    QString fmt = format;
+    if ( fmt.contains( 'w' ) )
+    {
+        fmt = qwtExpandedFormat( fmt, dateTime, week0Type );
+    }
+
+    return dateTime.toString( fmt );
+}
diff --git a/source/third_party/qwt/qwt_date_scale_draw.cpp b/source/third_party/qwt/qwt_date_scale_draw.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e4c46cc2221d44fd65707df5e1105ed940bccf02
--- /dev/null
+++ b/source/third_party/qwt/qwt_date_scale_draw.cpp
@@ -0,0 +1,278 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_date_scale_draw.h"
+
+class QwtDateScaleDraw::PrivateData
+{
+public:
+    PrivateData( Qt::TimeSpec spec ):
+        timeSpec( spec ),
+        utcOffset( 0 ),
+        week0Type( QwtDate::FirstThursday )
+    {
+        dateFormats[ QwtDate::Millisecond ] = "hh:mm:ss:zzz\nddd dd MMM yyyy";
+        dateFormats[ QwtDate::Second ] = "hh:mm:ss\nddd dd MMM yyyy";
+        dateFormats[ QwtDate::Minute ] = "hh:mm\nddd dd MMM yyyy";
+        dateFormats[ QwtDate::Hour ] = "hh:mm\nddd dd MMM yyyy";
+        dateFormats[ QwtDate::Day ] = "ddd dd MMM yyyy";
+        dateFormats[ QwtDate::Week ] = "Www yyyy";
+        dateFormats[ QwtDate::Month ] = "MMM yyyy";
+        dateFormats[ QwtDate::Year ] = "yyyy";
+    }
+
+    Qt::TimeSpec timeSpec;
+    int utcOffset;
+    QwtDate::Week0Type week0Type;
+    QString dateFormats[ QwtDate::Year + 1 ];
+};
+
+/*!
+  \brief Constructor
+
+  The default setting is to display tick labels for the 
+  given time specification. The first week of a year is defined like
+  for QwtDate::FirstThursday.
+
+  \param timeSpec Time specification
+
+  \sa setTimeSpec(), setWeek0Type()
+ */
+QwtDateScaleDraw::QwtDateScaleDraw( Qt::TimeSpec timeSpec )
+{
+    d_data = new PrivateData( timeSpec );
+}
+
+//! Destructor
+QwtDateScaleDraw::~QwtDateScaleDraw()
+{
+    delete d_data;
+}
+
+/*!
+  Set the time specification used for the tick labels
+
+  \param timeSpec Time specification
+  \sa timeSpec(), setUtcOffset(), toDateTime()
+ */
+void QwtDateScaleDraw::setTimeSpec( Qt::TimeSpec timeSpec )
+{
+    d_data->timeSpec = timeSpec;
+}
+
+/*!
+  \return Time specification used for the tick labels
+  \sa setTimeSpec(), utcOffset(), toDateTime()
+ */
+Qt::TimeSpec QwtDateScaleDraw::timeSpec() const
+{
+    return d_data->timeSpec;
+}
+
+/*!
+  Set the offset in seconds from Coordinated Universal Time
+
+  \param seconds Offset in seconds
+
+  \note The offset has no effect beside for the time specification
+        Qt::OffsetFromUTC.
+
+  \sa QDate::utcOffset(), setTimeSpec(), toDateTime()
+ */
+void QwtDateScaleDraw::setUtcOffset( int seconds )
+{
+    d_data->utcOffset = seconds;
+}
+
+/*!
+  \return Offset in seconds from Coordinated Universal Time
+  \note The offset has no effect beside for the time specification
+        Qt::OffsetFromUTC.
+
+  \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime()
+ */
+int QwtDateScaleDraw::utcOffset() const
+{
+    return d_data->utcOffset;
+}
+
+/*!
+  Sets how to identify the first week of a year.
+
+  \param week0Type Mode how to identify the first week of a year
+
+  \sa week0Type().
+  \note week0Type has no effect beside for intervals classified as
+        QwtDate::Week. 
+ */
+void QwtDateScaleDraw::setWeek0Type( QwtDate::Week0Type week0Type )
+{
+    d_data->week0Type = week0Type;
+}
+
+/*!
+  \return Setting how to identify the first week of a year. 
+  \sa setWeek0Type()
+ */
+QwtDate::Week0Type QwtDateScaleDraw::week0Type() const
+{
+    return d_data->week0Type;
+}
+
+/*!
+  Set the default format string for an datetime interval type
+
+  \param intervalType Interval type
+  \param format Default format string
+
+  \sa dateFormat(), dateFormatOfDate(), QwtDate::toString()
+ */
+void QwtDateScaleDraw::setDateFormat( 
+    QwtDate::IntervalType intervalType, const QString &format )
+{
+    if ( intervalType >= QwtDate::Millisecond && 
+        intervalType <= QwtDate::Year )
+    {
+        d_data->dateFormats[ intervalType ] = format;
+    }
+}
+
+/*!
+  \param intervalType Interval type
+  \return Default format string for an datetime interval type
+  \sa setDateFormat(), dateFormatOfDate()
+ */
+QString QwtDateScaleDraw::dateFormat( 
+    QwtDate::IntervalType intervalType ) const
+{
+    if ( intervalType >= QwtDate::Millisecond && 
+        intervalType <= QwtDate::Year )
+    {
+        return d_data->dateFormats[ intervalType ];
+    }
+
+    return QString::null;
+}
+
+/*!
+  Format string for the representation of a datetime
+
+  dateFormatOfDate() is intended to be overloaded for
+  situations, where formats are individual for specific
+  datetime values.
+
+  The default setting ignores dateTime and return
+  the default format for the interval type.
+
+  \param dateTime Datetime value
+  \param intervalType Interval type
+  \return Format string
+
+  \sa setDateFormat(), QwtDate::toString()
+ */
+QString QwtDateScaleDraw::dateFormatOfDate( const QDateTime &dateTime,
+    QwtDate::IntervalType intervalType ) const
+{
+    Q_UNUSED( dateTime )
+
+    if ( intervalType >= QwtDate::Millisecond && 
+        intervalType <= QwtDate::Year )
+    {
+        return d_data->dateFormats[ intervalType ];
+    }
+
+    return d_data->dateFormats[ QwtDate::Second ];
+}
+
+/*!
+  \brief Convert a value into its representing label
+
+  The value is converted to a datetime value using toDateTime()
+  and converted to a plain text using QwtDate::toString().
+
+  \param value Value
+  \return Label string.
+
+  \sa dateFormatOfDate()
+*/
+QwtText QwtDateScaleDraw::label( double value ) const
+{
+    const QDateTime dt = toDateTime( value );
+    const QString fmt = dateFormatOfDate( 
+        dt, intervalType( scaleDiv() ) );
+
+    return QwtDate::toString( dt, fmt, d_data->week0Type );
+}
+
+/*!
+  Find the less detailed datetime unit, where no rounding
+  errors happen.
+
+  \param scaleDiv Scale division
+  \return Interval type
+
+  \sa dateFormatOfDate()
+ */
+QwtDate::IntervalType QwtDateScaleDraw::intervalType( 
+    const QwtScaleDiv &scaleDiv ) const
+{
+    int intvType = QwtDate::Year;
+
+    bool alignedToWeeks = true;
+
+    const QList<double> ticks = scaleDiv.ticks( QwtScaleDiv::MajorTick );
+    for ( int i = 0; i < ticks.size(); i++ )
+    {
+        const QDateTime dt = toDateTime( ticks[i] );
+        for ( int j = QwtDate::Second; j <= intvType; j++ )
+        {
+            const QDateTime dt0 = QwtDate::floor( dt, 
+                static_cast<QwtDate::IntervalType>( j ) );
+
+            if ( dt0 != dt )
+            {
+                if ( j == QwtDate::Week )
+                {
+                    alignedToWeeks = false;
+                }
+                else
+                {
+                    intvType = j - 1;
+                    break;
+                }
+            }
+        }
+
+        if ( intvType == QwtDate::Millisecond )
+            break;
+    }
+
+    if ( intvType == QwtDate::Week && !alignedToWeeks )
+        intvType = QwtDate::Day;
+
+    return static_cast<QwtDate::IntervalType>( intvType );
+}
+
+/*!
+  Translate a double value into a QDateTime object.
+
+  \return QDateTime object initialized with timeSpec() and utcOffset().
+  \sa timeSpec(), utcOffset(), QwtDate::toDateTime()
+ */
+QDateTime QwtDateScaleDraw::toDateTime( double value ) const
+{
+    QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
+    if ( d_data->timeSpec == Qt::OffsetFromUTC )
+    {
+        dt = dt.addSecs( d_data->utcOffset );
+        dt.setUtcOffset( d_data->utcOffset );
+    }
+
+    return dt;
+}
diff --git a/source/third_party/qwt/qwt_date_scale_engine.cpp b/source/third_party/qwt/qwt_date_scale_engine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2ded7ee73f0c495e2e9fb77ccbbb6f0462df065e
--- /dev/null
+++ b/source/third_party/qwt/qwt_date_scale_engine.cpp
@@ -0,0 +1,1309 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_date_scale_engine.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_transform.h"
+#include <qdatetime.h>
+#include <limits.h>
+
+static inline double qwtMsecsForType( QwtDate::IntervalType type )
+{
+    static const double msecs[] =
+    {
+        1.0,
+        1000.0,
+        60.0 * 1000.0,
+        3600.0 * 1000.0,
+        24.0 * 3600.0 * 1000.0,
+        7.0 * 24.0 * 3600.0 * 1000.0,
+        30.0 * 24.0 * 3600.0 * 1000.0,
+        365.0 * 24.0 * 3600.0 * 1000.0,
+    };
+
+    if ( type < 0 || type >= static_cast<int>( sizeof( msecs ) / sizeof( msecs[0] ) ) )
+        return 1.0;
+
+    return msecs[ type ];
+}
+
+static inline int qwtAlignValue(
+    double value, double stepSize, bool up )
+{
+    double d = value / stepSize;
+    d = up ? ::ceil( d ) : ::floor( d );
+
+    return static_cast<int>( d * stepSize );
+}
+
+static double qwtIntervalWidth( const QDateTime &minDate,
+    const QDateTime &maxDate, QwtDate::IntervalType intervalType ) 
+{
+    switch( intervalType )
+    {
+        case QwtDate::Millisecond:
+        {
+            const double secsTo = minDate.secsTo( maxDate );
+            const double msecs = maxDate.time().msec() -
+                minDate.time().msec();
+
+            return secsTo * 1000 + msecs;
+        }
+        case QwtDate::Second:
+        {
+            return minDate.secsTo( maxDate );
+        }
+        case QwtDate::Minute:
+        {
+            const double secsTo = minDate.secsTo( maxDate );
+            return ::floor( secsTo / 60 );
+        }
+        case QwtDate::Hour:
+        {
+            const double secsTo = minDate.secsTo( maxDate );
+            return ::floor( secsTo / 3600 );
+        }
+        case QwtDate::Day:
+        {
+            return minDate.daysTo( maxDate );
+        }
+        case QwtDate::Week:
+        {
+            return ::floor( minDate.daysTo( maxDate ) / 7.0 );
+        }
+        case QwtDate::Month:
+        {
+            const double years = 
+                double( maxDate.date().year() ) - minDate.date().year();
+
+            int months = maxDate.date().month() - minDate.date().month();
+            if ( maxDate.date().day() < minDate.date().day() )
+                months--;
+
+            return years * 12 + months;
+        }
+        case QwtDate::Year:
+        {
+            double years = 
+                double( maxDate.date().year() ) - minDate.date().year();
+
+            if ( maxDate.date().month() < minDate.date().month() )
+                years -= 1.0;
+
+            return years;
+        }
+    }
+
+    return 0.0;
+}
+
+static double qwtRoundedIntervalWidth( 
+    const QDateTime &minDate, const QDateTime &maxDate, 
+    QwtDate::IntervalType intervalType ) 
+{
+    const QDateTime minD = QwtDate::floor( minDate, intervalType );
+    const QDateTime maxD = QwtDate::ceil( maxDate, intervalType );
+
+    return qwtIntervalWidth( minD, maxD, intervalType );
+}
+
+static inline int qwtStepCount( int intervalSize, int maxSteps,
+    const int limits[], size_t numLimits )
+{
+    for ( uint i = 0; i < numLimits; i++ )
+    {
+        const int numSteps = intervalSize / limits[ i ];
+
+        if ( numSteps > 1 && numSteps <= maxSteps &&
+            numSteps * limits[ i ] == intervalSize )
+        {
+            return numSteps;
+        }
+    }
+
+    return 0;
+}
+
+static int qwtStepSize( int intervalSize, int maxSteps, uint base ) 
+{
+    if ( maxSteps <= 0 )
+        return 0;
+
+    if ( maxSteps > 2 )
+    {
+        for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
+        {
+            const double stepSize = double( intervalSize ) / numSteps;
+
+            const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) );
+            const double fraction = qPow( base, p );
+
+            for ( uint n = base; n >= 1; n /= 2 )
+            {
+                if ( qFuzzyCompare( stepSize, n * fraction ) )
+                    return qRound( stepSize );
+
+                if ( n == 3 && ( base % 2 ) == 0 )
+                {
+                    if ( qFuzzyCompare( stepSize, 2 * fraction ) )
+                        return qRound( stepSize );
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int qwtDivideInterval( double intervalSize, int numSteps, 
+    const int limits[], size_t numLimits )
+{
+    const int v = qCeil( intervalSize / double( numSteps ) );
+
+    for ( uint i = 0; i < numLimits - 1; i++ )
+    {
+        if ( v <= limits[i] )
+            return limits[i];
+    }
+
+    return limits[ numLimits - 1 ];
+}
+
+static double qwtDivideScale( double intervalSize, int numSteps,
+    QwtDate::IntervalType intervalType )
+{
+    if ( intervalType != QwtDate::Day )
+    {
+        if ( ( intervalSize > numSteps ) && 
+            ( intervalSize <= 2 * numSteps ) )
+        {
+            return 2.0;
+        }
+    }
+
+    double stepSize;
+
+    switch( intervalType )
+    {
+        case QwtDate::Second:
+        case QwtDate::Minute:
+        {
+            static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+    
+            stepSize = qwtDivideInterval( intervalSize, numSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
+    
+            stepSize = qwtDivideInterval( intervalSize, numSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            break;
+        }
+        case QwtDate::Day:
+        {
+            const double v = intervalSize / double( numSteps );
+            if ( v <= 5.0 )
+                stepSize = qCeil( v );
+            else
+                stepSize = qCeil( v / 7 ) * 7;
+
+            break;
+        }
+        case QwtDate::Week:
+        {
+            static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
+
+            stepSize = qwtDivideInterval( intervalSize, numSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            break;
+        }
+        case QwtDate::Month:
+        {
+            static int limits[] = { 1, 2, 3, 4, 6, 12 };
+
+            stepSize = qwtDivideInterval( intervalSize, numSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            break;
+        }
+        case QwtDate::Year:
+        case QwtDate::Millisecond:
+        default:
+        {
+            stepSize = QwtScaleArithmetic::divideInterval(
+                intervalSize, numSteps, 10 );
+        }
+    }
+
+    return stepSize;
+}
+
+static double qwtDivideMajorStep( double stepSize, int maxMinSteps,
+    QwtDate::IntervalType intervalType )
+{
+    double minStepSize = 0.0;
+
+    switch( intervalType )
+    {
+        case QwtDate::Second:
+        {
+            minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
+            if ( minStepSize == 0.0 )
+                minStepSize = 0.5 * stepSize;
+
+            break;
+        }
+        case QwtDate::Minute:
+        {
+            static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+
+            int numSteps;
+
+            if ( stepSize > maxMinSteps )
+            {
+                numSteps = qwtStepCount( stepSize, maxMinSteps, 
+                    limits, sizeof( limits ) / sizeof( int ) );
+
+            }
+            else
+            {
+                numSteps = qwtStepCount( stepSize * 60, maxMinSteps, 
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+
+            if ( numSteps > 0 )
+                minStepSize = double( stepSize ) / numSteps;
+
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            int numSteps = 0;
+
+            if ( stepSize > maxMinSteps )
+            {
+                static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
+
+                numSteps = qwtStepCount( stepSize, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+            else
+            {
+                static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+
+                numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+
+            if ( numSteps > 0 )
+                minStepSize = double( stepSize ) / numSteps;
+
+            break;
+        }
+        case QwtDate::Day:
+        {
+            int numSteps = 0;
+
+            if ( stepSize > maxMinSteps )
+            {
+                static int limits[] = { 1, 2, 3, 7, 14, 28 };
+
+                numSteps = qwtStepCount( stepSize, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+            else
+            {
+                static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
+
+                numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+            }
+
+            if ( numSteps > 0 )
+                minStepSize = double( stepSize ) / numSteps;
+
+            break;
+        }
+        case QwtDate::Week:
+        {
+            const int daysInStep = stepSize * 7;
+
+            if ( maxMinSteps >= daysInStep )
+            {
+                // we want to have one tick per day
+                minStepSize = 1.0 / 7.0;
+            }
+            else
+            {
+                // when the stepSize is more than a week we want to
+                // have a tick for each week
+
+                const int stepSizeInWeeks = stepSize;
+
+                if ( stepSizeInWeeks <= maxMinSteps )
+                {
+                    minStepSize = 1;
+                }
+                else
+                {
+                    minStepSize = QwtScaleArithmetic::divideInterval( 
+                        stepSizeInWeeks, maxMinSteps, 10 );
+                }
+            }
+            break;
+        }
+        case QwtDate::Month:
+        {
+            // fractions of months doesn't make any sense
+
+            if ( stepSize < maxMinSteps )
+                maxMinSteps = static_cast<int>( stepSize );
+
+            static int limits[] = { 1, 2, 3, 4, 6, 12 };
+
+            int numSteps = qwtStepCount( stepSize, maxMinSteps,
+                limits, sizeof( limits ) / sizeof( int ) );
+
+            if ( numSteps > 0 )
+                minStepSize = double( stepSize ) / numSteps;
+
+            break;
+        }
+        case QwtDate::Year:
+        {
+            if ( stepSize >= maxMinSteps )
+            {
+                minStepSize = QwtScaleArithmetic::divideInterval(
+                    stepSize, maxMinSteps, 10 );
+            }
+            else
+            {
+                // something in months
+
+                static int limits[] = { 1, 2, 3, 4, 6, 12 };
+
+                int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
+                    limits, sizeof( limits ) / sizeof( int ) );
+
+                if ( numSteps > 0 )
+                    minStepSize = double( stepSize ) / numSteps;
+            }
+                
+            break;
+        }
+        default:
+            break;
+    }
+
+    if ( intervalType != QwtDate::Month
+        && minStepSize == 0.0 )
+    {
+        minStepSize = 0.5 * stepSize;
+    }
+
+    return minStepSize;
+}
+
+static QList<double> qwtDstTicks( const QDateTime &dateTime,
+    int secondsMajor, int secondsMinor )
+{
+    if ( secondsMinor <= 0 )
+        QList<double>();
+
+    QDateTime minDate = dateTime.addSecs( -secondsMajor );
+    minDate = QwtDate::floor( minDate, QwtDate::Hour );
+
+    const double utcOffset = QwtDate::utcOffset( dateTime );
+
+    // find the hours where daylight saving time happens
+
+    double dstMin = QwtDate::toDouble( minDate );
+    while ( minDate < dateTime &&
+        QwtDate::utcOffset( minDate ) != utcOffset )
+    {
+        minDate = minDate.addSecs( 3600 );
+        dstMin += 3600 * 1000.0;
+    }
+
+    QList<double> ticks;
+    for ( int i = 0; i < 3600; i += secondsMinor )
+        ticks += dstMin + i * 1000.0;
+
+    return ticks;
+}
+
+static QwtScaleDiv qwtDivideToSeconds( 
+    const QDateTime &minDate, const QDateTime &maxDate,
+    double stepSize, int maxMinSteps,
+    QwtDate::IntervalType intervalType ) 
+{
+    // calculate the min step size
+    double minStepSize = 0;
+
+    if ( maxMinSteps > 1 ) 
+    {
+        minStepSize = qwtDivideMajorStep( stepSize, 
+            maxMinSteps, intervalType );
+    }
+
+    bool daylightSaving = false;
+    if ( minDate.timeSpec() == Qt::LocalTime )
+    {
+        daylightSaving = intervalType > QwtDate::Hour;
+        if ( intervalType == QwtDate::Hour )
+        {
+            daylightSaving = stepSize > 1;
+        }
+    }
+
+    const double s = qwtMsecsForType( intervalType ) / 1000;
+    const int secondsMajor = static_cast<int>( stepSize * s );
+    const double secondsMinor = minStepSize * s;
+    
+    // UTC excludes daylight savings. So from the difference
+    // of a date and its UTC counterpart we can find out
+    // the daylight saving hours
+
+    const double utcOffset = QwtDate::utcOffset( minDate );
+    double dstOff = 0;
+
+    QList<double> majorTicks;
+    QList<double> mediumTicks;
+    QList<double> minorTicks;
+
+    for ( QDateTime dt = minDate; dt <= maxDate; 
+        dt = dt.addSecs( secondsMajor ) )
+    {
+        if ( !dt.isValid() )
+            break;
+
+        double majorValue = QwtDate::toDouble( dt );
+
+        if ( daylightSaving )
+        {
+            const double offset = utcOffset - QwtDate::utcOffset( dt );
+            majorValue += offset * 1000.0;
+
+            if ( offset > dstOff )
+            {
+                // we add some minor ticks for the DST hour,
+                // otherwise the ticks will be unaligned: 0, 2, 3, 5 ...
+                minorTicks += qwtDstTicks( 
+                    dt, secondsMajor, qRound( secondsMinor ) );
+            }
+
+            dstOff = offset;
+        }
+
+        if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
+            majorTicks += majorValue;
+
+        if ( secondsMinor > 0.0 )
+        {
+            const int numMinorSteps = qFloor( secondsMajor / secondsMinor );
+
+            for ( int i = 1; i < numMinorSteps; i++ )
+            {
+                const QDateTime mt = dt.addMSecs( 
+                    qRound64( i * secondsMinor * 1000 ) );
+
+                double minorValue = QwtDate::toDouble( mt );
+                if ( daylightSaving )
+                {
+                    const double offset = utcOffset - QwtDate::utcOffset( mt );
+                    minorValue += offset * 1000.0;
+                }
+
+                if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
+                {
+                    const bool isMedium = ( numMinorSteps % 2 == 0 ) 
+                        && ( i != 1 ) && ( i == numMinorSteps / 2 );
+
+                    if ( isMedium )
+                        mediumTicks += minorValue;
+                    else
+                        minorTicks += minorValue;
+                }
+            }
+        }
+    }
+
+    QwtScaleDiv scaleDiv;
+
+    scaleDiv.setInterval( QwtDate::toDouble( minDate ),
+        QwtDate::toDouble( maxDate ) );
+
+    scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
+
+    return scaleDiv;
+}
+
+static QwtScaleDiv qwtDivideToMonths( 
+    QDateTime &minDate, const QDateTime &maxDate,
+    double stepSize, int maxMinSteps ) 
+{
+    // months are intervals with non 
+    // equidistant ( in ms ) steps: we have to build the 
+    // scale division manually
+
+    int minStepDays = 0;
+    int minStepSize = 0.0; 
+
+    if ( maxMinSteps > 1 )
+    {
+        if ( stepSize == 1 )
+        {
+            if ( maxMinSteps >= 30 )
+                minStepDays = 1;
+            else if ( maxMinSteps >= 6 )
+                minStepDays = 5;
+            else if ( maxMinSteps >= 3 )
+                minStepDays = 10;
+            else
+                minStepDays = 15;
+        }
+        else
+        {
+            minStepSize = qwtDivideMajorStep( 
+                stepSize, maxMinSteps, QwtDate::Month );
+        }
+    }
+
+    QList<double> majorTicks;
+    QList<double> mediumTicks;
+    QList<double> minorTicks;
+
+    for ( QDateTime dt = minDate; 
+        dt <= maxDate; dt = dt.addMonths( stepSize ) )
+    {
+        if ( !dt.isValid() )
+            break;
+
+        majorTicks += QwtDate::toDouble( dt );
+
+        if ( minStepDays > 0 )
+        {
+            for ( int days = minStepDays; 
+                days < 30; days += minStepDays )
+            {
+                const double tick = QwtDate::toDouble( dt.addDays( days ) );
+
+                if ( days == 15 && minStepDays != 15 )
+                    mediumTicks += tick;
+                else
+                    minorTicks += tick;
+            }
+        }
+        else if ( minStepSize > 0.0 )
+        {
+            const int numMinorSteps = qRound( stepSize / (double) minStepSize );
+
+            for ( int i = 1; i < numMinorSteps; i++ )
+            {
+                const double minorValue =
+                    QwtDate::toDouble( dt.addMonths( i * minStepSize ) );
+
+                if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
+                    mediumTicks += minorValue;
+                else
+                    minorTicks += minorValue;
+            }
+        }
+    }
+
+    QwtScaleDiv scaleDiv;
+    scaleDiv.setInterval( QwtDate::toDouble( minDate ),
+        QwtDate::toDouble( maxDate ) );
+
+    scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
+
+    return scaleDiv;
+}
+
+static QwtScaleDiv qwtDivideToYears( 
+    const QDateTime &minDate, const QDateTime &maxDate,
+    double stepSize, int maxMinSteps ) 
+{
+    QList<double> majorTicks;
+    QList<double> mediumTicks;
+    QList<double> minorTicks;
+
+    double minStepSize = 0.0;
+
+    if ( maxMinSteps > 1 )
+    {
+        minStepSize = qwtDivideMajorStep( 
+            stepSize, maxMinSteps, QwtDate::Year );
+    }
+
+    int numMinorSteps = 0;
+    if ( minStepSize > 0.0 )
+        numMinorSteps = qFloor( stepSize / minStepSize );
+
+    bool dateBC = minDate.date().year() < -1;
+
+    for ( QDateTime dt = minDate; dt <= maxDate;
+        dt = dt.addYears( stepSize ) )
+    {
+        if ( dateBC && dt.date().year() > 1 )
+        {
+            // there is no year 0 in the Julian calendar
+            dt = dt.addYears( -1 );
+            dateBC = false;
+        }
+
+        if ( !dt.isValid() )
+            break;
+
+        majorTicks += QwtDate::toDouble( dt );
+
+        for ( int i = 1; i < numMinorSteps; i++ )
+        {
+            QDateTime tickDate;
+
+            const double years = qRound( i * minStepSize );
+            if ( years >= INT_MAX / 12 )
+            {
+                tickDate = dt.addYears( years );
+            }
+            else
+            {
+                tickDate = dt.addMonths( qRound( years * 12 ) );
+            }
+
+            const bool isMedium = ( numMinorSteps > 2 ) &&
+                ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
+
+            const double minorValue = QwtDate::toDouble( tickDate );
+            if ( isMedium )
+                mediumTicks += minorValue;
+            else
+                minorTicks += minorValue;
+        }
+
+        if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() )
+        {
+            break;
+        }
+    }   
+
+    QwtScaleDiv scaleDiv;
+    scaleDiv.setInterval( QwtDate::toDouble( minDate ),
+        QwtDate::toDouble( maxDate ) );
+
+    scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
+    scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
+
+    return scaleDiv;
+}
+
+class QwtDateScaleEngine::PrivateData
+{
+public:
+    PrivateData( Qt::TimeSpec spec ):
+        timeSpec( spec ),
+        utcOffset( 0 ),
+        week0Type( QwtDate::FirstThursday ),
+        maxWeeks( 4 )
+    {
+    }
+
+    Qt::TimeSpec timeSpec;
+    int utcOffset;
+    QwtDate::Week0Type week0Type;
+    int maxWeeks;
+};      
+
+
+/*!
+  \brief Constructor
+
+  The engine is initialized to build scales for the 
+  given time specification. It classifies intervals > 4 weeks
+  as >= Qt::Month. The first week of a year is defined like
+  for QwtDate::FirstThursday.
+
+  \param timeSpec Time specification
+
+  \sa setTimeSpec(), setMaxWeeks(), setWeek0Type()
+ */
+QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ):
+    QwtLinearScaleEngine( 10 )
+{
+    d_data = new PrivateData( timeSpec );
+}
+
+//! Destructor
+QwtDateScaleEngine::~QwtDateScaleEngine()
+{
+    delete d_data;
+}
+
+/*!
+  Set the time specification used by the engine
+
+  \param timeSpec Time specification
+  \sa timeSpec(), setUtcOffset(), toDateTime()
+ */
+void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec )
+{
+    d_data->timeSpec = timeSpec;
+}
+
+/*!
+  \return Time specification used by the engine
+  \sa setTimeSpec(), utcOffset(), toDateTime()
+ */
+Qt::TimeSpec QwtDateScaleEngine::timeSpec() const
+{
+    return d_data->timeSpec;
+}
+
+/*!
+  Set the offset in seconds from Coordinated Universal Time
+
+  \param seconds Offset in seconds
+
+  \note The offset has no effect beside for the time specification
+        Qt::OffsetFromUTC.
+
+  \sa QDate::utcOffset(), setTimeSpec(), toDateTime()
+ */
+void QwtDateScaleEngine::setUtcOffset( int seconds )
+{
+    d_data->utcOffset = seconds;
+}
+
+/*!
+  \return Offset in seconds from Coordinated Universal Time
+  \note The offset has no effect beside for the time specification
+        Qt::OffsetFromUTC.
+
+  \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime()
+ */
+int QwtDateScaleEngine::utcOffset() const
+{
+    return d_data->utcOffset;
+}
+
+/*!
+  Sets how to identify the first week of a year.
+
+  \param week0Type Mode how to identify the first week of a year
+
+  \sa week0Type(), setMaxWeeks()
+  \note week0Type has no effect beside for intervals classified as
+        QwtDate::Week. 
+ */
+void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type )
+{
+    d_data->week0Type = week0Type;
+}
+
+/*!
+  \return Setting how to identify the first week of a year. 
+  \sa setWeek0Type(), maxWeeks()
+ */
+QwtDate::Week0Type QwtDateScaleEngine::week0Type() const
+{
+    return d_data->week0Type;
+}
+
+/*!
+  Set a upper limit for the number of weeks, when an interval
+  can be classified as Qt::Week.
+
+  The default setting is 4 weeks.
+
+  \param weeks Upper limit for the number of weeks
+
+  \note In business charts a year is often devided
+        into weeks [1-52]
+  \sa maxWeeks(), setWeek0Type() 
+ */
+void QwtDateScaleEngine::setMaxWeeks( int weeks )
+{
+    d_data->maxWeeks = qMax( weeks, 0 );
+}
+
+/*!
+  \return Upper limit for the number of weeks, when an interval
+          can be classified as Qt::Week.
+  \sa setMaxWeeks(), week0Type()
+ */
+int QwtDateScaleEngine::maxWeeks() const
+{
+    return d_data->maxWeeks;
+}
+
+/*!
+  Classification of a date/time interval division
+
+  \param minDate Minimum ( = earlier ) of the interval
+  \param maxDate Maximum ( = later ) of the interval
+  \param maxSteps Maximum for the number of steps
+
+  \return Interval classification
+ */
+QwtDate::IntervalType QwtDateScaleEngine::intervalType( 
+    const QDateTime &minDate, const QDateTime &maxDate, 
+    int maxSteps ) const
+{
+    const double jdMin = minDate.date().toJulianDay();
+    const double jdMax = maxDate.date().toJulianDay();
+
+    if ( ( jdMax - jdMin ) / 365 > maxSteps )
+        return QwtDate::Year;
+
+    const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month );
+    if ( months > maxSteps * 6 )
+        return QwtDate::Year;
+
+    const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day );
+    const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week );
+
+    if ( weeks > d_data->maxWeeks )
+    {
+        if ( days > 4 * maxSteps * 7 )
+            return QwtDate::Month;
+    }
+
+    if ( days > maxSteps * 7 )
+        return QwtDate::Week;
+
+    const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour );
+    if ( hours > maxSteps * 24 )
+        return QwtDate::Day;
+
+    const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second );
+
+    if ( seconds >= maxSteps * 3600 )
+        return QwtDate::Hour;
+
+    if ( seconds >= maxSteps * 60 )
+        return QwtDate::Minute;
+
+    if ( seconds >= maxSteps )
+        return QwtDate::Second;
+
+    return QwtDate::Millisecond;
+}
+
+/*!
+  Align and divide an interval
+
+  The algorithm aligns and divides the interval into steps. 
+
+  Datetime interval divisions are usually not equidistant and the
+  calculated stepSize can only be used as an approximation
+  for the steps calculated by divideScale(). 
+
+  \param maxNumSteps Max. number of steps
+  \param x1 First limit of the interval (In/Out)
+  \param x2 Second limit of the interval (In/Out)
+  \param stepSize Step size (Out)
+
+  \sa QwtScaleEngine::setAttribute()
+*/
+void QwtDateScaleEngine::autoScale( int maxNumSteps,
+    double &x1, double &x2, double &stepSize ) const
+{
+    stepSize = 0.0;
+
+    QwtInterval interval( x1, x2 );
+    interval = interval.normalized();
+
+    interval.setMinValue( interval.minValue() - lowerMargin() );
+    interval.setMaxValue( interval.maxValue() + upperMargin() );
+
+    if ( testAttribute( QwtScaleEngine::Symmetric ) )
+        interval = interval.symmetrize( reference() );
+
+    if ( testAttribute( QwtScaleEngine::IncludeReference ) )
+        interval = interval.extend( reference() );
+
+    if ( interval.width() == 0.0 )
+        interval = buildInterval( interval.minValue() );
+
+    const QDateTime from = toDateTime( interval.minValue() );
+    const QDateTime to = toDateTime( interval.maxValue() );
+
+    if ( from.isValid() && to.isValid() )
+    {
+        if ( maxNumSteps < 1 )
+            maxNumSteps = 1;
+
+        const QwtDate::IntervalType intvType = 
+            intervalType( from, to, maxNumSteps );
+
+        const double width = qwtIntervalWidth( from, to, intvType );
+
+        const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType );
+        if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) )
+        {
+            const QDateTime d1 = alignDate( from, stepWidth, intvType, false );
+            const QDateTime d2 = alignDate( to, stepWidth, intvType, true );
+
+            interval.setMinValue( QwtDate::toDouble( d1 ) );
+            interval.setMaxValue( QwtDate::toDouble( d2 ) );
+        }
+
+        stepSize = stepWidth * qwtMsecsForType( intvType );
+    }
+
+    x1 = interval.minValue();
+    x2 = interval.maxValue();
+
+    if ( testAttribute( QwtScaleEngine::Inverted ) )
+    {
+        qSwap( x1, x2 );
+        stepSize = -stepSize;
+    }
+}
+
+/*!
+   \brief Calculate a scale division for a date/time interval
+
+   \param x1 First interval limit
+   \param x2 Second interval limit
+   \param maxMajorSteps Maximum for the number of major steps
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size. If stepSize == 0, the scaleEngine
+                   calculates one.
+   \return Calculated scale division
+*/
+QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2,
+    int maxMajorSteps, int maxMinorSteps, double stepSize ) const
+{
+    if ( maxMajorSteps < 1 )
+        maxMajorSteps = 1;
+
+    const double min = qMin( x1, x2 );
+    const double max = qMax( x1, x2 );
+
+    const QDateTime from = toDateTime( min );
+    const QDateTime to = toDateTime( max );
+
+    if ( from == to )
+        return QwtScaleDiv();
+
+    stepSize = qAbs( stepSize );
+    if ( stepSize > 0.0 )
+    {
+        // as interval types above hours are not equidistant
+        // ( even days might have 23/25 hours because of daylight saving )
+        // the stepSize is used as a hint only
+
+        maxMajorSteps = qCeil( ( max - min ) / stepSize );
+    }
+
+    const QwtDate::IntervalType intvType = 
+        intervalType( from, to, maxMajorSteps );
+
+    QwtScaleDiv scaleDiv;
+
+    if ( intvType == QwtDate::Millisecond )
+    {
+        // for milliseconds and below we can use the decimal system
+        scaleDiv = QwtLinearScaleEngine::divideScale( min, max,
+            maxMajorSteps, maxMinorSteps, stepSize );
+    }
+    else
+    {
+        const QDateTime minDate = QwtDate::floor( from, intvType );
+        const QDateTime maxDate = QwtDate::ceil( to, intvType );
+
+        scaleDiv = buildScaleDiv( minDate, maxDate, 
+            maxMajorSteps, maxMinorSteps, intvType );
+
+        // scaleDiv has been calculated from an extended interval
+        // adjusted to the step size. We have to shrink it again.
+
+        scaleDiv = scaleDiv.bounded( min, max );
+    }
+
+    if ( x1 > x2 )
+        scaleDiv.invert();
+
+    return scaleDiv;
+}
+
+QwtScaleDiv QwtDateScaleEngine::buildScaleDiv( 
+    const QDateTime &minDate, const QDateTime &maxDate,
+    int maxMajorSteps, int maxMinorSteps,
+    QwtDate::IntervalType intervalType ) const
+{
+    // calculate the step size
+    const double stepSize = qwtDivideScale( 
+        qwtIntervalWidth( minDate, maxDate, intervalType ), 
+        maxMajorSteps, intervalType );
+
+    // align minDate to the step size
+    QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false );
+    if ( !dt0.isValid() )
+    {
+        // the floored date is out of the range of a 
+        // QDateTime - we ceil instead.
+        dt0 = alignDate( minDate, stepSize, intervalType, true );
+    }
+
+    QwtScaleDiv scaleDiv;
+
+    if ( intervalType <= QwtDate::Week )
+    {
+        scaleDiv = qwtDivideToSeconds( dt0, maxDate, 
+            stepSize, maxMinorSteps, intervalType );
+    }
+    else
+    {
+        if( intervalType == QwtDate::Month )
+        {
+            scaleDiv = qwtDivideToMonths( dt0, maxDate,
+                stepSize, maxMinorSteps );
+        }
+        else if ( intervalType == QwtDate::Year )
+        {
+            scaleDiv = qwtDivideToYears( dt0, maxDate,
+                stepSize, maxMinorSteps );
+        }
+    }
+
+
+    return scaleDiv;
+}
+
+/*!
+  Align a date/time value for a step size
+
+  For Qt::Day alignments there is no "natural day 0" -
+  instead the first day of the year is used to avoid jumping 
+  major ticks positions when panning a scale. For other alignments
+  ( f.e according to the first day of the month ) alignDate()
+  has to be overloaded.
+
+  \param dateTime Date/time value
+  \param stepSize Step size
+  \param intervalType Interval type
+  \param up When true dateTime is ceiled - otherwise it is floored
+
+  \return Aligned date/time value
+ */
+QDateTime QwtDateScaleEngine::alignDate( 
+    const QDateTime &dateTime, double stepSize, 
+    QwtDate::IntervalType intervalType, bool up ) const
+{
+    // what about: (year == 1582 && month == 10 && day > 4 && day < 15) ??
+
+    QDateTime dt = dateTime;
+
+    if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
+    {
+        dt.setUtcOffset( 0 );
+    }
+
+    switch( intervalType )
+    {
+        case QwtDate::Millisecond:
+        {
+            const int ms = qwtAlignValue( 
+                dt.time().msec(), stepSize, up ) ;
+
+            dt = QwtDate::floor( dateTime, QwtDate::Second );
+            dt = dt.addMSecs( ms );
+
+            break;
+        }
+        case QwtDate::Second:
+        {
+            int second = dt.time().second();
+            if ( up )
+            {
+                if ( dt.time().msec() > 0 )
+                    second++;
+            }
+
+            const int s = qwtAlignValue( second, stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Minute );
+            dt = dt.addSecs( s );
+
+            break;
+        }
+        case QwtDate::Minute:
+        {
+            int minute = dt.time().minute();
+            if ( up )
+            {
+                if ( dt.time().msec() > 0 || dt.time().second() > 0 )
+                    minute++;
+            }
+
+            const int m = qwtAlignValue( minute, stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Hour );
+            dt = dt.addSecs( m * 60 );
+
+            break;
+        }
+        case QwtDate::Hour:
+        {
+            int hour = dt.time().hour();
+            if ( up )
+            {
+                if ( dt.time().msec() > 0 || dt.time().second() > 0
+                    || dt.time().minute() > 0 )
+                {
+                    hour++;
+                }
+            }
+            const int h = qwtAlignValue( hour, stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Day );
+            dt = dt.addSecs( h * 3600 );
+
+            break;
+        }
+        case QwtDate::Day:
+        {
+            // What date do we expect f.e. from an alignment of 5 days ??
+            // Aligning them to the beginning of the year avoids at least
+            // jumping major ticks when panning
+
+            int day = dt.date().dayOfYear();
+            if ( up )
+            {
+                if ( dt.time() > QTime( 0, 0 ) )
+                    day++;
+            }
+
+            const int d = qwtAlignValue( day, stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Year );
+            dt = dt.addDays( d - 1 );
+
+            break;
+        }
+        case QwtDate::Week:
+        {
+            const QDate date = QwtDate::dateOfWeek0(
+                dt.date().year(), d_data->week0Type );
+
+            int numWeeks = date.daysTo( dt.date() ) / 7;
+            if ( up )
+            {
+                if ( dt.time() > QTime( 0, 0 ) ||
+                    date.daysTo( dt.date() ) % 7 )
+                {
+                    numWeeks++;
+                }
+            }
+
+            const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
+
+            dt = QwtDate::floor( dt, QwtDate::Day );
+            dt.setDate( date );
+            dt = dt.addDays( d );
+
+            break;
+        }
+        case QwtDate::Month:
+        {
+            int month = dt.date().month();
+            if ( up )
+            {
+                if ( dt.date().day() > 1 ||
+                    dt.time() > QTime( 0, 0 ) )
+                {
+                    month++;
+                }
+            }
+
+            const int m = qwtAlignValue( month - 1, stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Year );
+            dt = dt.addMonths( m );
+
+            break;
+        }
+        case QwtDate::Year:
+        {
+            int year = dateTime.date().year();
+            if ( up )
+            {
+                if ( dateTime.date().dayOfYear() > 1 ||
+                    dt.time() > QTime( 0, 0 ) )
+                {
+                    year++;
+                }
+            }
+
+            const int y = qwtAlignValue( year, stepSize, up );
+
+            dt = QwtDate::floor( dt, QwtDate::Day );
+            if ( y == 0 )
+            {
+                // there is no year 0 in the Julian calendar
+                dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
+            }
+            else
+            {
+                dt.setDate( QDate( y, 1, 1 ) );
+            }
+
+            break;
+        }
+    }
+
+    if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
+    {
+        dt.setUtcOffset( dateTime.utcOffset() );
+    }
+
+    return dt;
+}
+
+/*!
+  Translate a double value into a QDateTime object.
+
+  For QDateTime result is bounded by QwtDate::minDate() and QwtDate::maxDate()
+
+  \return QDateTime object initialized with timeSpec() and utcOffset().
+  \sa timeSpec(), utcOffset(), QwtDate::toDateTime()
+ */
+QDateTime QwtDateScaleEngine::toDateTime( double value ) const
+{
+    QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
+    if ( !dt.isValid() )
+    {
+        const QDate date = ( value <= 0.0 ) 
+            ? QwtDate::minDate() : QwtDate::maxDate();
+
+        dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec );
+    }
+
+    if ( d_data->timeSpec == Qt::OffsetFromUTC )
+    {
+        dt = dt.addSecs( d_data->utcOffset );
+        dt.setUtcOffset( d_data->utcOffset );
+    }
+
+    return dt;
+}
+
diff --git a/source/third_party/qwt/qwt_dial.cpp b/source/third_party/qwt/qwt_dial.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..950139964aad0ee8b82706872f10ab77afd3a205
--- /dev/null
+++ b/source/third_party/qwt/qwt_dial.cpp
@@ -0,0 +1,871 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_dial.h"
+#include "qwt/qwt_dial_needle.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_scale_engine.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_round_scale_draw.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <qalgorithms.h>
+#include <qmath.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+
+static inline double qwtAngleDist( double a1, double a2 )
+{
+    double dist = qAbs( a2 - a1 );
+    if ( dist > 360.0 )
+        dist -= 360.0;
+
+    return dist;
+}
+
+static inline bool qwtIsOnArc( double angle, double min, double max )
+{
+    if ( min < max )
+    {
+        return ( angle >= min ) && ( angle <= max );
+    }
+    else
+    {
+        return ( angle >= min ) || ( angle <= max );
+    }
+}
+
+static inline double qwtBoundedAngle( double min, double angle, double max )
+{
+    double from = qwtNormalizeDegrees( min );
+    double to = qwtNormalizeDegrees( max );
+
+    double a;
+
+    if ( qwtIsOnArc( angle, from, to ) )
+    {
+        a = angle;
+        if ( a < min )
+            a += 360.0;
+    }
+    else
+    {
+        if ( qwtAngleDist( angle, from ) <
+            qwtAngleDist( angle, to ) )
+        {
+            a = min;
+        }
+        else
+        {
+            a = max;
+        }
+    }
+
+    return a;
+}
+
+class QwtDial::PrivateData
+{
+public:
+    PrivateData():
+        frameShadow( Sunken ),
+        lineWidth( 0 ),
+        mode( RotateNeedle ),
+        origin( 90.0 ),
+        minScaleArc( 0.0 ),
+        maxScaleArc( 0.0 ),
+        needle( NULL ),
+        arcOffset( 0.0 ),
+        mouseOffset( 0.0 )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete needle;
+    }
+    Shadow frameShadow;
+    int lineWidth;
+
+    QwtDial::Mode mode;
+
+    double origin;
+    double minScaleArc;
+    double maxScaleArc;
+
+    QwtDialNeedle *needle;
+
+    double arcOffset;
+    double mouseOffset;
+
+    QPixmap pixmapCache;
+};
+
+/*!
+  \brief Constructor
+  \param parent Parent widget
+
+  Create a dial widget with no needle. The scale is initialized
+  to [ 0.0, 360.0 ] and 360 steps ( QwtAbstractSlider::setTotalSteps() ).
+  The origin of the scale is at 90°,
+
+  The value is set to 0.0.
+
+  The default mode is QwtDial::RotateNeedle.
+*/
+QwtDial::QwtDial( QWidget* parent ):
+    QwtAbstractSlider( parent )
+{
+    d_data = new PrivateData;
+
+    setFocusPolicy( Qt::TabFocus );
+
+    QPalette p = palette();
+    for ( int i = 0; i < QPalette::NColorGroups; i++ )
+    {
+        const QPalette::ColorGroup colorGroup =
+            static_cast<QPalette::ColorGroup>( i );
+
+        // Base: background color of the circle inside the frame.
+        // WindowText: background color of the circle inside the scale
+
+        p.setColor( colorGroup, QPalette::WindowText,
+            p.color( colorGroup, QPalette::Base ) );
+    }
+    setPalette( p );
+
+    QwtRoundScaleDraw* scaleDraw = new QwtRoundScaleDraw();
+    scaleDraw->setRadius( 0 );
+
+    setScaleDraw( scaleDraw );
+
+    setScaleArc( 0.0, 360.0 ); // scale as a full circle
+
+    setScaleMaxMajor( 10 );
+    setScaleMaxMinor( 5 );
+
+    setValue( 0.0 );
+}
+
+//!  Destructor
+QwtDial::~QwtDial()
+{
+    delete d_data;
+}
+
+/*!
+  Sets the frame shadow value from the frame style.
+
+  \param shadow Frame shadow
+  \sa setLineWidth(), QFrame::setFrameShadow()
+*/
+void QwtDial::setFrameShadow( Shadow shadow )
+{
+    if ( shadow != d_data->frameShadow )
+    {
+        invalidateCache();
+
+        d_data->frameShadow = shadow;
+        if ( lineWidth() > 0 )
+            update();
+    }
+}
+
+/*!
+  \return Frame shadow
+  /sa setFrameShadow(), lineWidth(), QFrame::frameShadow()
+*/
+QwtDial::Shadow QwtDial::frameShadow() const
+{
+    return d_data->frameShadow;
+}
+
+/*!
+  Sets the line width of the frame
+
+  \param lineWidth Line width
+  \sa setFrameShadow()
+*/
+void QwtDial::setLineWidth( int lineWidth )
+{
+    if ( lineWidth < 0 )
+        lineWidth = 0;
+
+    if ( d_data->lineWidth != lineWidth )
+    {
+        invalidateCache();
+
+        d_data->lineWidth = lineWidth;
+        update();
+    }
+}
+
+/*!
+  \return Line width of the frame
+  \sa setLineWidth(), frameShadow(), lineWidth()
+*/
+int QwtDial::lineWidth() const
+{
+    return d_data->lineWidth;
+}
+
+/*!
+  \return bounding rectangle of the circle inside the frame
+  \sa setLineWidth(), scaleInnerRect(), boundingRect()
+*/
+QRect QwtDial::innerRect() const
+{
+    const int lw = lineWidth();
+    return boundingRect().adjusted( lw, lw, -lw, -lw );
+}
+
+/*!
+  \return bounding rectangle of the dial including the frame
+  \sa setLineWidth(), scaleInnerRect(), innerRect()
+*/
+QRect QwtDial::boundingRect() const
+{
+    const QRect cr = contentsRect();
+
+    const double dim = qMin( cr.width(), cr.height() );
+
+    QRect inner( 0, 0, dim, dim );
+    inner.moveCenter( cr.center() );
+
+    return inner;
+}
+
+/*!
+  \return rectangle inside the scale
+  \sa setLineWidth(), boundingRect(), innerRect()
+*/
+QRect QwtDial::scaleInnerRect() const
+{
+    QRect rect = innerRect();
+
+    const QwtAbstractScaleDraw *sd = scaleDraw();
+    if ( sd )
+    {
+        int scaleDist = qCeil( sd->extent( font() ) );
+        scaleDist++; // margin
+
+        rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist );
+    }
+
+    return rect;
+}
+
+/*!
+  \brief Change the mode of the dial.
+  \param mode New mode
+
+  In case of QwtDial::RotateNeedle the needle is rotating, in case of
+  QwtDial::RotateScale, the needle points to origin()
+  and the scale is rotating.
+
+  The default mode is QwtDial::RotateNeedle.
+
+  \sa mode(), setValue(), setOrigin()
+*/
+void QwtDial::setMode( Mode mode )
+{
+    if ( mode != d_data->mode )
+    {
+        invalidateCache();
+
+        d_data->mode = mode;
+        sliderChange();
+    }
+}
+
+/*!
+  \return Mode of the dial.
+  \sa setMode(), origin(), setScaleArc(), value()
+*/
+QwtDial::Mode QwtDial::mode() const
+{
+    return d_data->mode;
+}
+
+/*!
+  Invalidate the internal caches used to speed up repainting
+ */
+void QwtDial::invalidateCache()
+{
+    d_data->pixmapCache = QPixmap();
+}
+
+/*!
+   Paint the dial
+   \param event Paint event
+*/
+void QwtDial::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    if ( d_data->mode == QwtDial::RotateScale )
+    {
+        painter.save();
+        painter.setRenderHint( QPainter::Antialiasing, true );
+
+        drawContents( &painter );
+
+        painter.restore();
+    }
+
+    const QRect r = contentsRect();
+    if ( r.size() != d_data->pixmapCache.size() )
+    {
+        d_data->pixmapCache = QwtPainter::backingStore( this, r.size() );
+        d_data->pixmapCache.fill( Qt::transparent );
+
+        QPainter p( &d_data->pixmapCache );
+        p.setRenderHint( QPainter::Antialiasing, true );
+        p.translate( -r.topLeft() );
+            
+        if ( d_data->mode != QwtDial::RotateScale )
+            drawContents( &p );
+
+        if ( lineWidth() > 0 )
+            drawFrame( &p );
+
+        if ( d_data->mode != QwtDial::RotateNeedle )
+            drawNeedle( &p );
+    }
+
+    painter.drawPixmap( r.topLeft(), d_data->pixmapCache );
+
+    if ( d_data->mode == QwtDial::RotateNeedle )
+        drawNeedle( &painter );
+
+    if ( hasFocus() )
+        drawFocusIndicator( &painter );
+}
+
+/*!
+  Draw the focus indicator
+  \param painter Painter
+*/
+void QwtDial::drawFocusIndicator( QPainter *painter ) const
+{
+    QwtPainter::drawFocusRect( painter, this, boundingRect() );
+}
+
+/*!
+  Draw the frame around the dial
+
+  \param painter Painter
+  \sa lineWidth(), frameShadow()
+*/
+void QwtDial::drawFrame( QPainter *painter )
+{
+    QwtPainter::drawRoundFrame( painter, boundingRect(),
+        palette(), lineWidth(), d_data->frameShadow );
+}
+
+/*!
+  \brief Draw the contents inside the frame
+
+  QPalette::Window is the background color outside of the frame.
+  QPalette::Base is the background color inside the frame.
+  QPalette::WindowText is the background color inside the scale.
+
+  \param painter Painter
+
+  \sa boundingRect(), innerRect(),
+    scaleInnerRect(), QWidget::setPalette()
+*/
+void QwtDial::drawContents( QPainter *painter ) const
+{
+    if ( testAttribute( Qt::WA_NoSystemBackground ) ||
+        palette().brush( QPalette::Base ) !=
+            palette().brush( QPalette::Window ) )
+    {
+        const QRectF br = boundingRect();
+
+        painter->save();
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( palette().brush( QPalette::Base ) );
+        painter->drawEllipse( br );
+        painter->restore();
+    }
+
+    const QRectF insideScaleRect = scaleInnerRect();
+    if ( palette().brush( QPalette::WindowText ) !=
+            palette().brush( QPalette::Base ) )
+    {
+        painter->save();
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( palette().brush( QPalette::WindowText ) );
+        painter->drawEllipse( insideScaleRect );
+        painter->restore();
+    }
+
+    const QPointF center = insideScaleRect.center();
+    const double radius = 0.5 * insideScaleRect.width();
+
+    painter->save();
+    drawScale( painter, center, radius );
+    painter->restore();
+
+    painter->save();
+    drawScaleContents( painter, center, radius );
+    painter->restore();
+}
+
+/*!
+  Draw the needle
+
+  \param painter Painter
+  \param center Center of the dial
+  \param radius Length for the needle
+  \param direction Direction of the needle in degrees, counter clockwise
+  \param colorGroup ColorGroup
+*/
+void QwtDial::drawNeedle( QPainter *painter, const QPointF &center,
+    double radius, double direction, QPalette::ColorGroup colorGroup ) const
+{
+    if ( d_data->needle )
+    {
+        direction = 360.0 - direction; // counter clockwise
+        d_data->needle->draw( painter, center, radius, direction, colorGroup );
+    }
+}
+
+void QwtDial::drawNeedle( QPainter *painter ) const
+{
+    if ( !isValid() )
+        return;
+
+    QPalette::ColorGroup colorGroup;
+    if ( isEnabled() )
+        colorGroup = hasFocus() ? QPalette::Active : QPalette::Inactive;
+    else
+        colorGroup = QPalette::Disabled;
+
+    const QRectF sr = scaleInnerRect();
+
+    painter->save();
+    painter->setRenderHint( QPainter::Antialiasing, true );
+    drawNeedle( painter, sr.center(), 0.5 * sr.width(),
+        scaleMap().transform( value() ) + 270.0, colorGroup );
+    painter->restore();
+}
+
+/*!
+  Draw the scale
+
+  \param painter Painter
+  \param center Center of the dial
+  \param radius Radius of the scale
+*/
+void QwtDial::drawScale( QPainter *painter, 
+    const QPointF &center, double radius ) const
+{
+    QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
+    if ( sd == NULL )
+        return;
+
+    sd->setRadius( radius );
+    sd->moveCenter( center );
+
+    QPalette pal = palette();
+
+    const QColor textColor = pal.color( QPalette::Text );
+    pal.setColor( QPalette::WindowText, textColor ); // ticks, backbone
+
+    painter->setFont( font() );
+    painter->setPen( QPen( textColor, sd->penWidth() ) );
+
+    painter->setBrush( Qt::red );
+    sd->draw( painter, pal );
+}
+
+/*!
+  Draw the contents inside the scale
+
+  Paints nothing.
+
+  \param painter Painter
+  \param center Center of the contents circle
+  \param radius Radius of the contents circle
+*/
+void QwtDial::drawScaleContents( QPainter *painter,
+    const QPointF &center, double radius ) const
+{
+    Q_UNUSED(painter);
+    Q_UNUSED(center);
+    Q_UNUSED(radius);
+}
+
+/*!
+  Set a needle for the dial
+
+  \param needle Needle
+
+  \warning The needle will be deleted, when a different needle is
+           set or in ~QwtDial()
+*/
+void QwtDial::setNeedle( QwtDialNeedle *needle )
+{
+    if ( needle != d_data->needle )
+    {
+        if ( d_data->needle )
+            delete d_data->needle;
+
+        d_data->needle = needle;
+        update();
+    }
+}
+
+/*!
+  \return needle
+  \sa setNeedle()
+*/
+const QwtDialNeedle *QwtDial::needle() const
+{
+    return d_data->needle;
+}
+
+/*!
+  \return needle
+  \sa setNeedle()
+*/
+QwtDialNeedle *QwtDial::needle()
+{
+    return d_data->needle;
+}
+
+//! \return the scale draw
+QwtRoundScaleDraw *QwtDial::scaleDraw()
+{
+    return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
+}
+
+//! \return the scale draw
+const QwtRoundScaleDraw *QwtDial::scaleDraw() const
+{
+    return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+  Set an individual scale draw
+
+  The motivation for setting a scale draw is often
+  to overload QwtRoundScaleDraw::label() to return 
+  individual tick labels.
+  
+  \param scaleDraw Scale draw
+  \warning The previous scale draw is deleted
+*/
+void QwtDial::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
+{
+    setAbstractScaleDraw( scaleDraw );
+    sliderChange();
+}
+
+/*!
+  Change the arc of the scale
+
+  \param minArc Lower limit
+  \param maxArc Upper limit
+
+  \sa minScaleArc(), maxScaleArc()
+*/
+void QwtDial::setScaleArc( double minArc, double maxArc )
+{
+    if ( minArc != 360.0 && minArc != -360.0 )
+        minArc = ::fmod( minArc, 360.0 );
+    if ( maxArc != 360.0 && maxArc != -360.0 )
+        maxArc = ::fmod( maxArc, 360.0 );
+
+    double minScaleArc = qMin( minArc, maxArc );
+    double maxScaleArc = qMax( minArc, maxArc );
+
+    if ( maxScaleArc - minScaleArc > 360.0 )
+        maxScaleArc = minScaleArc + 360.0;
+
+    if ( ( minScaleArc != d_data->minScaleArc ) || 
+        ( maxScaleArc != d_data->maxScaleArc ) )
+    {
+        d_data->minScaleArc = minScaleArc;
+        d_data->maxScaleArc = maxScaleArc;
+
+        invalidateCache();
+        sliderChange();
+    }
+}
+
+/*! 
+  Set the lower limit for the scale arc
+
+  \param min Lower limit of the scale arc
+  \sa setScaleArc(), setMaxScaleArc()
+ */
+void QwtDial::setMinScaleArc( double min )
+{
+    setScaleArc( min, d_data->maxScaleArc );
+}
+
+/*! 
+  \return Lower limit of the scale arc
+  \sa setScaleArc()
+*/
+double QwtDial::minScaleArc() const
+{
+    return d_data->minScaleArc;
+}
+
+/*! 
+  Set the upper limit for the scale arc
+
+  \param max Upper limit of the scale arc
+  \sa setScaleArc(), setMinScaleArc()
+ */
+void QwtDial::setMaxScaleArc( double max )
+{
+    setScaleArc( d_data->minScaleArc, max );
+}
+
+/*! 
+  \return Upper limit of the scale arc
+  \sa setScaleArc()
+*/
+double QwtDial::maxScaleArc() const
+{
+    return d_data->maxScaleArc;
+}
+
+/*!
+  \brief Change the origin
+
+  The origin is the angle where scale and needle is relative to.
+
+  \param origin New origin
+  \sa origin()
+*/
+void QwtDial::setOrigin( double origin )
+{
+    invalidateCache();
+
+    d_data->origin = origin;
+    sliderChange();
+}
+
+/*!
+  The origin is the angle where scale and needle is relative to.
+
+  \return Origin of the dial
+  \sa setOrigin()
+*/
+double QwtDial::origin() const
+{
+    return d_data->origin;
+}
+
+/*!
+  \return Size hint
+  \sa minimumSizeHint()
+*/
+QSize QwtDial::sizeHint() const
+{
+    int sh = 0;
+    if ( scaleDraw() )
+        sh = qCeil( scaleDraw()->extent( font() ) );
+
+    const int d = 6 * sh + 2 * lineWidth();
+
+    QSize hint( d, d ); 
+    if ( !isReadOnly() )
+        hint = hint.expandedTo( QApplication::globalStrut() );
+
+    return hint;
+}
+
+/*!
+  \return Minimum size hint
+  \sa sizeHint()
+*/
+QSize QwtDial::minimumSizeHint() const
+{
+    int sh = 0;
+    if ( scaleDraw() )
+        sh = qCeil( scaleDraw()->extent( font() ) );
+
+    const int d = 3 * sh + 2 * lineWidth();
+
+    return QSize( d, d );
+}
+
+/*!
+  \brief Determine what to do when the user presses a mouse button.
+
+  \param pos Mouse position
+
+  \retval True, when the inner circle contains pos 
+  \sa scrolledTo()
+*/
+bool QwtDial::isScrollPosition( const QPoint &pos ) const
+{
+    const QRegion region( innerRect(), QRegion::Ellipse );
+    if ( region.contains( pos ) && ( pos != innerRect().center() ) )
+    {
+        double angle = QLineF( rect().center(), pos ).angle();
+        if ( d_data->mode == QwtDial::RotateScale )
+            angle = 360.0 - angle;
+
+        double valueAngle = 
+            qwtNormalizeDegrees( 90.0 - scaleMap().transform( value() ) );
+
+        d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
+        d_data->arcOffset = scaleMap().p1();
+
+        return true;
+    }
+
+    return false;
+}
+
+/*!
+  \brief Determine the value for a new position of the
+         slider handle.
+
+  \param pos Mouse position
+
+  \return Value for the mouse position
+  \sa isScrollPosition()
+*/
+double QwtDial::scrolledTo( const QPoint &pos ) const
+{
+    double angle = QLineF( rect().center(), pos ).angle();
+    if ( d_data->mode == QwtDial::RotateScale )
+    {
+        angle += scaleMap().p1() - d_data->arcOffset;
+        angle = 360.0 - angle;
+    }
+
+    angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
+    angle = qwtNormalizeDegrees( 90.0 - angle );
+
+    if ( scaleMap().pDist() >= 360.0 )
+    {
+        if ( angle < scaleMap().p1() )
+            angle += 360.0;
+
+        if ( !wrapping() )
+        {
+            double boundedAngle = angle;
+
+            const double arc = angle - scaleMap().transform( value() );
+            if ( qAbs( arc ) > 180.0 )
+            {
+                boundedAngle = ( arc > 0 ) 
+                    ? scaleMap().p1() : scaleMap().p2();
+            }
+
+            d_data->mouseOffset += ( boundedAngle - angle );
+
+            angle = boundedAngle;
+        }
+    }
+    else
+    {
+        const double boundedAngle =
+            qwtBoundedAngle( scaleMap().p1(), angle, scaleMap().p2() );
+
+        if ( !wrapping() )
+            d_data->mouseOffset += ( boundedAngle - angle );
+
+        angle = boundedAngle;
+    }
+
+    return scaleMap().invTransform( angle );
+}
+
+/*!
+  Change Event handler
+  \param event Change event
+
+  Invalidates internal paint caches if necessary
+*/
+void QwtDial::changeEvent( QEvent *event )
+{
+    switch( event->type() )
+    {
+        case QEvent::EnabledChange:
+        case QEvent::FontChange:
+        case QEvent::StyleChange:
+        case QEvent::PaletteChange:
+        case QEvent::LanguageChange:
+        case QEvent::LocaleChange:
+        {
+            invalidateCache();
+            break;
+        }
+        default:
+            break;
+    }
+    
+    QwtAbstractSlider::changeEvent( event );
+}
+
+/*!
+  Wheel Event handler
+  \param event Wheel event
+*/
+void QwtDial::wheelEvent( QWheelEvent *event )
+{
+    const QRegion region( innerRect(), QRegion::Ellipse );
+    if ( region.contains( event->pos() ) )
+        QwtAbstractSlider::wheelEvent( event );
+}
+
+void QwtDial::setAngleRange( double angle, double span )
+{
+    QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
+    if ( sd  )
+    {
+        angle = qwtNormalizeDegrees( angle - 270.0 );
+        sd->setAngleRange( angle, angle + span );
+    }
+}
+
+/*!
+  Invalidate the internal caches and call 
+  QwtAbstractSlider::scaleChange()
+ */
+void QwtDial::scaleChange()
+{
+    invalidateCache();
+    QwtAbstractSlider::scaleChange();
+}
+
+void QwtDial::sliderChange()
+{
+    setAngleRange( d_data->origin + d_data->minScaleArc,
+        d_data->maxScaleArc - d_data->minScaleArc );
+
+    if ( mode() == RotateScale )
+    {
+        const double arc = scaleMap().transform( value() ) - scaleMap().p1();
+        setAngleRange( d_data->origin - arc,
+            d_data->maxScaleArc - d_data->minScaleArc );
+    }
+
+    QwtAbstractSlider::sliderChange();
+}
diff --git a/source/third_party/qwt/qwt_dial_needle.cpp b/source/third_party/qwt/qwt_dial_needle.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8e663d1f67b98962e65ff20a4278d7a417d3939
--- /dev/null
+++ b/source/third_party/qwt/qwt_dial_needle.cpp
@@ -0,0 +1,440 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_dial_needle.h"
+#include "qwt/qwt_global.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_painter.h"
+#include <qapplication.h>
+#include <qpainter.h>
+
+#if QT_VERSION < 0x040601
+#define qFastSin(x) qSin(x)
+#define qFastCos(x) qCos(x)
+#endif
+
+static void qwtDrawStyle1Needle( QPainter *painter,
+    const QPalette &palette, QPalette::ColorGroup colorGroup,
+    double length )
+{
+    const double r[] = { 0.4, 0.3, 1, 0.8, 1, 0.3, 0.4 };
+    const double a[] = { -45, -20, -15, 0, 15, 20, 45 };
+
+    QPainterPath path;
+    for ( int i = 0; i < 7; i++ )
+    {
+        const double angle = a[i] / 180.0 * M_PI;
+        const double radius = r[i] * length;
+
+        const double x = radius * qFastCos( angle );
+        const double y = radius * qFastSin( angle );
+
+        path.lineTo( x, -y );
+    }
+
+    painter->setPen( Qt::NoPen );
+    painter->setBrush( palette.brush( colorGroup, QPalette::Light ) );
+    painter->drawPath( path );
+}
+
+static void qwtDrawStyle2Needle( QPainter *painter,
+    const QPalette &palette, QPalette::ColorGroup colorGroup, double length )
+{
+    const double ratioX = 0.7;
+    const double ratioY = 0.3;
+
+    QPainterPath path1;
+    path1.lineTo( ratioX * length, 0.0 );
+    path1.lineTo( length, ratioY * length );
+
+    QPainterPath path2;
+    path2.lineTo( ratioX * length, 0.0 );
+    path2.lineTo( length, -ratioY * length );
+
+    painter->setPen( Qt::NoPen );
+
+    painter->setBrush( palette.brush( colorGroup, QPalette::Light ) );
+    painter->drawPath( path1 );
+
+    painter->setBrush( palette.brush( colorGroup, QPalette::Dark ) );
+    painter->drawPath( path2 );
+}
+
+static void qwtDrawShadedPointer( QPainter *painter, 
+    const QColor &lightColor, const QColor &darkColor,
+    double length, double width )
+{
+    const double peak = qMax( length / 10.0, 5.0 );
+
+    const double knobWidth = width + 8;
+    QRectF knobRect( 0, 0, knobWidth, knobWidth );
+    knobRect.moveCenter( QPointF(0, 0) );
+
+    QPainterPath path1;
+    path1.lineTo( 0.0, 0.5 * width );
+    path1.lineTo( length - peak, 0.5 * width );
+    path1.lineTo( length, 0.0 );
+    path1.lineTo( 0.0, 0.0 );
+
+    QPainterPath arcPath1;
+    arcPath1.arcTo( knobRect, 0.0, -90.0 );
+
+    path1 = path1.united( arcPath1 );
+
+    QPainterPath path2;
+    path2.lineTo( 0.0, -0.5 * width );
+    path2.lineTo( length - peak, -0.5 * width );
+    path2.lineTo( length, 0.0 );
+    path2.lineTo( 0.0, 0.0 );
+
+    QPainterPath arcPath2;
+    arcPath2.arcTo( knobRect, 0.0, 90.0 );
+
+    path2 = path2.united( arcPath2 );
+
+    painter->setPen( Qt::NoPen );
+
+    painter->setBrush( lightColor );
+    painter->drawPath( path1 );
+
+    painter->setBrush( darkColor );
+    painter->drawPath( path2 );
+}
+
+static void qwtDrawArrowNeedle( QPainter *painter,
+    const QPalette &palette, QPalette::ColorGroup colorGroup,
+    double length, double width )
+{
+    if ( width <= 0 )
+        width = qMax( length * 0.06, 9.0 );
+
+    const double peak = qMax( 2.0, 0.4 * width );
+
+    QPainterPath path;
+    path.moveTo( 0.0, 0.5 * width );
+    path.lineTo( length - peak, 0.3 * width );
+    path.lineTo( length, 0.0 );
+    path.lineTo( length - peak, -0.3 * width );
+    path.lineTo( 0.0, -0.5 * width );
+
+    QRectF br = path.boundingRect();
+
+    QPalette pal( palette.color( QPalette::Mid ) );
+    QColor c1 = pal.color( QPalette::Light );
+    QColor c2 = pal.color( QPalette::Dark );
+
+    QLinearGradient gradient( br.topLeft(), br.bottomLeft() );
+    gradient.setColorAt( 0.0, c1 );
+    gradient.setColorAt( 0.5, c1 );
+    gradient.setColorAt( 0.5001, c2 );
+    gradient.setColorAt( 1.0, c2 );
+
+    QPen pen( gradient, 1 );
+    pen.setJoinStyle( Qt::MiterJoin );
+
+    painter->setPen( pen );
+    painter->setBrush( palette.brush( colorGroup, QPalette::Mid ) );
+
+    painter->drawPath( path );
+}
+
+static void qwtDrawTriangleNeedle( QPainter *painter,
+    const QPalette &palette, QPalette::ColorGroup colorGroup,
+    double length )
+{
+    const double width = qRound( length / 3.0 );
+
+    QPainterPath path[4];
+
+    path[0].lineTo( length, 0.0 );
+    path[0].lineTo( 0.0, width / 2 );
+
+    path[1].lineTo( length, 0.0 );
+    path[1].lineTo( 0.0, -width / 2 );
+
+    path[2].lineTo( -length, 0.0 );
+    path[2].lineTo( 0.0, width / 2 );
+
+    path[3].lineTo( -length, 0.0 );
+    path[3].lineTo( 0.0, -width / 2 );
+
+
+    const int colorOffset =  10;
+    const QColor darkColor = palette.color( colorGroup, QPalette::Dark );
+    const QColor lightColor = palette.color( colorGroup, QPalette::Light );
+
+    QColor color[4];
+    color[0] = darkColor.light( 100 + colorOffset );
+    color[1] = darkColor.dark( 100 + colorOffset );
+    color[2] = lightColor.light( 100 + colorOffset );
+    color[3] = lightColor.dark( 100 + colorOffset );
+
+    painter->setPen( Qt::NoPen );
+
+    for ( int i = 0; i < 4; i++ )
+    {
+        painter->setBrush( color[i] );
+        painter->drawPath( path[i] );
+    }
+}
+
+//! Constructor
+QwtDialNeedle::QwtDialNeedle():
+    d_palette( QApplication::palette() )
+{
+}
+
+//! Destructor
+QwtDialNeedle::~QwtDialNeedle()
+{
+}
+
+/*!
+    Sets the palette for the needle.
+
+    \param palette New Palette
+*/
+void QwtDialNeedle::setPalette( const QPalette &palette )
+{
+    d_palette = palette;
+}
+
+/*!
+  \return the palette of the needle.
+*/
+const QPalette &QwtDialNeedle::palette() const
+{
+    return d_palette;
+}
+
+/*!
+  Draw the needle
+
+  \param painter Painter
+  \param center Center of the dial, start position for the needle
+  \param length Length of the needle
+  \param direction Direction of the needle, in degrees counter clockwise
+  \param colorGroup Color group, used for painting
+*/
+void QwtDialNeedle::draw( QPainter *painter, 
+    const QPointF &center, double length, double direction, 
+    QPalette::ColorGroup colorGroup ) const
+{
+    painter->save();
+
+    painter->translate( center );
+    painter->rotate( -direction );
+
+    drawNeedle( painter, length, colorGroup );
+
+    painter->restore();
+}
+
+//!  Draw the knob
+void QwtDialNeedle::drawKnob( QPainter *painter,
+    double width, const QBrush &brush, bool sunken ) const
+{
+    QPalette palette( brush.color() );
+
+    QColor c1 = palette.color( QPalette::Light );
+    QColor c2 = palette.color( QPalette::Dark );
+
+    if ( sunken )
+        qSwap( c1, c2 );
+
+    QRectF rect( 0.0, 0.0, width, width );
+    rect.moveCenter( painter->combinedTransform().map( QPointF() ) );
+
+    QLinearGradient gradient( rect.topLeft(), rect.bottomRight() );
+    gradient.setColorAt( 0.0, c1 );
+    gradient.setColorAt( 0.3, c1 );
+    gradient.setColorAt( 0.7, c2 );
+    gradient.setColorAt( 1.0, c2 );
+
+    painter->save();
+
+    painter->resetTransform();
+
+    painter->setPen( QPen( gradient, 1 ) );
+    painter->setBrush( brush );
+    painter->drawEllipse( rect );
+
+    painter->restore();
+}
+
+/*!
+  Constructor
+
+  \param style Style
+  \param hasKnob With/Without knob
+  \param mid Middle color
+  \param base Base color
+*/
+QwtDialSimpleNeedle::QwtDialSimpleNeedle( Style style, bool hasKnob,
+        const QColor &mid, const QColor &base ):
+    d_style( style ),
+    d_hasKnob( hasKnob ),
+    d_width( -1 )
+{
+    QPalette palette;
+    palette.setColor( QPalette::Mid, mid );
+    palette.setColor( QPalette::Base, base );
+
+    setPalette( palette );
+}
+
+/*!
+  Set the width of the needle
+  \param width Width
+  \sa width()
+*/
+void QwtDialSimpleNeedle::setWidth( double width )
+{
+    d_width = width;
+}
+
+/*!
+  \return the width of the needle
+  \sa setWidth()
+*/
+double QwtDialSimpleNeedle::width() const
+{
+    return d_width;
+}
+
+/*!
+ Draw the needle
+
+ \param painter Painter
+ \param length Length of the needle
+ \param colorGroup Color group, used for painting
+*/
+void QwtDialSimpleNeedle::drawNeedle( QPainter *painter, 
+    double length, QPalette::ColorGroup colorGroup ) const
+{
+    double knobWidth = 0.0;
+    double width = d_width;
+
+    if ( d_style == Arrow )
+    {
+        if ( width <= 0.0 )
+            width = qMax(length * 0.06, 6.0);
+
+        qwtDrawArrowNeedle( painter, 
+            palette(), colorGroup, length, width );
+
+        knobWidth = qMin( width * 2.0, 0.2 * length );
+    }
+    else
+    {
+        if ( width <= 0.0 )
+            width = 5.0;
+        
+        QPen pen ( palette().brush( colorGroup, QPalette::Mid ), width );
+        pen.setCapStyle( Qt::FlatCap );
+    
+        painter->setPen( pen );
+        painter->drawLine( QPointF( 0.0, 0.0 ), QPointF( length, 0.0 ) );
+
+        knobWidth = qMax( width * 3.0, 5.0 );
+    }
+
+    if ( d_hasKnob && knobWidth > 0.0 )
+    {
+        drawKnob( painter, knobWidth,
+            palette().brush( colorGroup, QPalette::Base ), false );
+    }
+}
+
+//! Constructor
+QwtCompassMagnetNeedle::QwtCompassMagnetNeedle( Style style,
+        const QColor &light, const QColor &dark ):
+    d_style( style )
+{
+    QPalette palette;
+    palette.setColor( QPalette::Light, light );
+    palette.setColor( QPalette::Dark, dark );
+    palette.setColor( QPalette::Base, Qt::gray );
+
+    setPalette( palette );
+}
+
+/*!
+    Draw the needle
+
+    \param painter Painter
+    \param length Length of the needle
+    \param colorGroup Color group, used for painting
+*/
+void QwtCompassMagnetNeedle::drawNeedle( QPainter *painter, 
+    double length, QPalette::ColorGroup colorGroup ) const
+{
+    if ( d_style == ThinStyle )
+    {
+        const double width = qMax( length / 6.0, 3.0 );
+
+        const int colorOffset = 10;
+
+        const QColor light = palette().color( colorGroup, QPalette::Light );
+        const QColor dark = palette().color( colorGroup, QPalette::Dark );
+
+        qwtDrawShadedPointer( painter,
+            dark.light( 100 + colorOffset ),
+            dark.dark( 100 + colorOffset ),
+            length, width );
+        
+        painter->rotate( 180.0 );
+    
+        qwtDrawShadedPointer( painter,
+            light.light( 100 + colorOffset ),
+            light.dark( 100 + colorOffset ),
+            length, width );
+        
+        const QBrush baseBrush = palette().brush( colorGroup, QPalette::Base );
+        drawKnob( painter, width, baseBrush, true );
+    }
+    else
+    {
+        qwtDrawTriangleNeedle( painter, palette(), colorGroup, length );
+    }
+}
+
+/*!
+   Constructor
+
+   \param style Arrow style
+   \param light Light color
+   \param dark Dark color
+*/
+QwtCompassWindArrow::QwtCompassWindArrow( Style style,
+        const QColor &light, const QColor &dark ):
+    d_style( style )
+{
+    QPalette palette;
+    palette.setColor( QPalette::Light, light );
+    palette.setColor( QPalette::Dark, dark );
+
+    setPalette( palette );
+}
+
+/*!
+ Draw the needle
+
+ \param painter Painter
+ \param length Length of the needle
+ \param colorGroup Color group, used for painting
+*/
+void QwtCompassWindArrow::drawNeedle( QPainter *painter, 
+    double length, QPalette::ColorGroup colorGroup ) const
+{
+    if ( d_style == Style1 )
+        qwtDrawStyle1Needle( painter, palette(), colorGroup, length );
+    else
+        qwtDrawStyle2Needle( painter, palette(), colorGroup, length );
+}
diff --git a/source/third_party/qwt/qwt_dyngrid_layout.cpp b/source/third_party/qwt/qwt_dyngrid_layout.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..438ceaec2ce395f1c5fdf9076e3c057419d2750c
--- /dev/null
+++ b/source/third_party/qwt/qwt_dyngrid_layout.cpp
@@ -0,0 +1,591 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_dyngrid_layout.h"
+#include "qwt/qwt_math.h"
+#include <qvector.h>
+#include <qlist.h>
+
+class QwtDynGridLayout::PrivateData
+{
+public:
+    PrivateData():
+        isDirty( true )
+    {
+    }
+
+    void updateLayoutCache();
+
+    mutable QList<QLayoutItem*> itemList;
+
+    uint maxColumns;
+    uint numRows;
+    uint numColumns;
+
+    Qt::Orientations expanding;
+
+    bool isDirty;
+    QVector<QSize> itemSizeHints;
+};
+
+void QwtDynGridLayout::PrivateData::updateLayoutCache()
+{
+    itemSizeHints.resize( itemList.count() );
+
+    int index = 0;
+
+    for ( QList<QLayoutItem*>::iterator it = itemList.begin();
+        it != itemList.end(); ++it, index++ )
+    {
+        itemSizeHints[ index ] = ( *it )->sizeHint();
+    }
+
+    isDirty = false;
+}
+
+/*!
+  \param parent Parent widget
+  \param margin Margin
+  \param spacing Spacing
+*/
+
+QwtDynGridLayout::QwtDynGridLayout( QWidget *parent,
+        int margin, int spacing ):
+    QLayout( parent )
+{
+    init();
+
+    setSpacing( spacing );
+    setMargin( margin );
+}
+
+/*!
+  \param spacing Spacing
+*/
+
+QwtDynGridLayout::QwtDynGridLayout( int spacing )
+{
+    init();
+    setSpacing( spacing );
+}
+
+/*!
+  Initialize the layout with default values.
+*/
+void QwtDynGridLayout::init()
+{
+    d_data = new QwtDynGridLayout::PrivateData;
+    d_data->maxColumns = d_data->numRows = d_data->numColumns = 0;
+    d_data->expanding = 0;
+}
+
+//! Destructor
+
+QwtDynGridLayout::~QwtDynGridLayout()
+{
+    for ( int i = 0; i < d_data->itemList.size(); i++ )
+        delete d_data->itemList[i];
+
+    delete d_data;
+}
+
+//! Invalidate all internal caches
+void QwtDynGridLayout::invalidate()
+{
+    d_data->isDirty = true;
+    QLayout::invalidate();
+}
+
+/*!
+  Limit the number of columns.
+  \param maxColumns upper limit, 0 means unlimited
+  \sa maxColumns()
+*/
+void QwtDynGridLayout::setMaxColumns( uint maxColumns )
+{
+    d_data->maxColumns = maxColumns;
+}
+
+/*!
+  \brief Return the upper limit for the number of columns.
+
+  0 means unlimited, what is the default.
+
+  \return Upper limit for the number of columns
+  \sa setMaxColumns()
+*/
+uint QwtDynGridLayout::maxColumns() const
+{
+    return d_data->maxColumns;
+}
+
+/*! 
+  \brief Add an item to the next free position.
+  \param item Layout item
+ */
+void QwtDynGridLayout::addItem( QLayoutItem *item )
+{
+    d_data->itemList.append( item );
+    invalidate();
+}
+
+/*!
+  \return true if this layout is empty.
+*/
+bool QwtDynGridLayout::isEmpty() const
+{
+    return d_data->itemList.isEmpty();
+}
+
+/*!
+  \return number of layout items
+*/
+uint QwtDynGridLayout::itemCount() const
+{
+    return d_data->itemList.count();
+}
+
+/*!
+  Find the item at a specific index
+
+  \param index Index
+  \return Item at a specific index
+  \sa takeAt()
+*/
+QLayoutItem *QwtDynGridLayout::itemAt( int index ) const
+{
+    if ( index < 0 || index >= d_data->itemList.count() )
+        return NULL;
+
+    return d_data->itemList.at( index );
+}
+
+/*!
+  Find the item at a specific index and remove it from the layout
+
+  \param index Index
+  \return Layout item, removed from the layout
+  \sa itemAt()
+*/
+QLayoutItem *QwtDynGridLayout::takeAt( int index )
+{
+    if ( index < 0 || index >= d_data->itemList.count() )
+        return NULL;
+
+    d_data->isDirty = true;
+    return d_data->itemList.takeAt( index );
+}
+
+//! \return Number of items in the layout
+int QwtDynGridLayout::count() const
+{
+    return d_data->itemList.count();
+}
+
+/*!
+  Set whether this layout can make use of more space than sizeHint().
+  A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
+  one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
+  to grow in both dimensions. The default value is 0.
+
+  \param expanding Or'd orientations
+  \sa expandingDirections()
+*/
+void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding )
+{
+    d_data->expanding = expanding;
+}
+
+/*!
+  \brief Returns whether this layout can make use of more space than sizeHint().
+
+  A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
+  one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
+  to grow in both dimensions.
+
+  \return Orientations, where the layout expands
+  \sa setExpandingDirections()
+*/
+Qt::Orientations QwtDynGridLayout::expandingDirections() const
+{
+    return d_data->expanding;
+}
+
+/*!
+  Reorganizes columns and rows and resizes managed items within
+  a rectangle.
+
+  \param rect Layout geometry
+*/
+void QwtDynGridLayout::setGeometry( const QRect &rect )
+{
+    QLayout::setGeometry( rect );
+
+    if ( isEmpty() )
+        return;
+
+    d_data->numColumns = columnsForWidth( rect.width() );
+    d_data->numRows = itemCount() / d_data->numColumns;
+    if ( itemCount() % d_data->numColumns )
+        d_data->numRows++;
+
+    QList<QRect> itemGeometries = layoutItems( rect, d_data->numColumns );
+
+    int index = 0;
+    for ( QList<QLayoutItem*>::iterator it = d_data->itemList.begin();
+        it != d_data->itemList.end(); ++it )
+    {
+        ( *it )->setGeometry( itemGeometries[index] );
+        index++;
+    }
+}
+
+/*!
+  \brief Calculate the number of columns for a given width. 
+
+  The calculation tries to use as many columns as possible 
+  ( limited by maxColumns() )
+
+  \param width Available width for all columns
+  \return Number of columns for a given width
+
+  \sa maxColumns(), setMaxColumns()
+*/
+uint QwtDynGridLayout::columnsForWidth( int width ) const
+{
+    if ( isEmpty() )
+        return 0;
+
+    uint maxColumns = itemCount();
+    if ( d_data->maxColumns > 0 ) 
+        maxColumns = qMin( d_data->maxColumns, maxColumns );
+
+    if ( maxRowWidth( maxColumns ) <= width )
+        return maxColumns;
+
+    for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ )
+    {
+        const int rowWidth = maxRowWidth( numColumns );
+        if ( rowWidth > width )
+            return numColumns - 1;
+    }
+
+    return 1; // At least 1 column
+}
+
+/*!
+  Calculate the width of a layout for a given number of
+  columns.
+
+  \param numColumns Given number of columns
+  \param itemWidth Array of the width hints for all items
+*/
+int QwtDynGridLayout::maxRowWidth( int numColumns ) const
+{
+    int col;
+
+    QVector<int> colWidth( numColumns );
+    for ( col = 0; col < numColumns; col++ )
+        colWidth[col] = 0;
+
+    if ( d_data->isDirty )
+        d_data->updateLayoutCache();
+
+    for ( int index = 0;
+        index < d_data->itemSizeHints.count(); index++ )
+    {
+        col = index % numColumns;
+        colWidth[col] = qMax( colWidth[col],
+            d_data->itemSizeHints[int( index )].width() );
+    }
+
+    int rowWidth = 2 * margin() + ( numColumns - 1 ) * spacing();
+    for ( col = 0; col < numColumns; col++ )
+        rowWidth += colWidth[col];
+
+    return rowWidth;
+}
+
+/*!
+  \return the maximum width of all layout items
+*/
+int QwtDynGridLayout::maxItemWidth() const
+{
+    if ( isEmpty() )
+        return 0;
+
+    if ( d_data->isDirty )
+        d_data->updateLayoutCache();
+
+    int w = 0;
+    for ( int i = 0; i < d_data->itemSizeHints.count(); i++ )
+    {
+        const int itemW = d_data->itemSizeHints[i].width();
+        if ( itemW > w )
+            w = itemW;
+    }
+
+    return w;
+}
+
+/*!
+  Calculate the geometries of the layout items for a layout
+  with numColumns columns and a given rectangle.
+
+  \param rect Rect where to place the items
+  \param numColumns Number of columns
+  \return item geometries
+*/
+
+QList<QRect> QwtDynGridLayout::layoutItems( const QRect &rect,
+    uint numColumns ) const
+{
+    QList<QRect> itemGeometries;
+    if ( numColumns == 0 || isEmpty() )
+        return itemGeometries;
+
+    uint numRows = itemCount() / numColumns;
+    if ( numColumns % itemCount() )
+        numRows++;
+
+    if ( numRows == 0 )
+        return itemGeometries;
+
+    QVector<int> rowHeight( numRows );
+    QVector<int> colWidth( numColumns );
+
+    layoutGrid( numColumns, rowHeight, colWidth );
+
+    bool expandH, expandV;
+    expandH = expandingDirections() & Qt::Horizontal;
+    expandV = expandingDirections() & Qt::Vertical;
+
+    if ( expandH || expandV )
+        stretchGrid( rect, numColumns, rowHeight, colWidth );
+
+    const int maxColumns = d_data->maxColumns;
+    d_data->maxColumns = numColumns;
+    const QRect alignedRect = alignmentRect( rect );
+    d_data->maxColumns = maxColumns;
+
+    const int xOffset = expandH ? 0 : alignedRect.x();
+    const int yOffset = expandV ? 0 : alignedRect.y();
+
+    QVector<int> colX( numColumns );
+    QVector<int> rowY( numRows );
+
+    const int xySpace = spacing();
+
+    rowY[0] = yOffset + margin();
+    for ( uint r = 1; r < numRows; r++ )
+        rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace;
+
+    colX[0] = xOffset + margin();
+    for ( uint c = 1; c < numColumns; c++ )
+        colX[c] = colX[c-1] + colWidth[c-1] + xySpace;
+
+    const int itemCount = d_data->itemList.size();
+    for ( int i = 0; i < itemCount; i++ )
+    {
+        const int row = i / numColumns;
+        const int col = i % numColumns;
+
+        QRect itemGeometry( colX[col], rowY[row],
+            colWidth[col], rowHeight[row] );
+        itemGeometries.append( itemGeometry );
+    }
+
+    return itemGeometries;
+}
+
+
+/*!
+  Calculate the dimensions for the columns and rows for a grid
+  of numColumns columns.
+
+  \param numColumns Number of columns.
+  \param rowHeight Array where to fill in the calculated row heights.
+  \param colWidth Array where to fill in the calculated column widths.
+*/
+
+void QwtDynGridLayout::layoutGrid( uint numColumns,
+    QVector<int>& rowHeight, QVector<int>& colWidth ) const
+{
+    if ( numColumns <= 0 )
+        return;
+
+    if ( d_data->isDirty )
+        d_data->updateLayoutCache();
+
+    for ( int index = 0; index < d_data->itemSizeHints.count(); index++ )
+    {
+        const int row = index / numColumns;
+        const int col = index % numColumns;
+
+        const QSize &size = d_data->itemSizeHints[int( index )];
+
+        rowHeight[row] = ( col == 0 )
+            ? size.height() : qMax( rowHeight[row], size.height() );
+        colWidth[col] = ( row == 0 )
+            ? size.width() : qMax( colWidth[col], size.width() );
+    }
+}
+
+/*!
+  \return true: QwtDynGridLayout implements heightForWidth().
+  \sa heightForWidth()
+*/
+bool QwtDynGridLayout::hasHeightForWidth() const
+{
+    return true;
+}
+
+/*!
+  \return The preferred height for this layout, given a width.
+  \sa hasHeightForWidth()
+*/
+int QwtDynGridLayout::heightForWidth( int width ) const
+{
+    if ( isEmpty() )
+        return 0;
+
+    const uint numColumns = columnsForWidth( width );
+    uint numRows = itemCount() / numColumns;
+    if ( itemCount() % numColumns )
+        numRows++;
+
+    QVector<int> rowHeight( numRows );
+    QVector<int> colWidth( numColumns );
+
+    layoutGrid( numColumns, rowHeight, colWidth );
+
+    int h = 2 * margin() + ( numRows - 1 ) * spacing();
+    for ( uint row = 0; row < numRows; row++ )
+        h += rowHeight[row];
+
+    return h;
+}
+
+/*!
+  Stretch columns in case of expanding() & QSizePolicy::Horizontal and
+  rows in case of expanding() & QSizePolicy::Vertical to fill the entire
+  rect. Rows and columns are stretched with the same factor.
+
+  \param rect Bounding rectangle
+  \param numColumns Number of columns
+  \param rowHeight Array to be filled with the calculated row heights
+  \param colWidth Array to be filled with the calculated column widths
+
+  \sa setExpanding(), expanding()
+*/
+void QwtDynGridLayout::stretchGrid( const QRect &rect,
+    uint numColumns, QVector<int>& rowHeight, QVector<int>& colWidth ) const
+{
+    if ( numColumns == 0 || isEmpty() )
+        return;
+
+    bool expandH, expandV;
+    expandH = expandingDirections() & Qt::Horizontal;
+    expandV = expandingDirections() & Qt::Vertical;
+
+    if ( expandH )
+    {
+        int xDelta = rect.width() - 2 * margin() - ( numColumns - 1 ) * spacing();
+        for ( uint col = 0; col < numColumns; col++ )
+            xDelta -= colWidth[col];
+
+        if ( xDelta > 0 )
+        {
+            for ( uint col = 0; col < numColumns; col++ )
+            {
+                const int space = xDelta / ( numColumns - col );
+                colWidth[col] += space;
+                xDelta -= space;
+            }
+        }
+    }
+
+    if ( expandV )
+    {
+        uint numRows = itemCount() / numColumns;
+        if ( itemCount() % numColumns )
+            numRows++;
+
+        int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing();
+        for ( uint row = 0; row < numRows; row++ )
+            yDelta -= rowHeight[row];
+
+        if ( yDelta > 0 )
+        {
+            for ( uint row = 0; row < numRows; row++ )
+            {
+                const int space = yDelta / ( numRows - row );
+                rowHeight[row] += space;
+                yDelta -= space;
+            }
+        }
+    }
+}
+
+/*!
+   Return the size hint. If maxColumns() > 0 it is the size for
+   a grid with maxColumns() columns, otherwise it is the size for
+   a grid with only one row.
+
+   \return Size hint
+   \sa maxColumns(), setMaxColumns()
+*/
+QSize QwtDynGridLayout::sizeHint() const
+{
+    if ( isEmpty() )
+        return QSize();
+
+    uint numColumns = itemCount();
+    if ( d_data->maxColumns > 0 )
+        numColumns = qMin( d_data->maxColumns, numColumns );
+
+    uint numRows = itemCount() / numColumns;
+    if ( itemCount() % numColumns )
+        numRows++;
+
+    QVector<int> rowHeight( numRows );
+    QVector<int> colWidth( numColumns );
+
+    layoutGrid( numColumns, rowHeight, colWidth );
+
+    int h = 2 * margin() + ( numRows - 1 ) * spacing();
+    for ( uint row = 0; row < numRows; row++ )
+        h += rowHeight[row];
+
+    int w = 2 * margin() + ( numColumns - 1 ) * spacing();
+    for ( uint col = 0; col < numColumns; col++ )
+        w += colWidth[col];
+
+    return QSize( w, h );
+}
+
+/*!
+  \return Number of rows of the current layout.
+  \sa numColumns()
+  \warning The number of rows might change whenever the geometry changes
+*/
+uint QwtDynGridLayout::numRows() const
+{
+    return d_data->numRows;
+}
+
+/*!
+  \return Number of columns of the current layout.
+  \sa numRows()
+  \warning The number of columns might change whenever the geometry changes
+*/
+uint QwtDynGridLayout::numColumns() const
+{
+    return d_data->numColumns;
+}
diff --git a/source/third_party/qwt/qwt_event_pattern.cpp b/source/third_party/qwt/qwt_event_pattern.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d805cb2e483f901318f32456d8b631812a68836c
--- /dev/null
+++ b/source/third_party/qwt/qwt_event_pattern.cpp
@@ -0,0 +1,265 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_event_pattern.h"
+#include <qevent.h>
+
+/*!
+  Constructor
+
+  \sa MousePatternCode, KeyPatternCode
+*/
+
+QwtEventPattern::QwtEventPattern():
+    d_mousePattern( MousePatternCount ),
+    d_keyPattern( KeyPatternCount )
+{
+    initKeyPattern();
+    initMousePattern( 3 );
+}
+
+//! Destructor
+QwtEventPattern::~QwtEventPattern()
+{
+}
+
+/*!
+  Set default mouse patterns, depending on the number of mouse buttons
+
+  \param numButtons Number of mouse buttons ( <= 3 )
+  \sa MousePatternCode
+*/
+void QwtEventPattern::initMousePattern( int numButtons )
+{
+    d_mousePattern.resize( MousePatternCount );
+
+    switch ( numButtons )
+    {
+        case 1:
+        {
+            setMousePattern( MouseSelect1, Qt::LeftButton );
+            setMousePattern( MouseSelect2, Qt::LeftButton, Qt::ControlModifier );
+            setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier );
+            break;
+        }
+        case 2:
+        {
+            setMousePattern( MouseSelect1, Qt::LeftButton );
+            setMousePattern( MouseSelect2, Qt::RightButton );
+            setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier );
+            break;
+        }
+        default:
+        {
+            setMousePattern( MouseSelect1, Qt::LeftButton );
+            setMousePattern( MouseSelect2, Qt::RightButton );
+            setMousePattern( MouseSelect3, Qt::MidButton );
+        }
+    }
+
+    setMousePattern( MouseSelect4, d_mousePattern[MouseSelect1].button,
+        d_mousePattern[MouseSelect1].modifiers | Qt::ShiftModifier );
+
+    setMousePattern( MouseSelect5, d_mousePattern[MouseSelect2].button,
+        d_mousePattern[MouseSelect2].modifiers | Qt::ShiftModifier );
+
+    setMousePattern( MouseSelect6, d_mousePattern[MouseSelect3].button,
+        d_mousePattern[MouseSelect3].modifiers | Qt::ShiftModifier );
+}
+
+/*!
+  Set default mouse patterns.
+
+  \sa KeyPatternCode
+*/
+void QwtEventPattern::initKeyPattern()
+{
+    d_keyPattern.resize( KeyPatternCount );
+
+    setKeyPattern( KeySelect1, Qt::Key_Return );
+    setKeyPattern( KeySelect2, Qt::Key_Space );
+    setKeyPattern( KeyAbort, Qt::Key_Escape );
+
+    setKeyPattern( KeyLeft, Qt::Key_Left );
+    setKeyPattern( KeyRight, Qt::Key_Right );
+    setKeyPattern( KeyUp, Qt::Key_Up );
+    setKeyPattern( KeyDown, Qt::Key_Down );
+
+    setKeyPattern( KeyRedo, Qt::Key_Plus );
+    setKeyPattern( KeyUndo, Qt::Key_Minus );
+    setKeyPattern( KeyHome, Qt::Key_Escape );
+}
+
+/*!
+  Change one mouse pattern
+
+  \param pattern Index of the pattern
+  \param button Button
+  \param modifiers Keyboard modifiers
+
+  \sa QMouseEvent
+*/
+void QwtEventPattern::setMousePattern( MousePatternCode pattern, 
+    Qt::MouseButton button, Qt::KeyboardModifiers modifiers )
+{
+    if ( pattern >= 0 && pattern < MousePatternCount )
+    {
+        d_mousePattern[ pattern ].button = button;
+        d_mousePattern[ pattern ].modifiers = modifiers;
+    }
+}
+
+/*!
+  Change one key pattern
+
+  \param pattern Index of the pattern
+  \param key Key
+  \param modifiers Keyboard modifiers
+
+  \sa QKeyEvent
+*/
+void QwtEventPattern::setKeyPattern( KeyPatternCode pattern, 
+    int key, Qt::KeyboardModifiers modifiers )
+{
+    if ( pattern >= 0 && pattern < KeyPatternCount )
+    {
+        d_keyPattern[ pattern ].key = key;
+        d_keyPattern[ pattern ].modifiers = modifiers;
+    }
+}
+
+//! Change the mouse event patterns
+void QwtEventPattern::setMousePattern( const QVector<MousePattern> &pattern )
+{
+    d_mousePattern = pattern;
+}
+
+//! Change the key event patterns
+void QwtEventPattern::setKeyPattern( const QVector<KeyPattern> &pattern )
+{
+    d_keyPattern = pattern;
+}
+
+//! \return Mouse pattern
+const QVector<QwtEventPattern::MousePattern> &
+QwtEventPattern::mousePattern() const
+{
+    return d_mousePattern;
+}
+
+//! \return Key pattern
+const QVector<QwtEventPattern::KeyPattern> &
+QwtEventPattern::keyPattern() const
+{
+    return d_keyPattern;
+}
+
+//! \return Mouse pattern
+QVector<QwtEventPattern::MousePattern> &QwtEventPattern::mousePattern()
+{
+    return d_mousePattern;
+}
+
+//! \return Key pattern
+QVector<QwtEventPattern::KeyPattern> &QwtEventPattern::keyPattern()
+{
+    return d_keyPattern;
+}
+
+/*!
+  \brief Compare a mouse event with an event pattern.
+
+  A mouse event matches the pattern when both have the same button
+  value and in the state value the same key flags(Qt::KeyButtonMask)
+  are set.
+
+  \param code Index of the event pattern
+  \param event Mouse event
+  \return true if matches
+
+  \sa keyMatch()
+*/
+bool QwtEventPattern::mouseMatch( MousePatternCode code, 
+    const QMouseEvent *event ) const
+{
+    if ( code >= 0 && code < MousePatternCount )
+        return mouseMatch( d_mousePattern[ code ], event );
+
+    return false;
+}
+
+/*!
+  \brief Compare a mouse event with an event pattern.
+
+  A mouse event matches the pattern when both have the same button
+  value and in the state value the same key flags(Qt::KeyButtonMask)
+  are set.
+
+  \param pattern Mouse event pattern
+  \param event Mouse event
+  \return true if matches
+
+  \sa keyMatch()
+*/
+
+bool QwtEventPattern::mouseMatch( const MousePattern &pattern,
+    const QMouseEvent *event ) const
+{
+    if ( event == NULL )
+        return false;
+
+    const MousePattern mousePattern( event->button(), event->modifiers() );
+    return mousePattern == pattern;
+}
+
+/*!
+  \brief Compare a key event with an event pattern.
+
+  A key event matches the pattern when both have the same key
+  value and in the state value the same key flags (Qt::KeyButtonMask)
+  are set.
+
+  \param code Index of the event pattern
+  \param event Key event
+  \return true if matches
+
+  \sa mouseMatch()
+*/
+bool QwtEventPattern::keyMatch( KeyPatternCode code, 
+    const QKeyEvent *event ) const
+{
+    if ( code >= 0 && code < KeyPatternCount )
+        return keyMatch( d_keyPattern[ code ], event );
+
+    return false;
+}
+
+/*!
+  \brief Compare a key event with an event pattern.
+
+  A key event matches the pattern when both have the same key
+  value and in the state value the same key flags (Qt::KeyButtonMask)
+  are set.
+
+  \param pattern Key event pattern
+  \param event Key event
+  \return true if matches
+
+  \sa mouseMatch()
+*/
+
+bool QwtEventPattern::keyMatch(
+    const KeyPattern &pattern, const QKeyEvent *event ) const
+{
+    if ( event == NULL )
+        return false;
+
+    const KeyPattern keyPattern( event->key(), event->modifiers() );
+    return keyPattern == pattern;
+}
diff --git a/source/third_party/qwt/qwt_graphic.cpp b/source/third_party/qwt/qwt_graphic.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9e04311889ef178b9e5da25bed4f12dd5f33ef14
--- /dev/null
+++ b/source/third_party/qwt/qwt_graphic.cpp
@@ -0,0 +1,1009 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_graphic.h"
+#include "qwt/qwt_painter_command.h"
+#include <qvector.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qpainterpath.h>
+#include <qmath.h>
+
+static bool qwtHasScalablePen( const QPainter *painter )
+{
+    const QPen pen = painter->pen();
+
+    bool scalablePen = false;
+
+    if ( pen.style() != Qt::NoPen && pen.brush().style() != Qt::NoBrush )
+    {
+        scalablePen = !pen.isCosmetic();
+        if ( !scalablePen && pen.widthF() == 0.0 )
+        {
+            const QPainter::RenderHints hints = painter->renderHints();
+            if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
+                scalablePen = true;
+        }
+    }
+
+    return scalablePen;
+}
+
+static QRectF qwtStrokedPathRect( 
+    const QPainter *painter, const QPainterPath &path )
+{
+    QPainterPathStroker stroker;
+    stroker.setWidth( painter->pen().widthF() );
+    stroker.setCapStyle( painter->pen().capStyle() );
+    stroker.setJoinStyle( painter->pen().joinStyle() );
+    stroker.setMiterLimit( painter->pen().miterLimit() );
+
+    QRectF rect;
+    if ( qwtHasScalablePen( painter ) )
+    {
+        QPainterPath stroke = stroker.createStroke(path);
+        rect = painter->transform().map(stroke).boundingRect();
+    }
+    else
+    {
+        QPainterPath mappedPath = painter->transform().map(path);
+        mappedPath = stroker.createStroke( mappedPath );
+
+        rect = mappedPath.boundingRect();
+    }
+
+    return rect;
+}
+
+static inline void qwtExecCommand( 
+    QPainter *painter, const QwtPainterCommand &cmd, 
+    QwtGraphic::RenderHints renderHints,
+    const QTransform &transform,
+    const QTransform *initialTransform )
+{
+    switch( cmd.type() )
+    {
+        case QwtPainterCommand::Path:
+        {
+            bool doMap = false;
+
+            if ( renderHints.testFlag( QwtGraphic::RenderPensUnscaled )
+                && painter->transform().isScaling() )
+            {
+                bool isCosmetic = painter->pen().isCosmetic();
+                if ( isCosmetic && painter->pen().widthF() == 0.0 )
+                {
+                    QPainter::RenderHints hints = painter->renderHints();
+                    if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
+                        isCosmetic = false;
+                }
+
+                doMap = !isCosmetic;
+            }
+
+            if ( doMap )
+            {
+                const QTransform tr = painter->transform();
+
+                painter->resetTransform();
+
+                QPainterPath path = tr.map( *cmd.path() );
+                if ( initialTransform )
+                {
+                    painter->setTransform( *initialTransform );
+                    path = initialTransform->inverted().map( path );
+                }
+
+                painter->drawPath( path );
+
+                painter->setTransform( tr );
+            }
+            else
+            {
+                painter->drawPath( *cmd.path() );
+            }
+            break;
+        }
+        case QwtPainterCommand::Pixmap:
+        {
+            const QwtPainterCommand::PixmapData *data = cmd.pixmapData();
+            painter->drawPixmap( data->rect, data->pixmap, data->subRect );
+            break;
+        }
+        case QwtPainterCommand::Image:
+        {
+            const QwtPainterCommand::ImageData *data = cmd.imageData();
+            painter->drawImage( data->rect, data->image, 
+                data->subRect, data->flags );
+            break;
+        }
+        case QwtPainterCommand::State:
+        {
+            const QwtPainterCommand::StateData *data = cmd.stateData();
+
+            if ( data->flags & QPaintEngine::DirtyPen ) 
+                painter->setPen( data->pen );
+
+            if ( data->flags & QPaintEngine::DirtyBrush ) 
+                painter->setBrush( data->brush );
+
+            if ( data->flags & QPaintEngine::DirtyBrushOrigin ) 
+                painter->setBrushOrigin( data->brushOrigin );
+
+            if ( data->flags & QPaintEngine::DirtyFont ) 
+                painter->setFont( data->font );
+
+            if ( data->flags & QPaintEngine::DirtyBackground ) 
+            {
+                painter->setBackgroundMode( data->backgroundMode );
+                painter->setBackground( data->backgroundBrush );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyTransform ) 
+            {
+                painter->setTransform( data->transform * transform );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyClipEnabled ) 
+                painter->setClipping( data->isClipEnabled );
+
+            if ( data->flags & QPaintEngine::DirtyClipRegion) 
+            {
+                painter->setClipRegion( data->clipRegion, 
+                    data->clipOperation );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyClipPath ) 
+            {
+                painter->setClipPath( data->clipPath, data->clipOperation );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyHints) 
+            {
+                const QPainter::RenderHints hints = data->renderHints;
+
+                painter->setRenderHint( QPainter::Antialiasing,
+                    hints.testFlag( QPainter::Antialiasing ) );
+
+                painter->setRenderHint( QPainter::TextAntialiasing,
+                    hints.testFlag( QPainter::TextAntialiasing ) );
+
+                painter->setRenderHint( QPainter::SmoothPixmapTransform,
+                    hints.testFlag( QPainter::SmoothPixmapTransform ) );
+
+                painter->setRenderHint( QPainter::HighQualityAntialiasing,
+                    hints.testFlag( QPainter::HighQualityAntialiasing ) );
+
+                painter->setRenderHint( QPainter::NonCosmeticDefaultPen,
+                    hints.testFlag( QPainter::NonCosmeticDefaultPen ) );
+            }
+
+            if ( data->flags & QPaintEngine::DirtyCompositionMode) 
+                painter->setCompositionMode( data->compositionMode );
+
+            if ( data->flags & QPaintEngine::DirtyOpacity) 
+                painter->setOpacity( data->opacity );
+
+            break;
+        }
+        default:
+            break;
+    }
+
+}
+
+class QwtGraphic::PathInfo
+{
+public:
+    PathInfo():
+        d_scalablePen( false )
+    {
+        // QVector needs a default constructor
+    }
+
+    PathInfo( const QRectF &pointRect, 
+            const QRectF &boundingRect, bool scalablePen ):
+        d_pointRect( pointRect ),
+        d_boundingRect( boundingRect ),
+        d_scalablePen( scalablePen )
+    {
+    }
+
+    inline QRectF scaledBoundingRect( double sx, double sy,
+        bool scalePens ) const
+    {
+        if ( sx == 1.0 && sy == 1.0 )
+            return d_boundingRect;
+
+        QTransform transform;
+        transform.scale( sx, sy );
+
+        QRectF rect;
+        if ( scalePens && d_scalablePen )
+        {
+            rect = transform.mapRect( d_boundingRect );
+        }
+        else
+        {
+            rect = transform.mapRect( d_pointRect );
+
+            const double l = qAbs( d_pointRect.left() - d_boundingRect.left() );
+            const double r = qAbs( d_pointRect.right() - d_boundingRect.right() );
+            const double t = qAbs( d_pointRect.top() - d_boundingRect.top() );
+            const double b = qAbs( d_pointRect.bottom() - d_boundingRect.bottom() );
+
+            rect.adjust( -l, -t, r, b );
+        }
+
+        return rect;
+    }
+
+    inline double scaleFactorX( const QRectF& pathRect, 
+        const QRectF &targetRect, bool scalePens ) const
+    {
+        if ( pathRect.width() <= 0.0 )
+            return 0.0;
+
+        const QPointF p0 = d_pointRect.center();
+
+        const double l = qAbs( pathRect.left() - p0.x() );
+        const double r = qAbs( pathRect.right() - p0.x() );
+
+        const double w = 2.0 * qMin( l, r ) 
+            * targetRect.width() / pathRect.width();
+
+        double sx;
+        if ( scalePens && d_scalablePen )
+        {
+            sx = w / d_boundingRect.width();
+        }
+        else
+        {
+            const double pw = qMax( 
+                qAbs( d_boundingRect.left() - d_pointRect.left() ),
+                qAbs( d_boundingRect.right() - d_pointRect.right() ) );
+
+            sx = ( w - 2 * pw ) / d_pointRect.width();
+        }
+
+        return sx;
+    }
+
+    inline double scaleFactorY( const QRectF& pathRect, 
+        const QRectF &targetRect, bool scalePens ) const
+    {
+        if ( pathRect.height() <= 0.0 )
+            return 0.0;
+
+        const QPointF p0 = d_pointRect.center();
+
+        const double t = qAbs( pathRect.top() - p0.y() );
+        const double b = qAbs( pathRect.bottom() - p0.y() );
+
+        const double h = 2.0 * qMin( t, b ) 
+            * targetRect.height() / pathRect.height();
+
+        double sy;
+        if ( scalePens && d_scalablePen )
+        {
+            sy = h / d_boundingRect.height();
+        }
+        else
+        {
+            const double pw = 
+                qMax( qAbs( d_boundingRect.top() - d_pointRect.top() ),
+                qAbs( d_boundingRect.bottom() - d_pointRect.bottom() ) );
+
+            sy = ( h - 2 * pw ) / d_pointRect.height();
+        }
+
+        return sy;
+    }
+
+private:
+    QRectF d_pointRect;
+    QRectF d_boundingRect;
+    bool d_scalablePen;
+};
+
+class QwtGraphic::PrivateData
+{
+public:
+    PrivateData():
+        boundingRect( 0.0, 0.0, -1.0, -1.0 ),
+        pointRect( 0.0, 0.0, -1.0, -1.0 ),
+        initialTransform( NULL )
+    {
+    }
+
+    QSizeF defaultSize;
+    QVector<QwtPainterCommand> commands;
+    QVector<QwtGraphic::PathInfo> pathInfos;
+
+    QRectF boundingRect;
+    QRectF pointRect;
+
+    QwtGraphic::RenderHints renderHints;
+    QTransform *initialTransform;
+};
+
+/*!
+  \brief Constructor
+
+  Initializes a null graphic
+  \sa isNull()
+ */
+QwtGraphic::QwtGraphic():
+    QwtNullPaintDevice()
+{
+    setMode( QwtNullPaintDevice::PathMode );
+    d_data = new PrivateData;
+}
+
+/*!
+  \brief Copy constructor
+
+  \param other Source 
+  \sa operator=()
+ */
+QwtGraphic::QwtGraphic( const QwtGraphic &other ):
+    QwtNullPaintDevice()
+{
+    setMode( other.mode() );
+    d_data = new PrivateData( *other.d_data );
+}
+
+//! Destructor
+QwtGraphic::~QwtGraphic()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Assignment operator
+
+  \param other Source 
+  \return A reference of this object
+ */
+QwtGraphic& QwtGraphic::operator=(const QwtGraphic &other)
+{
+    setMode( other.mode() );
+    *d_data = *other.d_data;
+
+    return *this;
+}
+
+/*!
+  \brief Clear all stored commands 
+  \sa isNull()
+ */
+void QwtGraphic::reset() 
+{
+    d_data->commands.clear();
+    d_data->pathInfos.clear();
+
+    d_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
+    d_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
+    d_data->defaultSize = QSizeF();
+
+}
+
+/*!
+  \return True, when no painter commands have been stored
+  \sa isEmpty(), commands()
+*/
+bool QwtGraphic::isNull() const
+{
+    return d_data->commands.isEmpty();
+}
+
+/*!
+  \return True, when the bounding rectangle is empty
+  \sa boundingRect(), isNull()
+*/
+bool QwtGraphic::isEmpty() const
+{
+    return d_data->boundingRect.isEmpty();
+}
+
+/*!
+  Toggle an render hint
+
+  \param hint Render hint
+  \param on true/false
+
+  \sa testRenderHint(), RenderHint
+*/
+void QwtGraphic::setRenderHint( RenderHint hint, bool on )
+{
+    if ( on )
+        d_data->renderHints |= hint;
+    else
+        d_data->renderHints &= ~hint;
+}
+
+/*!
+  Test a render hint
+
+  \param hint Render hint
+  \return true/false
+  \sa setRenderHint(), RenderHint
+*/
+bool QwtGraphic::testRenderHint( RenderHint hint ) const
+{
+    return d_data->renderHints.testFlag( hint );
+}
+
+/*!
+  The bounding rectangle is the controlPointRect()
+  extended by the areas needed for rendering the outlines
+  with unscaled pens.
+
+  \return Bounding rectangle of the graphic
+  \sa controlPointRect(), scaledBoundingRect()
+ */
+QRectF QwtGraphic::boundingRect() const
+{
+    if ( d_data->boundingRect.width() < 0 )
+        return QRectF();
+
+    return d_data->boundingRect;
+}
+
+/*!
+  The control point rectangle is the bounding rectangle 
+  of all control points of the paths and the target
+  rectangles of the images/pixmaps.
+
+  \return Control point rectangle
+  \sa boundingRect(), scaledBoundingRect()
+ */
+QRectF QwtGraphic::controlPointRect() const
+{
+    if ( d_data->pointRect.width() < 0 )
+        return QRectF();
+
+    return d_data->pointRect;
+}
+
+/*!
+  \brief Calculate the target rectangle for scaling the graphic
+
+  \param sx Horizontal scaling factor 
+  \param sy Vertical scaling factor 
+
+  \note In case of paths that are painted with a cosmetic pen 
+        ( see QPen::isCosmetic() ) the target rectangle is different to
+        multiplying the bounding rectangle.
+
+  \return Scaled bounding rectangle
+  \sa boundingRect(), controlPointRect()
+ */
+QRectF QwtGraphic::scaledBoundingRect( double sx, double sy ) const
+{
+    if ( sx == 1.0 && sy == 1.0 )
+        return d_data->boundingRect;
+
+    QTransform transform;
+    transform.scale( sx, sy );
+
+    QRectF rect = transform.mapRect( d_data->pointRect );
+
+    for ( int i = 0; i < d_data->pathInfos.size(); i++ )
+    {
+        rect |= d_data->pathInfos[i].scaledBoundingRect( sx, sy, 
+            !d_data->renderHints.testFlag( RenderPensUnscaled ) );
+    }
+
+    return rect;
+}
+
+//! \return Ceiled defaultSize()
+QSize QwtGraphic::sizeMetrics() const
+{
+    const QSizeF sz = defaultSize();
+    return QSize( qCeil( sz.width() ), qCeil( sz.height() ) );
+}
+
+/*!
+  \brief Set a default size
+
+  The default size is used in all methods rendering the graphic,
+  where no size is explicitly specified. Assigning an empty size
+  means, that the default size will be calculated from the bounding 
+  rectangle.
+
+  The default setting is an empty size.
+    
+  \param size Default size
+
+  \sa defaultSize(), boundingRect()
+ */
+void QwtGraphic::setDefaultSize( const QSizeF &size )
+{
+    const double w = qMax( qreal( 0.0 ), size.width() );
+    const double h = qMax( qreal( 0.0 ), size.height() );
+
+    d_data->defaultSize = QSizeF( w, h );
+}
+
+/*!
+  \brief Default size
+
+  When a non empty size has been assigned by setDefaultSize() this
+  size will be returned. Otherwise the default size is the size
+  of the bounding rectangle.
+
+  The default size is used in all methods rendering the graphic,
+  where no size is explicitly specified. 
+
+  \return Default size
+  \sa setDefaultSize(), boundingRect()
+ */
+QSizeF QwtGraphic::defaultSize() const
+{
+    if ( !d_data->defaultSize.isEmpty() )
+        return d_data->defaultSize;
+
+    return boundingRect().size();
+}
+
+/*!
+  \brief Replay all recorded painter commands
+  \param painter Qt painter
+ */
+void QwtGraphic::render( QPainter *painter ) const
+{
+    if ( isNull() )
+        return;
+
+    const int numCommands = d_data->commands.size();
+    const QwtPainterCommand *commands = d_data->commands.constData();
+
+    const QTransform transform = painter->transform();
+
+    painter->save();
+
+    for ( int i = 0; i < numCommands; i++ )
+    {
+        qwtExecCommand( painter, commands[i], 
+            d_data->renderHints, transform, d_data->initialTransform );
+    }
+
+    painter->restore();
+}
+
+/*!
+  \brief Replay all recorded painter commands
+
+  The graphic is scaled to fit into the rectangle
+  of the given size starting at ( 0, 0 ).
+
+  \param painter Qt painter
+  \param size Size for the scaled graphic
+  \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode
+ */
+void QwtGraphic::render( QPainter *painter, const QSizeF &size, 
+    Qt::AspectRatioMode aspectRatioMode ) const
+{
+    const QRectF r( 0.0, 0.0, size.width(), size.height() );
+    render( painter, r, aspectRatioMode );
+}
+
+/*!
+  \brief Replay all recorded painter commands
+
+  The graphic is scaled to fit into the given rectangle
+
+  \param painter Qt painter
+  \param rect Rectangle for the scaled graphic
+  \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode
+ */
+void QwtGraphic::render( QPainter *painter, const QRectF &rect, 
+    Qt::AspectRatioMode aspectRatioMode ) const
+{
+    if ( isEmpty() || rect.isEmpty() )
+        return;
+
+    double sx = 1.0; 
+    double sy = 1.0;
+
+    if ( d_data->pointRect.width() > 0.0 )
+        sx = rect.width() / d_data->pointRect.width();
+
+    if ( d_data->pointRect.height() > 0.0 )
+        sy = rect.height() / d_data->pointRect.height();
+
+    const bool scalePens = 
+        !d_data->renderHints.testFlag( RenderPensUnscaled );
+
+    for ( int i = 0; i < d_data->pathInfos.size(); i++ )
+    {
+        const PathInfo info = d_data->pathInfos[i];
+
+        const double ssx = info.scaleFactorX( 
+            d_data->pointRect, rect, scalePens );
+
+        if ( ssx > 0.0 )
+            sx = qMin( sx, ssx );
+
+        const double ssy = info.scaleFactorY( 
+            d_data->pointRect, rect, scalePens );
+
+        if ( ssy > 0.0 )
+            sy = qMin( sy, ssy );
+    }
+
+    if ( aspectRatioMode == Qt::KeepAspectRatio )
+    {
+        const double s = qMin( sx, sy );
+        sx = s;
+        sy = s;
+    }
+    else if ( aspectRatioMode == Qt::KeepAspectRatioByExpanding )
+    {
+        const double s = qMax( sx, sy );
+        sx = s;
+        sy = s;
+    }
+
+    QTransform tr;
+    tr.translate( rect.center().x() - 0.5 * sx * d_data->pointRect.width(),
+        rect.center().y() - 0.5 * sy * d_data->pointRect.height() );
+    tr.scale( sx, sy );
+    tr.translate( -d_data->pointRect.x(), -d_data->pointRect.y() );
+
+    const QTransform transform = painter->transform();
+    if ( !scalePens && transform.isScaling() )
+    {
+        // we don't want to scale pens according to sx/sy,
+        // but we want to apply the scaling from the 
+        // painter transformation later
+
+        d_data->initialTransform = new QTransform();
+        d_data->initialTransform->scale( transform.m11(), transform.m22() );
+    }
+
+    painter->setTransform( tr, true );
+    render( painter );
+
+    painter->setTransform( transform );
+
+    delete d_data->initialTransform;
+    d_data->initialTransform = NULL;
+}
+
+/*!
+  \brief Replay all recorded painter commands
+
+  The graphic is scaled to the defaultSize() and aligned
+  to a position.
+
+  \param painter Qt painter
+  \param pos Reference point, where to render
+  \param alignment Flags how to align the target rectangle 
+                   to pos.
+ */
+void QwtGraphic::render( QPainter *painter, 
+    const QPointF &pos, Qt::Alignment alignment ) const
+{
+    QRectF r( pos, defaultSize() );
+
+    if ( alignment & Qt::AlignLeft )
+    {
+        r.moveLeft( pos.x() );
+    }
+    else if ( alignment & Qt::AlignHCenter )
+    {
+        r.moveCenter( QPointF( pos.x(), r.center().y() ) );
+    }
+    else if ( alignment & Qt::AlignRight )
+    {
+        r.moveRight( pos.x() );
+    }
+
+    if ( alignment & Qt::AlignTop )
+    {
+        r.moveTop( pos.y() );
+    }
+    else if ( alignment & Qt::AlignVCenter )
+    {
+        r.moveCenter( QPointF( r.center().x(), pos.y() ) );
+    }
+    else if ( alignment & Qt::AlignBottom )
+    {
+        r.moveBottom( pos.y() );
+    }
+
+    render( painter, r );
+}
+
+/*!
+  \brief Convert the graphic to a QPixmap
+    
+  All pixels of the pixmap get initialized by Qt::transparent
+  before the graphic is scaled and rendered on it.
+    
+  The size of the pixmap is the default size ( ceiled to integers )
+  of the graphic.
+
+  \return The graphic as pixmap in default size
+  \sa defaultSize(), toImage(), render()
+ */ 
+QPixmap QwtGraphic::toPixmap() const
+{
+    if ( isNull() )
+        return QPixmap();
+
+    const QSizeF sz = defaultSize();
+
+    const int w = qCeil( sz.width() );
+    const int h = qCeil( sz.height() );
+
+    QPixmap pixmap( w, h );
+    pixmap.fill( Qt::transparent );
+
+    const QRectF r( 0.0, 0.0, sz.width(), sz.height() );
+
+    QPainter painter( &pixmap );
+    render( &painter, r, Qt::KeepAspectRatio );
+    painter.end();
+
+    return pixmap;
+}
+
+/*!
+  \brief Convert the graphic to a QPixmap
+
+  All pixels of the pixmap get initialized by Qt::transparent
+  before the graphic is scaled and rendered on it.
+
+  \param size Size of the image
+  \param aspectRatioMode Aspect ratio how to scale the graphic
+
+  \return The graphic as pixmap
+  \sa toImage(), render()
+ */
+QPixmap QwtGraphic::toPixmap( const QSize &size,
+    Qt::AspectRatioMode aspectRatioMode ) const
+{
+    QPixmap pixmap( size );
+    pixmap.fill( Qt::transparent );
+
+    const QRect r( 0, 0, size.width(), size.height() );
+
+    QPainter painter( &pixmap );
+    render( &painter, r, aspectRatioMode );
+    painter.end();
+
+    return pixmap;
+}
+
+/*!
+  \brief Convert the graphic to a QImage
+
+  All pixels of the image get initialized by 0 ( transparent )
+  before the graphic is scaled and rendered on it.
+
+  The format of the image is QImage::Format_ARGB32_Premultiplied.
+
+  \param size Size of the image
+  \param aspectRatioMode Aspect ratio how to scale the graphic
+
+  \return The graphic as image
+  \sa toPixmap(), render()
+ */
+QImage QwtGraphic::toImage( const QSize &size,
+    Qt::AspectRatioMode aspectRatioMode  ) const
+{
+    QImage image( size, QImage::Format_ARGB32_Premultiplied );
+    image.fill( 0 );
+
+    const QRect r( 0, 0, size.width(), size.height() );
+
+    QPainter painter( &image );
+    render( &painter, r, aspectRatioMode );
+    painter.end();
+
+    return image;
+}
+
+/*!
+  \brief Convert the graphic to a QImage
+    
+  All pixels of the image get initialized by 0 ( transparent )
+  before the graphic is scaled and rendered on it.
+
+  The format of the image is QImage::Format_ARGB32_Premultiplied.
+
+  The size of the image is the default size ( ceiled to integers )
+  of the graphic.
+    
+  \return The graphic as image in default size
+  \sa defaultSize(), toPixmap(), render()
+ */
+QImage QwtGraphic::toImage() const
+{
+    if ( isNull() )
+        return QImage();
+
+    const QSizeF sz = defaultSize();
+
+    const int w = qCeil( sz.width() );
+    const int h = qCeil( sz.height() );
+
+    QImage image( w, h, QImage::Format_ARGB32 );
+    image.fill( 0 );
+
+    const QRect r( 0, 0, sz.width(), sz.height() );
+
+    QPainter painter( &image );
+    render( &painter, r, Qt::KeepAspectRatio );
+    painter.end();
+
+    return image;
+}
+
+/*!
+  Store a path command in the command list
+
+  \param path Painter path
+  \sa QPaintEngine::drawPath()
+*/
+void QwtGraphic::drawPath( const QPainterPath &path )
+{
+    const QPainter *painter = paintEngine()->painter();
+    if ( painter == NULL )
+        return;
+
+    d_data->commands += QwtPainterCommand( path );
+
+    if ( !path.isEmpty() )
+    {
+        const QPainterPath scaledPath = painter->transform().map( path );
+
+        QRectF pointRect = scaledPath.boundingRect();
+        QRectF boundingRect = pointRect;
+
+        if ( painter->pen().style() != Qt::NoPen 
+            && painter->pen().brush().style() != Qt::NoBrush )
+        {
+            boundingRect = qwtStrokedPathRect( painter, path );
+        }
+
+        updateControlPointRect( pointRect );
+        updateBoundingRect( boundingRect );
+
+        d_data->pathInfos += PathInfo( pointRect, 
+            boundingRect, qwtHasScalablePen( painter ) );
+    }
+}
+
+/*!
+  \brief Store a pixmap command in the command list
+
+  \param rect target rectangle
+  \param pixmap Pixmap to be painted
+  \param subRect Reactangle of the pixmap to be painted
+
+  \sa QPaintEngine::drawPixmap()
+*/
+void QwtGraphic::drawPixmap( const QRectF &rect, 
+    const QPixmap &pixmap, const QRectF &subRect )
+{
+    const QPainter *painter = paintEngine()->painter();
+    if ( painter == NULL )
+        return;
+
+    d_data->commands += QwtPainterCommand( rect, pixmap, subRect );
+
+    const QRectF r = painter->transform().mapRect( rect );
+    updateControlPointRect( r );
+    updateBoundingRect( r );
+}
+
+/*!
+  \brief Store a image command in the command list
+
+  \param rect traget rectangle
+  \param image Image to be painted
+  \param subRect Reactangle of the pixmap to be painted
+  \param flags Image conversion flags
+
+  \sa QPaintEngine::drawImage()
+ */
+void QwtGraphic::drawImage( const QRectF &rect, const QImage &image,
+    const QRectF &subRect, Qt::ImageConversionFlags flags)
+{
+    const QPainter *painter = paintEngine()->painter();
+    if ( painter == NULL )
+        return;
+
+    d_data->commands += QwtPainterCommand( rect, image, subRect, flags );
+
+    const QRectF r = painter->transform().mapRect( rect );
+
+    updateControlPointRect( r );
+    updateBoundingRect( r );
+}
+
+/*!
+  \brief Store a state command in the command list
+
+  \param state State to be stored
+  \sa QPaintEngine::updateState()
+ */
+void QwtGraphic::updateState( const QPaintEngineState &state)
+{
+    d_data->commands += QwtPainterCommand( state );
+}
+
+void QwtGraphic::updateBoundingRect( const QRectF &rect )
+{
+    QRectF br = rect;
+
+    const QPainter *painter = paintEngine()->painter();
+    if ( painter && painter->hasClipping() )
+    {
+        QRectF cr = painter->clipRegion().boundingRect();
+        cr = painter->transform().mapRect( cr );
+
+        br &= cr;
+    }
+
+    if ( d_data->boundingRect.width() < 0 )
+        d_data->boundingRect = br;
+    else
+        d_data->boundingRect |= br;
+}
+
+void QwtGraphic::updateControlPointRect( const QRectF &rect )
+{
+    if ( d_data->pointRect.width() < 0.0 )
+        d_data->pointRect = rect;
+    else
+        d_data->pointRect |= rect;
+}
+
+/*!
+  \return List of recorded paint commands
+  \sa setCommands()
+ */
+const QVector< QwtPainterCommand > &QwtGraphic::commands() const
+{
+    return d_data->commands;
+}
+
+/*!
+  \brief Append paint commands
+
+  \param commands Paint commands
+  \sa commands()
+ */
+void QwtGraphic::setCommands( QVector< QwtPainterCommand > &commands )
+{
+    reset();
+
+    const int numCommands = commands.size();
+    if ( numCommands <= 0 )
+        return;
+
+    // to calculate a proper bounding rectangle we don't simply copy 
+    // the commands. 
+
+    const QwtPainterCommand *cmds = commands.constData();
+
+    QPainter painter( this );
+    for ( int i = 0; i < numCommands; i++ )
+        qwtExecCommand( &painter, cmds[i], RenderHints(), QTransform(), NULL );
+
+    painter.end();
+}
diff --git a/source/third_party/qwt/qwt_interval.cpp b/source/third_party/qwt/qwt_interval.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5652c4db1bd44d8f853d113f1c01249af9164df5
--- /dev/null
+++ b/source/third_party/qwt/qwt_interval.cpp
@@ -0,0 +1,354 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_interval.h"
+#include "qwt/qwt_math.h"
+#include <qalgorithms.h>
+
+/*!
+  \brief Normalize the limits of the interval
+
+  If maxValue() < minValue() the limits will be inverted.
+  \return Normalized interval
+
+  \sa isValid(), inverted()
+*/
+QwtInterval QwtInterval::normalized() const
+{
+    if ( d_minValue > d_maxValue )
+    {
+        return inverted();
+    }
+    if ( d_minValue == d_maxValue && d_borderFlags == ExcludeMinimum )
+    {
+        return inverted();
+    }
+
+    return *this;
+}
+
+/*!
+  Invert the limits of the interval
+  \return Inverted interval
+  \sa normalized()
+*/
+QwtInterval QwtInterval::inverted() const
+{
+    BorderFlags borderFlags = IncludeBorders;
+    if ( d_borderFlags & ExcludeMinimum )
+        borderFlags |= ExcludeMaximum;
+    if ( d_borderFlags & ExcludeMaximum )
+        borderFlags |= ExcludeMinimum;
+
+    return QwtInterval( d_maxValue, d_minValue, borderFlags );
+}
+
+/*!
+  Test if a value is inside an interval
+
+  \param value Value
+  \return true, if value >= minValue() && value <= maxValue()
+*/
+bool QwtInterval::contains( double value ) const
+{
+    if ( !isValid() )
+        return false;
+
+    if ( value < d_minValue || value > d_maxValue )
+        return false;
+
+    if ( value == d_minValue && d_borderFlags & ExcludeMinimum )
+        return false;
+
+    if ( value == d_maxValue && d_borderFlags & ExcludeMaximum )
+        return false;
+
+    return true;
+}
+
+//! Unite 2 intervals
+QwtInterval QwtInterval::unite( const QwtInterval &other ) const
+{
+    /*
+     If one of the intervals is invalid return the other one.
+     If both are invalid return an invalid default interval
+     */
+    if ( !isValid() )
+    {
+        if ( !other.isValid() )
+            return QwtInterval();
+        else
+            return other;
+    }
+    if ( !other.isValid() )
+        return *this;
+
+    QwtInterval united;
+    BorderFlags flags = IncludeBorders;
+
+    // minimum
+    if ( d_minValue < other.minValue() )
+    {
+        united.setMinValue( d_minValue );
+        flags &= d_borderFlags & ExcludeMinimum;
+    }
+    else if ( other.minValue() < d_minValue )
+    {
+        united.setMinValue( other.minValue() );
+        flags &= other.borderFlags() & ExcludeMinimum;
+    }
+    else // d_minValue == other.minValue()
+    {
+        united.setMinValue( d_minValue );
+        flags &= ( d_borderFlags & other.borderFlags() ) & ExcludeMinimum;
+    }
+
+    // maximum
+    if ( d_maxValue > other.maxValue() )
+    {
+        united.setMaxValue( d_maxValue );
+        flags &= d_borderFlags & ExcludeMaximum;
+    }
+    else if ( other.maxValue() > d_maxValue )
+    {
+        united.setMaxValue( other.maxValue() );
+        flags &= other.borderFlags() & ExcludeMaximum;
+    }
+    else // d_maxValue == other.maxValue() )
+    {
+        united.setMaxValue( d_maxValue );
+        flags &= d_borderFlags & other.borderFlags() & ExcludeMaximum;
+    }
+
+    united.setBorderFlags( flags );
+    return united;
+}
+
+/*! 
+  \brief Intersect 2 intervals
+  
+  \param other Interval to be intersect with
+  \return Intersection
+ */
+QwtInterval QwtInterval::intersect( const QwtInterval &other ) const
+{
+    if ( !other.isValid() || !isValid() )
+        return QwtInterval();
+
+    QwtInterval i1 = *this;
+    QwtInterval i2 = other;
+
+    // swap i1/i2, so that the minimum of i1
+    // is smaller then the minimum of i2
+
+    if ( i1.minValue() > i2.minValue() )
+    {
+        qSwap( i1, i2 );
+    }
+    else if ( i1.minValue() == i2.minValue() )
+    {
+        if ( i1.borderFlags() & ExcludeMinimum )
+            qSwap( i1, i2 );
+    }
+
+    if ( i1.maxValue() < i2.minValue() )
+    {
+        return QwtInterval();
+    }
+
+    if ( i1.maxValue() == i2.minValue() )
+    {
+        if ( i1.borderFlags() & ExcludeMaximum ||
+            i2.borderFlags() & ExcludeMinimum )
+        {
+            return QwtInterval();
+        }
+    }
+
+    QwtInterval intersected;
+    BorderFlags flags = IncludeBorders;
+
+    intersected.setMinValue( i2.minValue() );
+    flags |= i2.borderFlags() & ExcludeMinimum;
+
+    if ( i1.maxValue() < i2.maxValue() )
+    {
+        intersected.setMaxValue( i1.maxValue() );
+        flags |= i1.borderFlags() & ExcludeMaximum;
+    }
+    else if ( i2.maxValue() < i1.maxValue() )
+    {
+        intersected.setMaxValue( i2.maxValue() );
+        flags |= i2.borderFlags() & ExcludeMaximum;
+    }
+    else // i1.maxValue() == i2.maxValue()
+    {
+        intersected.setMaxValue( i1.maxValue() );
+        flags |= i1.borderFlags() & i2.borderFlags() & ExcludeMaximum;
+    }
+
+    intersected.setBorderFlags( flags );
+    return intersected;
+}
+
+/*! 
+  \brief Unite this interval with the given interval.
+
+  \param other Interval to be united with
+  \return This interval
+ */
+QwtInterval& QwtInterval::operator|=( const QwtInterval &other )
+{
+    *this = *this | other;
+    return *this;
+}
+
+/*! 
+  \brief Intersect this interval with the given interval.
+
+  \param other Interval to be intersected with
+  \return This interval
+ */
+QwtInterval& QwtInterval::operator&=( const QwtInterval &other )
+{
+    *this = *this & other;
+    return *this;
+}
+
+/*!
+  \brief Test if two intervals overlap
+
+  \param other Interval
+  \return True, when the intervals are intersecting
+*/
+bool QwtInterval::intersects( const QwtInterval &other ) const
+{
+    if ( !isValid() || !other.isValid() )
+        return false;
+
+    QwtInterval i1 = *this;
+    QwtInterval i2 = other;
+
+    // swap i1/i2, so that the minimum of i1
+    // is smaller then the minimum of i2
+
+    if ( i1.minValue() > i2.minValue() )
+    {
+        qSwap( i1, i2 );
+    }
+    else if ( i1.minValue() == i2.minValue() &&
+              i1.borderFlags() & ExcludeMinimum )
+    {
+        qSwap( i1, i2 );
+    }
+
+    if ( i1.maxValue() > i2.minValue() )
+    {
+        return true;
+    }
+    if ( i1.maxValue() == i2.minValue() )
+    {
+        return !( ( i1.borderFlags() & ExcludeMaximum ) ||
+            ( i2.borderFlags() & ExcludeMinimum ) );
+    }
+    return false;
+}
+
+/*!
+  Adjust the limit that is closer to value, so that value becomes
+  the center of the interval.
+
+  \param value Center
+  \return Interval with value as center
+*/
+QwtInterval QwtInterval::symmetrize( double value ) const
+{
+    if ( !isValid() )
+        return *this;
+
+    const double delta =
+        qMax( qAbs( value - d_maxValue ), qAbs( value - d_minValue ) );
+
+    return QwtInterval( value - delta, value + delta );
+}
+
+/*!
+  Limit the interval, keeping the border modes
+
+  \param lowerBound Lower limit
+  \param upperBound Upper limit
+
+  \return Limited interval
+*/
+QwtInterval QwtInterval::limited( double lowerBound, double upperBound ) const
+{
+    if ( !isValid() || lowerBound > upperBound )
+        return QwtInterval();
+
+    double minValue = qMax( d_minValue, lowerBound );
+    minValue = qMin( minValue, upperBound );
+
+    double maxValue = qMax( d_maxValue, lowerBound );
+    maxValue = qMin( maxValue, upperBound );
+
+    return QwtInterval( minValue, maxValue, d_borderFlags );
+}
+
+/*!
+  \brief Extend the interval
+
+  If value is below minValue(), value becomes the lower limit.
+  If value is above maxValue(), value becomes the upper limit.
+
+  extend() has no effect for invalid intervals
+
+  \param value Value
+  \return extended interval
+
+  \sa isValid()
+*/
+QwtInterval QwtInterval::extend( double value ) const
+{
+    if ( !isValid() )
+        return *this;
+
+    return QwtInterval( qMin( value, d_minValue ),
+        qMax( value, d_maxValue ), d_borderFlags );
+}
+
+/*!
+  Extend an interval
+
+  \param value Value
+  \return Reference of the extended interval
+
+  \sa extend()
+*/
+QwtInterval& QwtInterval::operator|=( double value )
+{
+    *this = *this | value;
+    return *this;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtInterval &interval )
+{
+    const int flags = interval.borderFlags();
+
+    debug.nospace() << "QwtInterval("
+        << ( ( flags & QwtInterval::ExcludeMinimum ) ? "]" : "[" )
+        << interval.minValue() << "," << interval.maxValue()
+        << ( ( flags & QwtInterval::ExcludeMaximum ) ? "[" : "]" )
+        << ")";
+
+    return debug.space();
+}
+
+#endif
diff --git a/source/third_party/qwt/qwt_interval_symbol.cpp b/source/third_party/qwt/qwt_interval_symbol.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c7d43349d66b1c15aa8a60d97cfe3277aae3bc2
--- /dev/null
+++ b/source/third_party/qwt/qwt_interval_symbol.cpp
@@ -0,0 +1,319 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_interval_symbol.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_math.h"
+#include <qpainter.h>
+
+#if QT_VERSION < 0x040601
+#define qAtan2(y, x) ::atan2(y, x)
+#define qFastSin(x) qSin(x)
+#define qFastCos(x) qCos(x)
+#endif
+
+class QwtIntervalSymbol::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtIntervalSymbol::NoSymbol ),
+        width( 6 )
+    {
+    }
+
+    bool operator==( const PrivateData &other ) const
+    {
+        return ( style == other.style )
+            && ( width == other.width )
+            && ( brush == other.brush )
+            && ( pen == other.pen );
+    }
+
+    QwtIntervalSymbol::Style style;
+    int width;
+
+    QPen pen;
+    QBrush brush;
+};
+
+/*!
+  Constructor
+
+  \param style Style of the symbol
+  \sa setStyle(), style(), Style
+*/
+QwtIntervalSymbol::QwtIntervalSymbol( Style style )
+{
+    d_data = new PrivateData();
+    d_data->style = style;
+}
+
+//! Copy constructor
+QwtIntervalSymbol::QwtIntervalSymbol( const QwtIntervalSymbol &other )
+{
+    d_data = new PrivateData();
+    *d_data = *other.d_data;
+}
+
+//! Destructor
+QwtIntervalSymbol::~QwtIntervalSymbol()
+{
+    delete d_data;
+}
+
+//! \brief Assignment operator
+QwtIntervalSymbol &QwtIntervalSymbol::operator=( 
+    const QwtIntervalSymbol &other )
+{
+    *d_data = *other.d_data;
+    return *this;
+}
+
+//! \brief Compare two symbols
+bool QwtIntervalSymbol::operator==( 
+    const QwtIntervalSymbol &other ) const
+{
+    return *d_data == *other.d_data;
+}
+
+//! \brief Compare two symbols
+bool QwtIntervalSymbol::operator!=( 
+    const QwtIntervalSymbol &other ) const
+{
+    return !( *d_data == *other.d_data );
+}
+
+/*!
+  Specify the symbol style
+
+  \param style Style
+  \sa style(), Style
+*/
+void QwtIntervalSymbol::setStyle( Style style )
+{
+    d_data->style = style;
+}
+
+/*!
+  \return Current symbol style
+  \sa setStyle()
+*/
+QwtIntervalSymbol::Style QwtIntervalSymbol::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  Specify the width of the symbol
+  It is used depending on the style.
+
+  \param width Width
+  \sa width(), setStyle()
+*/
+void QwtIntervalSymbol::setWidth( int width )
+{
+    d_data->width = width;
+}
+
+/*!
+  \return Width of the symbol.
+  \sa setWidth(), setStyle()
+*/
+int QwtIntervalSymbol::width() const
+{
+    return d_data->width;
+}
+
+/*!
+  \brief Assign a brush
+
+  The brush is used for the Box style.
+
+  \param brush Brush
+  \sa brush()
+*/
+void QwtIntervalSymbol::setBrush( const QBrush &brush )
+{
+    d_data->brush = brush;
+}
+
+/*!
+  \return Brush
+  \sa setBrush()
+*/
+const QBrush& QwtIntervalSymbol::brush() const
+{
+    return d_data->brush;
+}
+
+/*! 
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtIntervalSymbol::setPen( const QColor &color, 
+    qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen
+
+  \param pen Pen
+  \sa pen(), setBrush()
+*/
+void QwtIntervalSymbol::setPen( const QPen &pen )
+{
+    d_data->pen = pen;
+}
+
+/*!
+  \return Pen
+  \sa setPen(), brush()
+*/
+const QPen& QwtIntervalSymbol::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  Draw a symbol depending on its style
+
+  \param painter Painter
+  \param orientation Orientation
+  \param from Start point of the interval in target device coordinates
+  \param to End point of the interval in target device coordinates
+
+  \sa setStyle()
+*/
+void QwtIntervalSymbol::draw( QPainter *painter, Qt::Orientation orientation,
+    const QPointF &from, const QPointF &to ) const
+{
+    const qreal pw = qMax( painter->pen().widthF(), qreal( 1.0 ) );
+
+    QPointF p1 = from;
+    QPointF p2 = to;
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        p1 = p1.toPoint();
+        p2 = p2.toPoint();
+    }
+
+    switch ( d_data->style )
+    {
+        case QwtIntervalSymbol::Bar:
+        {
+            QwtPainter::drawLine( painter, p1, p2 );
+            if ( d_data->width > pw )
+            {
+                if ( ( orientation == Qt::Horizontal ) 
+                    && ( p1.y() == p2.y() ) )
+                {
+                    const double sw = d_data->width;
+
+                    const double y = p1.y() - sw / 2;
+                    QwtPainter::drawLine( painter,
+                        p1.x(), y, p1.x(), y + sw );
+                    QwtPainter::drawLine( painter,
+                        p2.x(), y, p2.x(), y + sw );
+                }
+                else if ( ( orientation == Qt::Vertical ) 
+                    && ( p1.x() == p2.x() ) )
+                {
+                    const double sw = d_data->width;
+
+                    const double x = p1.x() - sw / 2;
+                    QwtPainter::drawLine( painter,
+                        x, p1.y(), x + sw, p1.y() );
+                    QwtPainter::drawLine( painter,
+                        x, p2.y(), x + sw, p2.y() );
+                }
+                else
+                {
+                    const double sw = d_data->width;
+
+                    const double dx = p2.x() - p1.x();
+                    const double dy = p2.y() - p1.y();
+                    const double angle = qAtan2( dy, dx ) + M_PI_2;
+                    double dw2 = sw / 2.0;
+
+                    const double cx = qFastCos( angle ) * dw2;
+                    const double sy = qFastSin( angle ) * dw2;
+
+                    QwtPainter::drawLine( painter,
+                        p1.x() - cx, p1.y() - sy,
+                        p1.x() + cx, p1.y() + sy );
+                    QwtPainter::drawLine( painter,
+                        p2.x() - cx, p2.y() - sy,
+                        p2.x() + cx, p2.y() + sy );
+                }
+            }
+            break;
+        }
+        case QwtIntervalSymbol::Box:
+        {
+            if ( d_data->width <= pw )
+            {
+                QwtPainter::drawLine( painter, p1, p2 );
+            }
+            else
+            {
+                if ( ( orientation == Qt::Horizontal ) 
+                    && ( p1.y() == p2.y() ) )
+                {
+                    const double sw = d_data->width;
+
+                    const double y = p1.y() - d_data->width / 2;
+                    QwtPainter::drawRect( painter,
+                        p1.x(), y, p2.x() - p1.x(),  sw );
+                }
+                else if ( ( orientation == Qt::Vertical )
+                    && ( p1.x() == p2.x() ) )
+                {
+                    const double sw = d_data->width;
+
+                    const double x = p1.x() - d_data->width / 2;
+                    QwtPainter::drawRect( painter,
+                        x, p1.y(), sw, p2.y() - p1.y() );
+                }
+                else
+                {
+                    const double sw = d_data->width;
+
+                    const double dx = p2.x() - p1.x();
+                    const double dy = p2.y() - p1.y();
+                    const double angle = qAtan2( dy, dx ) + M_PI_2;
+                    double dw2 = sw / 2.0;
+
+                    const double cx = qFastCos( angle ) * dw2;
+                    const double sy = qFastSin( angle ) * dw2;
+
+                    QPolygonF polygon;
+                    polygon += QPointF( p1.x() - cx, p1.y() - sy );
+                    polygon += QPointF( p1.x() + cx, p1.y() + sy );
+                    polygon += QPointF( p2.x() + cx, p2.y() + sy );
+                    polygon += QPointF( p2.x() - cx, p2.y() - sy );
+
+                    QwtPainter::drawPolygon( painter, polygon );
+                }
+            }
+            break;
+        }
+        default:;
+    }
+}
diff --git a/source/third_party/qwt/qwt_knob.cpp b/source/third_party/qwt/qwt_knob.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ded9e2890ed0fd1efbd3867ad05be5db2133c49d
--- /dev/null
+++ b/source/third_party/qwt/qwt_knob.cpp
@@ -0,0 +1,855 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_knob.h"
+#include "qwt/qwt_round_scale_draw.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_map.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qevent.h>
+#include <qmath.h>
+#include <qapplication.h>
+
+#if QT_VERSION < 0x040601
+#define qAtan2(y, x) ::atan2(y, x)
+#define qFabs(x) ::fabs(x)
+#define qFastCos(x) qCos(x)
+#define qFastSin(x) qSin(x)
+#endif
+
+static QSize qwtKnobSizeHint( const QwtKnob *knob, int min )
+{
+    int knobWidth = knob->knobWidth();
+    if ( knobWidth <= 0 )
+        knobWidth = qMax( 3 * knob->markerSize(), min );
+
+    // Add the scale radial thickness to the knobWidth
+    const int extent = qCeil( knob->scaleDraw()->extent( knob->font() ) );
+    const int d = 2 * ( extent + 4 ) + knobWidth;
+
+    int left, right, top, bottom;
+    knob->getContentsMargins( &left, &top, &right, &bottom );
+
+    return QSize( d + left + right, d + top + bottom );
+}
+
+static inline double qwtToScaleAngle( double angle )
+{
+    // the map is counter clockwise with the origin
+    // at 90° using angles from -180° -> 180°
+
+    double a = 90.0 - angle;
+    if ( a <= -180.0 )
+        a += 360.0;
+    else if ( a >= 180.0 )
+        a -= 360.0;
+
+    return a;
+}
+
+static double qwtToDegrees( double value )
+{
+    return qwtNormalizeDegrees( 90.0 - value );
+}
+
+class QwtKnob::PrivateData
+{
+public:
+    PrivateData():
+        knobStyle( QwtKnob::Raised ),
+        markerStyle( QwtKnob::Notch ),
+        borderWidth( 2 ),
+        borderDist( 4 ),
+        scaleDist( 4 ),
+        maxScaleTicks( 11 ),
+        knobWidth( 0 ),
+        alignment( Qt::AlignCenter ),
+        markerSize( 8 ),
+        totalAngle( 270.0 ),
+        mouseOffset( 0.0 )
+    {
+    }
+
+    QwtKnob::KnobStyle knobStyle;
+    QwtKnob::MarkerStyle markerStyle;
+
+    int borderWidth;
+    int borderDist;
+    int scaleDist;
+    int maxScaleTicks;
+    int knobWidth;
+    Qt::Alignment alignment;
+    int markerSize;
+
+    double totalAngle;
+
+    double mouseOffset;
+};
+
+/*!
+  \brief Constructor
+
+  Construct a knob with an angle of 270°. The style is
+  QwtKnob::Raised and the marker style is QwtKnob::Notch.
+  The width of the knob is set to 50 pixels.
+
+  \param parent Parent widget
+
+  \sa setTotalAngle()
+*/
+QwtKnob::QwtKnob( QWidget* parent ):
+    QwtAbstractSlider( parent )
+{
+    d_data = new PrivateData;
+
+    setScaleDraw( new QwtRoundScaleDraw() );
+
+    setTotalAngle( 270.0 );
+
+    setScale( 0.0, 10.0 );
+    setValue( 0.0 );
+
+    setSizePolicy( QSizePolicy::MinimumExpanding, 
+        QSizePolicy::MinimumExpanding );
+}
+
+//! Destructor
+QwtKnob::~QwtKnob()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Set the knob type 
+
+  \param knobStyle Knob type
+  \sa knobStyle(), setBorderWidth()
+*/
+void QwtKnob::setKnobStyle( KnobStyle knobStyle )
+{
+    if ( d_data->knobStyle != knobStyle )
+    {
+        d_data->knobStyle = knobStyle;
+        update();
+    }
+}
+
+/*!
+    \return Marker type of the knob
+    \sa setKnobStyle(), setBorderWidth()
+*/
+QwtKnob::KnobStyle QwtKnob::knobStyle() const
+{
+    return d_data->knobStyle;
+}
+
+/*!
+  \brief Set the marker type of the knob
+
+  \param markerStyle Marker type
+  \sa markerStyle(), setMarkerSize()
+*/
+void QwtKnob::setMarkerStyle( MarkerStyle markerStyle )
+{
+    if ( d_data->markerStyle != markerStyle )
+    {
+        d_data->markerStyle = markerStyle;
+        update();
+    }
+}
+
+/*!
+  \return Marker type of the knob
+  \sa setMarkerStyle(), setMarkerSize()
+*/
+QwtKnob::MarkerStyle QwtKnob::markerStyle() const
+{
+    return d_data->markerStyle;
+}
+
+/*!
+  \brief Set the total angle by which the knob can be turned
+  \param angle Angle in degrees.
+
+  The angle has to be between [10, 360] degrees. Angles above
+  360 ( so that the knob can be turned several times around its axis )
+  have to be set using setNumTurns().
+
+  The default angle is 270 degrees. 
+
+  \sa totalAngle(), setNumTurns()
+*/
+void QwtKnob::setTotalAngle ( double angle )
+{
+    angle = qBound( 10.0, angle, 360.0 );
+
+    if ( angle != d_data->totalAngle )
+    {
+        d_data->totalAngle = angle;
+
+        scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
+            0.5 * d_data->totalAngle );
+
+        updateGeometry();
+        update();
+    }
+}
+
+/*! 
+  \return the total angle
+  \sa setTotalAngle(), setNumTurns(), numTurns()
+ */
+double QwtKnob::totalAngle() const
+{
+    return d_data->totalAngle;
+}
+
+/*!
+  \brief Set the number of turns
+
+  When numTurns > 1 the knob can be turned several times around its axis
+  - otherwise the total angle is floored to 360°.
+
+  \sa numTurns(), totalAngle(), setTotalAngle()
+*/
+  
+void QwtKnob::setNumTurns( int numTurns )
+{
+    numTurns = qMax( numTurns, 1 );
+
+    if ( numTurns == 1 && d_data->totalAngle <= 360.0 )
+        return;
+
+    const double angle = numTurns * 360.0;
+    if ( angle != d_data->totalAngle )
+    {
+        d_data->totalAngle = angle;
+
+        scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
+            0.5 * d_data->totalAngle );
+
+        updateGeometry();
+        update();
+    }
+}
+
+/*!
+  \return Number of turns. 
+
+  When the total angle is below 360° numTurns() is ceiled to 1.
+  \sa setNumTurns(), setTotalAngle(), totalAngle()
+ */
+int QwtKnob::numTurns() const
+{
+    return qCeil( d_data->totalAngle / 360.0 );
+}
+
+/*!
+   Change the scale draw of the knob
+
+   For changing the labels of the scales, it
+   is necessary to derive from QwtRoundScaleDraw and
+   overload QwtRoundScaleDraw::label().
+
+   \sa scaleDraw()
+*/
+void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
+{
+    setAbstractScaleDraw( scaleDraw );
+    setTotalAngle( d_data->totalAngle );
+
+    updateGeometry();
+    update();
+}
+
+/*!
+   \return the scale draw of the knob
+   \sa setScaleDraw()
+*/
+const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
+{
+    return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+   \return the scale draw of the knob
+   \sa setScaleDraw()
+*/
+QwtRoundScaleDraw *QwtKnob::scaleDraw()
+{
+    return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+  Calculate the bounding rectangle of the knob without the scale
+
+  \return Bounding rectangle of the knob
+  \sa knobWidth(), alignment(), QWidget::contentsRect()
+ */
+QRect QwtKnob::knobRect() const
+{
+    const QRect cr = contentsRect();
+
+    const int extent = qCeil( scaleDraw()->extent( font() ) );
+    const int d = extent + d_data->scaleDist;
+
+    int w = d_data->knobWidth;
+    if ( w <= 0 )
+    {
+        const int dim = qMin( cr.width(), cr.height() );
+
+        w = dim - 2 * ( d );
+        w = qMax( 0, w );
+    }
+
+    QRect r( 0, 0, w, w );
+
+    if ( d_data->alignment & Qt::AlignLeft )
+    {
+        r.moveLeft( cr.left() + d );
+    }
+    else if ( d_data->alignment & Qt::AlignRight )
+    {
+        r.moveRight( cr.right() - d );
+    }
+    else
+    {
+        r.moveCenter( QPoint( cr.center().x(), r.center().y() ) );
+    }
+
+    if ( d_data->alignment & Qt::AlignTop )
+    {
+        r.moveTop( cr.top() + d );
+    }
+    else if ( d_data->alignment & Qt::AlignBottom )
+    {
+        r.moveBottom( cr.bottom() - d );
+    }
+    else 
+    {
+        r.moveCenter( QPoint( r.center().x(), cr.center().y() ) );
+    }
+
+    return r;
+}
+
+/*!
+  \brief Determine what to do when the user presses a mouse button.
+
+  \param pos Mouse position
+
+  \retval True, when pos is inside the circle of the knob.
+  \sa scrolledTo()
+*/
+bool QwtKnob::isScrollPosition( const QPoint &pos ) const
+{
+    const QRect kr = knobRect();
+
+    const QRegion region( kr, QRegion::Ellipse );
+    if ( region.contains( pos ) && ( pos != kr.center() ) )
+    {
+        const double angle = QLineF( kr.center(), pos ).angle();
+        const double valueAngle = qwtToDegrees( scaleMap().transform( value() ) );
+
+        d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
+
+        return true;
+    }
+
+    return false;
+}
+
+/*!
+  \brief Determine the value for a new position of the mouse
+
+  \param pos Mouse position
+
+  \return Value for the mouse position
+  \sa isScrollPosition()
+*/
+double QwtKnob::scrolledTo( const QPoint &pos ) const
+{
+    double angle = QLineF( rect().center(), pos ).angle();
+    angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
+
+    if ( scaleMap().pDist() > 360.0 )
+    {
+        angle = qwtToDegrees( angle );
+
+        const double v = scaleMap().transform( value() );
+
+        int numTurns = qFloor( ( v - scaleMap().p1() ) / 360.0 );
+
+        double valueAngle = qwtNormalizeDegrees( v );
+        if ( qAbs( valueAngle - angle ) > 180.0 )
+        {
+            numTurns += ( angle > valueAngle ) ? -1 : 1;
+        }
+
+        angle += scaleMap().p1() + numTurns * 360.0;
+
+        if ( !wrapping() )
+        {
+            const double boundedAngle = 
+                qBound( scaleMap().p1(), angle, scaleMap().p2() );
+
+            d_data->mouseOffset += ( boundedAngle - angle );
+            angle = boundedAngle;
+        }
+    }
+    else
+    {
+        angle = qwtToScaleAngle( angle );
+
+        double boundedAngle = qBound( scaleMap().p1(), angle, scaleMap().p2() );
+
+        if ( !wrapping() )
+        {
+            const double currentAngle = scaleMap().transform( value() );
+
+            if ( ( currentAngle > 90.0 ) && ( boundedAngle < -90.0 ) )
+                boundedAngle = scaleMap().p2();
+            else if ( ( currentAngle < -90.0 ) && ( boundedAngle > 90.0 ) )
+                boundedAngle = scaleMap().p1();
+
+            d_data->mouseOffset += ( boundedAngle - angle );
+        }
+
+        angle = boundedAngle;
+    }
+
+    return scaleMap().invTransform( angle );
+}
+
+/*! 
+  Handle QEvent::StyleChange and QEvent::FontChange;
+  \param event Change event
+*/
+void QwtKnob::changeEvent( QEvent *event )
+{
+    switch( event->type() )
+    {
+        case QEvent::StyleChange:
+        case QEvent::FontChange:
+        {
+            updateGeometry();
+            update();
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*!
+  Repaint the knob
+  \param event Paint event
+*/
+void QwtKnob::paintEvent( QPaintEvent *event )
+{
+    const QRectF knobRect = this->knobRect();
+
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    painter.setRenderHint( QPainter::Antialiasing, true );
+
+    if ( !knobRect.contains( event->region().boundingRect() ) )
+    {
+        scaleDraw()->setRadius( 0.5 * knobRect.width() + d_data->scaleDist );
+        scaleDraw()->moveCenter( knobRect.center() );
+
+        scaleDraw()->draw( &painter, palette() );
+    }
+
+    drawKnob( &painter, knobRect );
+
+    drawMarker( &painter, knobRect, 
+        qwtNormalizeDegrees( scaleMap().transform( value() ) ) );
+
+    painter.setRenderHint( QPainter::Antialiasing, false );
+
+    if ( hasFocus() )
+        drawFocusIndicator( &painter );
+}
+
+/*!
+  \brief Draw the knob
+
+  \param painter painter
+  \param knobRect Bounding rectangle of the knob (without scale)
+*/
+void QwtKnob::drawKnob( QPainter *painter, const QRectF &knobRect ) const
+{
+    double dim = qMin( knobRect.width(), knobRect.height() );
+    dim -= d_data->borderWidth * 0.5;
+
+    QRectF aRect( 0, 0, dim, dim );
+    aRect.moveCenter( knobRect.center() );
+
+    QPen pen( Qt::NoPen );
+    if ( d_data->borderWidth > 0 )
+    {
+        QColor c1 = palette().color( QPalette::Light );
+        QColor c2 = palette().color( QPalette::Dark );
+
+        QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() );
+        gradient.setColorAt( 0.0, c1 );
+        gradient.setColorAt( 0.3, c1 );
+        gradient.setColorAt( 0.7, c2 );
+        gradient.setColorAt( 1.0, c2 );
+
+        pen = QPen( gradient, d_data->borderWidth ); 
+    }
+
+    QBrush brush;
+    switch( d_data->knobStyle )
+    {
+        case QwtKnob::Raised:
+        {
+            double off = 0.3 * knobRect.width();
+            QRadialGradient gradient( knobRect.center(),
+                knobRect.width(), knobRect.topLeft() + QPointF( off, off ) );
+            
+            gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) );
+            gradient.setColorAt( 1.0, palette().color( QPalette::Button ) );
+
+            brush = QBrush( gradient );
+
+            break;
+        }
+        case QwtKnob::Styled:
+        {
+            QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3,
+                knobRect.center().y() - knobRect.height() / 2,
+                knobRect.width() * 1.3,
+                knobRect.center().x(),
+                knobRect.center().y() - knobRect.height() / 2);
+
+            const QColor c = palette().color( QPalette::Button );
+            gradient.setColorAt(0, c.lighter(110));
+            gradient.setColorAt(qreal(0.5), c);
+            gradient.setColorAt(qreal(0.501), c.darker(102));
+            gradient.setColorAt(1, c.darker(115));
+
+            brush = QBrush( gradient );
+
+            break;
+        }
+        case QwtKnob::Sunken:
+        {
+            QLinearGradient gradient( 
+                knobRect.topLeft(), knobRect.bottomRight() );
+            gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) );
+            gradient.setColorAt( 0.5, palette().color( QPalette::Button ) );
+            gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) );
+            brush = QBrush( gradient );
+
+            break;
+        }
+        case QwtKnob::Flat:
+        default:
+            brush = palette().brush( QPalette::Button );
+    }
+
+    painter->setPen( pen );
+    painter->setBrush( brush );
+    painter->drawEllipse( aRect );
+}
+
+
+/*!
+  \brief Draw the marker at the knob's front
+
+  \param painter Painter
+  \param rect Bounding rectangle of the knob without scale
+  \param angle Angle of the marker in degrees 
+               ( clockwise, 0 at the 12 o'clock position )
+*/
+void QwtKnob::drawMarker( QPainter *painter, 
+    const QRectF &rect, double angle ) const
+{
+    if ( d_data->markerStyle == NoMarker || !isValid() )
+        return;
+
+    const double radians = qwtRadians( angle );
+    const double sinA = -qFastSin( radians );
+    const double cosA = qFastCos( radians );
+
+    const double xm = rect.center().x();
+    const double ym = rect.center().y();
+    const double margin = 4.0;
+
+    double radius = 0.5 * ( rect.width() - d_data->borderWidth ) - margin;
+    if ( radius < 1.0 )
+        radius = 1.0;
+
+    int markerSize = d_data->markerSize;
+    if ( markerSize <= 0 )
+        markerSize = qRound( 0.4 * radius );
+
+    switch ( d_data->markerStyle )
+    {
+        case Notch:
+        case Nub:
+        {
+            const double dotWidth = 
+                qMin( double( markerSize ), radius);
+
+            const double dotCenterDist = radius - 0.5 * dotWidth;
+            if ( dotCenterDist > 0.0 )
+            {
+                const QPointF center( xm - sinA * dotCenterDist, 
+                    ym - cosA * dotCenterDist );
+
+                QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
+                ellipse.moveCenter( center );
+
+                QColor c1 = palette().color( QPalette::Light );
+                QColor c2 = palette().color( QPalette::Mid );
+
+                if ( d_data->markerStyle == Notch )
+                    qSwap( c1, c2 );
+
+                QLinearGradient gradient( 
+                    ellipse.topLeft(), ellipse.bottomRight() );
+                gradient.setColorAt( 0.0, c1 );
+                gradient.setColorAt( 1.0, c2 );
+
+                painter->setPen( Qt::NoPen );
+                painter->setBrush( gradient );
+
+                painter->drawEllipse( ellipse );
+            }
+            break;
+        }
+        case Dot:
+        {
+            const double dotWidth = 
+                qMin( double( markerSize ), radius);
+
+            const double dotCenterDist = radius - 0.5 * dotWidth;
+            if ( dotCenterDist > 0.0 )
+            {
+                const QPointF center( xm - sinA * dotCenterDist, 
+                    ym - cosA * dotCenterDist );
+
+                QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
+                ellipse.moveCenter( center );
+
+                painter->setPen( Qt::NoPen );
+                painter->setBrush( palette().color( QPalette::ButtonText ) );
+                painter->drawEllipse( ellipse );
+            }
+
+            break;
+        }
+        case Tick:
+        {
+            const double rb = qMax( radius - markerSize, 1.0 );
+            const double re = radius;
+
+            const QLineF line( xm - sinA * rb, ym - cosA * rb,
+                xm - sinA * re, ym - cosA * re );
+
+            QPen pen( palette().color( QPalette::ButtonText ), 0 );
+            pen.setCapStyle( Qt::FlatCap );
+            painter->setPen( pen );
+            painter->drawLine ( line );
+
+            break;
+        }
+        case Triangle:
+        {
+            const double rb = qMax( radius - markerSize, 1.0 );
+            const double re = radius;
+
+            painter->translate( rect.center() );
+            painter->rotate( angle - 90.0 );
+            
+            QPolygonF polygon;
+            polygon += QPointF( re, 0.0 );
+            polygon += QPointF( rb, 0.5 * ( re - rb ) );
+            polygon += QPointF( rb, -0.5 * ( re - rb ) );
+
+            painter->setPen( Qt::NoPen );
+            painter->setBrush( palette().color( QPalette::ButtonText ) );
+            painter->drawPolygon( polygon );
+
+            painter->resetTransform();
+
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*!
+  Draw the focus indicator
+  \param painter Painter
+*/
+void QwtKnob::drawFocusIndicator( QPainter *painter ) const
+{       
+    const QRect cr = contentsRect();
+
+    int w = d_data->knobWidth;
+    if ( w <= 0 )
+    {
+        w = qMin( cr.width(), cr.height() );
+    }
+    else
+    {
+        const int extent = qCeil( scaleDraw()->extent( font() ) );
+        w += 2 * ( extent + d_data->scaleDist );
+    }
+
+    QRect focusRect( 0, 0, w, w );
+    focusRect.moveCenter( cr.center() );
+
+    QwtPainter::drawFocusRect( painter, this, focusRect );
+}  
+
+/*!
+  \brief Set the alignment of the knob
+
+  Similar to a QLabel::alignment() the flags decide how
+  to align the knob inside of contentsRect(). 
+
+  The default setting is Qt::AlignCenter
+
+  \param alignment Or'd alignment flags
+
+  \sa alignment(), setKnobWidth(), knobRect()
+ */
+void QwtKnob::setAlignment( Qt::Alignment alignment )
+{
+    if ( d_data->alignment != alignment )
+    {
+        d_data->alignment = alignment;
+        update();
+    }
+}
+
+/*!
+  \return Alignment of the knob inside of contentsRect()
+  \sa setAlignment(), knobWidth(), knobRect()
+ */
+Qt::Alignment QwtKnob::alignment() const
+{
+    return d_data->alignment;
+}
+
+/*!
+  \brief Change the knob's width.
+
+  Setting a fixed value for the diameter of the knob 
+  is helpful for aligning several knobs in a row.
+
+  \param width New width
+
+  \sa knobWidth(), setAlignment()
+  \note Modifies the sizePolicy() 
+*/
+void QwtKnob::setKnobWidth( int width )
+{
+    width = qMax( width, 0 );
+
+    if ( width != d_data->knobWidth )
+    {
+        QSizePolicy::Policy policy;
+        if ( width > 0 )
+            policy = QSizePolicy::Minimum;
+        else
+            policy = QSizePolicy::MinimumExpanding;
+
+        setSizePolicy( policy, policy );
+
+        d_data->knobWidth = width;
+
+        updateGeometry();
+        update();
+    }
+}
+
+//! Return the width of the knob
+int QwtKnob::knobWidth() const
+{
+    return d_data->knobWidth;
+}
+
+/*!
+  \brief Set the knob's border width
+  \param borderWidth new border width
+*/
+void QwtKnob::setBorderWidth( int borderWidth )
+{
+    d_data->borderWidth = qMax( borderWidth, 0 );
+
+    updateGeometry();
+    update();
+}
+
+//! Return the border width
+int QwtKnob::borderWidth() const
+{
+    return d_data->borderWidth;
+}
+
+/*!
+  \brief Set the size of the marker
+
+  When setting a size <= 0 the marker will
+  automatically scaled to 40% of the radius of the knob.
+
+  \sa markerSize(), markerStyle()
+*/
+void QwtKnob::setMarkerSize( int size )
+{
+    if ( d_data->markerSize != size )
+    {
+        d_data->markerSize = size;
+        update();
+    }
+}
+
+/*! 
+  \return Marker size
+  \sa setMarkerSize()
+ */
+int QwtKnob::markerSize() const
+{
+    return d_data->markerSize;
+}
+
+/*!
+  \return sizeHint()
+*/
+QSize QwtKnob::sizeHint() const
+{
+    const QSize hint = qwtKnobSizeHint( this, 50 );
+    return hint.expandedTo( QApplication::globalStrut() );
+}
+
+/*!
+  \return Minimum size hint
+  \sa sizeHint()
+*/
+QSize QwtKnob::minimumSizeHint() const
+{
+    return qwtKnobSizeHint( this, 20 );
+}
diff --git a/source/third_party/qwt/qwt_legend.cpp b/source/third_party/qwt/qwt_legend.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..df13195f0eff82b8b9b0c5f31b73588c4feef7c2
--- /dev/null
+++ b/source/third_party/qwt/qwt_legend.cpp
@@ -0,0 +1,811 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_legend.h"
+#include "qwt/qwt_legend_label.h"
+#include "qwt/qwt_dyngrid_layout.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_painter.h"
+#include <qapplication.h>
+#include <qscrollbar.h>
+#include <qscrollarea.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+
+class QwtLegendMap
+{
+public:
+    inline bool isEmpty() const { return d_entries.isEmpty(); }
+
+    void insert( const QVariant &, const QList<QWidget *> & );
+    void remove( const QVariant & );
+
+    void removeWidget( const QWidget * );
+
+    QList<QWidget *> legendWidgets( const QVariant & ) const;
+    QVariant itemInfo( const QWidget * ) const;
+
+private:
+    // we don't know anything about itemInfo and therefore don't have
+    // any key that can be used for a map or hashtab.
+    // But a simple linear list is o.k. here, as we will never have
+    // more than a few entries.
+
+    class Entry
+    {
+    public:
+        QVariant itemInfo;
+        QList<QWidget *> widgets;
+    };
+
+    QList< Entry > d_entries;
+};
+
+void QwtLegendMap::insert( const QVariant &itemInfo, 
+    const QList<QWidget *> &widgets )
+{
+    for ( int i = 0; i < d_entries.size(); i++ )
+    {
+        Entry &entry = d_entries[i];
+        if ( entry.itemInfo == itemInfo )
+        {
+            entry.widgets = widgets;
+            return;
+        }
+    }
+
+    Entry newEntry;
+    newEntry.itemInfo = itemInfo;
+    newEntry.widgets = widgets;
+
+    d_entries += newEntry;
+}
+
+void QwtLegendMap::remove( const QVariant &itemInfo )
+{
+    for ( int i = 0; i < d_entries.size(); i++ )
+    {
+        Entry &entry = d_entries[i];
+        if ( entry.itemInfo == itemInfo )
+        {
+            d_entries.removeAt( i );
+            return;
+        }
+    }
+}
+
+void QwtLegendMap::removeWidget( const QWidget *widget )
+{
+    QWidget *w = const_cast<QWidget *>( widget );
+
+    for ( int i = 0; i < d_entries.size(); i++ )
+        d_entries[ i ].widgets.removeAll( w );
+}
+
+QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const
+{
+    if ( widget != NULL )
+    {
+        QWidget *w = const_cast<QWidget *>( widget );
+
+        for ( int i = 0; i < d_entries.size(); i++ )
+        {
+            const Entry &entry = d_entries[i];
+            if ( entry.widgets.indexOf( w ) >= 0 )
+                return entry.itemInfo;
+        }
+    }
+
+    return QVariant();
+}
+
+QList<QWidget *> QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const
+{
+    if ( itemInfo.isValid() )
+    {
+        for ( int i = 0; i < d_entries.size(); i++ )
+        {
+            const Entry &entry = d_entries[i];
+            if ( entry.itemInfo == itemInfo )
+                return entry.widgets;
+        }
+    }
+
+    return QList<QWidget *>();
+}
+
+class QwtLegend::PrivateData
+{
+public:
+    PrivateData():
+        itemMode( QwtLegendData::ReadOnly ),
+        view( NULL )
+    {
+    }
+
+    QwtLegendData::Mode itemMode;
+    QwtLegendMap itemMap;
+
+    class LegendView;
+    LegendView *view;
+};
+
+class QwtLegend::PrivateData::LegendView: public QScrollArea
+{
+public:
+    LegendView( QWidget *parent ):
+        QScrollArea( parent )
+    {
+        contentsWidget = new QWidget( this );
+        contentsWidget->setObjectName( "QwtLegendViewContents" );
+
+        setWidget( contentsWidget );
+        setWidgetResizable( false );
+
+        viewport()->setObjectName( "QwtLegendViewport" );
+
+        // QScrollArea::setWidget internally sets autoFillBackground to true
+        // But we don't want a background.
+        contentsWidget->setAutoFillBackground( false );
+        viewport()->setAutoFillBackground( false );
+    }
+
+    virtual bool event( QEvent *event )
+    {
+        if ( event->type() == QEvent::PolishRequest )
+        {
+            setFocusPolicy( Qt::NoFocus );
+        }
+
+        if ( event->type() == QEvent::Resize )
+        {
+            // adjust the size to en/disable the scrollbars
+            // before QScrollArea adjusts the viewport size
+
+            const QRect cr = contentsRect();
+
+            int w = cr.width();
+            int h = contentsWidget->heightForWidth( cr.width() );
+            if ( h > w )
+            {
+                w -= verticalScrollBar()->sizeHint().width();
+                h = contentsWidget->heightForWidth( w );
+            }
+
+            contentsWidget->resize( w, h );
+        }
+
+        return QScrollArea::event( event );
+    }
+
+    virtual bool viewportEvent( QEvent *event )
+    {
+        bool ok = QScrollArea::viewportEvent( event );
+
+        if ( event->type() == QEvent::Resize )
+        {
+            layoutContents();
+        }
+        return ok;
+    }
+
+    QSize viewportSize( int w, int h ) const
+    {
+        const int sbHeight = horizontalScrollBar()->sizeHint().height();
+        const int sbWidth = verticalScrollBar()->sizeHint().width();
+
+        const int cw = contentsRect().width();
+        const int ch = contentsRect().height();
+
+        int vw = cw;
+        int vh = ch;
+
+        if ( w > vw )
+            vh -= sbHeight;
+
+        if ( h > vh )
+        {
+            vw -= sbWidth;
+            if ( w > vw && vh == ch )
+                vh -= sbHeight;
+        }
+        return QSize( vw, vh );
+    }
+
+    void layoutContents()
+    {
+        const QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
+            contentsWidget->layout() );
+        if ( tl == NULL )
+            return;
+
+        const QSize visibleSize = viewport()->contentsRect().size();
+
+        const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin();
+
+        int w = qMax( visibleSize.width(), minW );
+        int h = qMax( tl->heightForWidth( w ), visibleSize.height() );
+
+        const int vpWidth = viewportSize( w, h ).width();
+        if ( w > vpWidth )
+        {
+            w = qMax( vpWidth, minW );
+            h = qMax( tl->heightForWidth( w ), visibleSize.height() );
+        }
+
+        contentsWidget->resize( w, h );
+    }
+
+    QWidget *contentsWidget;
+};
+
+/*!
+  Constructor
+  \param parent Parent widget
+*/
+QwtLegend::QwtLegend( QWidget *parent ):
+    QwtAbstractLegend( parent )
+{
+    setFrameStyle( NoFrame );
+
+    d_data = new QwtLegend::PrivateData;
+
+    d_data->view = new QwtLegend::PrivateData::LegendView( this );
+    d_data->view->setObjectName( "QwtLegendView" );
+    d_data->view->setFrameStyle( NoFrame );
+
+    QwtDynGridLayout *gridLayout = new QwtDynGridLayout(
+        d_data->view->contentsWidget );
+    gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop );
+
+    d_data->view->contentsWidget->installEventFilter( this );
+
+    QVBoxLayout *layout = new QVBoxLayout( this );
+    layout->setContentsMargins( 0, 0, 0, 0 );
+    layout->addWidget( d_data->view );
+}
+
+//! Destructor
+QwtLegend::~QwtLegend()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Set the maximum number of entries in a row
+
+  F.e when the maximum is set to 1 all items are aligned
+  vertically. 0 means unlimited
+
+  \param numColums Maximum number of entries in a row
+
+  \sa maxColumns(), QwtDynGridLayout::setMaxColumns()
+ */
+void QwtLegend::setMaxColumns( uint numColums )
+{
+    QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
+        d_data->view->contentsWidget->layout() );
+    if ( tl )
+        tl->setMaxColumns( numColums );
+}
+
+/*!
+  \return Maximum number of entries in a row
+  \sa setMaxColumns(), QwtDynGridLayout::maxColumns()
+ */
+uint QwtLegend::maxColumns() const
+{
+    uint maxCols = 0;
+
+    const QwtDynGridLayout *tl = qobject_cast<const QwtDynGridLayout *>(
+        d_data->view->contentsWidget->layout() );
+    if ( tl )
+        maxCols = tl->maxColumns();
+
+    return maxCols;
+}
+
+/*!
+  \brief Set the default mode for legend labels
+
+  Legend labels will be constructed according to the
+  attributes in a QwtLegendData object. When it doesn't
+  contain a value for the QwtLegendData::ModeRole the
+  label will be initialized with the default mode of the legend.
+
+  \param mode Default item mode
+
+  \sa itemMode(), QwtLegendData::value(), QwtPlotItem::legendData()
+  \note Changing the mode doesn't have any effect on existing labels.
+ */
+void QwtLegend::setDefaultItemMode( QwtLegendData::Mode mode )
+{
+    d_data->itemMode = mode;
+}
+
+/*!
+  \return Default item mode
+  \sa setDefaultItemMode()
+*/
+QwtLegendData::Mode QwtLegend::defaultItemMode() const
+{
+    return d_data->itemMode;
+}
+
+/*!
+  The contents widget is the only child of the viewport of 
+  the internal QScrollArea and the parent widget of all legend items.
+
+  \return Container widget of the legend items
+*/
+QWidget *QwtLegend::contentsWidget()
+{
+    return d_data->view->contentsWidget;
+}
+
+/*!
+  \return Horizontal scrollbar
+  \sa verticalScrollBar()
+*/
+QScrollBar *QwtLegend::horizontalScrollBar() const
+{
+    return d_data->view->horizontalScrollBar();
+}
+
+/*!
+  \return Vertical scrollbar
+  \sa horizontalScrollBar()
+*/
+QScrollBar *QwtLegend::verticalScrollBar() const
+{
+    return d_data->view->verticalScrollBar();
+}
+
+/*!
+  The contents widget is the only child of the viewport of 
+  the internal QScrollArea and the parent widget of all legend items.
+
+  \return Container widget of the legend items
+
+*/
+const QWidget *QwtLegend::contentsWidget() const
+{
+    return d_data->view->contentsWidget;
+}
+
+/*!
+  \brief Update the entries for an item
+
+  \param itemInfo Info for an item
+  \param data List of legend entry attributes for the item
+ */
+void QwtLegend::updateLegend( const QVariant &itemInfo, 
+    const QList<QwtLegendData> &data )
+{
+    QList<QWidget *> widgetList = legendWidgets( itemInfo );
+
+    if ( widgetList.size() != data.size() )
+    {
+        QLayout *contentsLayout = d_data->view->contentsWidget->layout();
+
+        while ( widgetList.size() > data.size() )
+        {
+            QWidget *w = widgetList.takeLast();
+
+            contentsLayout->removeWidget( w );
+
+            // updates might be triggered by signals from the legend widget
+            // itself. So we better don't delete it here.
+
+            w->hide();
+            w->deleteLater();
+        }
+
+        for ( int i = widgetList.size(); i < data.size(); i++ )
+        {
+            QWidget *widget = createWidget( data[i] );
+
+            if ( contentsLayout )
+                contentsLayout->addWidget( widget );
+
+            if ( isVisible() )
+            {
+                // QLayout does a delayed show, with the effect, that
+                // the size hint will be wrong, when applications
+                // call replot() right after changing the list
+                // of plot items. So we better do the show now.
+
+                widget->setVisible( true );
+            }
+
+            widgetList += widget;
+        }
+
+        if ( widgetList.isEmpty() )
+        {
+            d_data->itemMap.remove( itemInfo );
+        }
+        else
+        {
+            d_data->itemMap.insert( itemInfo, widgetList );
+        }
+
+        updateTabOrder();
+    }
+    
+    for ( int i = 0; i < data.size(); i++ )
+        updateWidget( widgetList[i], data[i] );
+}
+
+/*!
+  \brief Create a widget to be inserted into the legend
+
+  The default implementation returns a QwtLegendLabel.
+
+  \param data Attributes of the legend entry
+  \return Widget representing data on the legend
+  
+  \note updateWidget() will called soon after createWidget()
+        with the same attributes.
+ */
+QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const
+{
+    Q_UNUSED( data );
+
+    QwtLegendLabel *label = new QwtLegendLabel();
+    label->setItemMode( defaultItemMode() );
+
+    connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) );
+    connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) );
+
+    return label;
+}
+
+/*!
+  \brief Update the widget 
+
+  \param widget Usually a QwtLegendLabel
+  \param data Attributes to be displayed
+
+  \sa createWidget()
+  \note When widget is no QwtLegendLabel updateWidget() does nothing.
+ */
+void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data )
+{
+    QwtLegendLabel *label = qobject_cast<QwtLegendLabel *>( widget );
+    if ( label )
+    {
+        label->setData( data );
+        if ( !data.value( QwtLegendData::ModeRole ).isValid() )
+        {
+            // use the default mode, when there is no specific
+            // hint from the legend data
+
+            label->setItemMode( defaultItemMode() );
+        }
+    }
+}
+
+void QwtLegend::updateTabOrder()
+{
+    QLayout *contentsLayout = d_data->view->contentsWidget->layout();
+    if ( contentsLayout )
+    {
+        // set tab focus chain
+
+        QWidget *w = NULL;
+
+        for ( int i = 0; i < contentsLayout->count(); i++ )
+        {
+            QLayoutItem *item = contentsLayout->itemAt( i );
+            if ( w && item->widget() )
+                QWidget::setTabOrder( w, item->widget() );
+
+            w = item->widget();
+        }
+    }
+}
+
+//! Return a size hint.
+QSize QwtLegend::sizeHint() const
+{
+    QSize hint = d_data->view->contentsWidget->sizeHint();
+    hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
+
+    return hint;
+}
+
+/*!
+  \return The preferred height, for a width.
+  \param width Width
+*/
+int QwtLegend::heightForWidth( int width ) const
+{
+    width -= 2 * frameWidth();
+
+    int h = d_data->view->contentsWidget->heightForWidth( width );
+    if ( h >= 0 )
+        h += 2 * frameWidth();
+
+    return h;
+}
+
+
+/*!
+  Handle QEvent::ChildRemoved andQEvent::LayoutRequest events 
+  for the contentsWidget().
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return Forwarded to QwtAbstractLegend::eventFilter()
+*/
+bool QwtLegend::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object == d_data->view->contentsWidget )
+    {
+        switch ( event->type() )
+        {
+            case QEvent::ChildRemoved:
+            {
+                const QChildEvent *ce = 
+                    static_cast<const QChildEvent *>(event);
+                if ( ce->child()->isWidgetType() )
+                {
+                    QWidget *w = static_cast< QWidget * >( ce->child() );
+                    d_data->itemMap.removeWidget( w );
+                }
+                break;
+            }
+            case QEvent::LayoutRequest:
+            {
+                d_data->view->layoutContents();
+
+                if ( parentWidget() && parentWidget()->layout() == NULL )
+                {
+                    /*
+                       We want the parent widget ( usually QwtPlot ) to recalculate
+                       its layout, when the contentsWidget has changed. But
+                       because of the scroll view we have to forward the LayoutRequest
+                       event manually.
+
+                       We don't use updateGeometry() because it doesn't post LayoutRequest
+                       events when the legend is hidden. But we want the
+                       parent widget notified, so it can show/hide the legend
+                       depending on its items.
+                     */
+                    QApplication::postEvent( parentWidget(),
+                        new QEvent( QEvent::LayoutRequest ) );
+                }                
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    return QwtAbstractLegend::eventFilter( object, event );
+}
+
+/*!
+  Called internally when the legend has been clicked on.
+  Emits a clicked() signal.
+*/
+void QwtLegend::itemClicked()
+{
+    QWidget *w = qobject_cast<QWidget *>( sender() );
+    if ( w )
+    {
+        const QVariant itemInfo = d_data->itemMap.itemInfo( w );
+        if ( itemInfo.isValid() )
+        {
+            const QList<QWidget *> widgetList =
+                d_data->itemMap.legendWidgets( itemInfo );
+
+            const int index = widgetList.indexOf( w );
+            if ( index >= 0 )
+                Q_EMIT clicked( itemInfo, index );
+        }
+    }
+}
+
+/*!
+  Called internally when the legend has been checked
+  Emits a checked() signal.
+*/
+void QwtLegend::itemChecked( bool on )
+{
+    QWidget *w = qobject_cast<QWidget *>( sender() );
+    if ( w )
+    {
+        const QVariant itemInfo = d_data->itemMap.itemInfo( w );
+        if ( itemInfo.isValid() )
+        {
+            const QList<QWidget *> widgetList =
+                d_data->itemMap.legendWidgets( itemInfo );
+
+            const int index = widgetList.indexOf( w );
+            if ( index >= 0 )
+                Q_EMIT checked( itemInfo, on, index );
+        }
+    }
+}
+
+/*!
+  Render the legend into a given rectangle.
+
+  \param painter Painter
+  \param rect Bounding rectangle
+  \param fillBackground When true, fill rect with the widget background 
+
+  \sa renderLegend() is used by QwtPlotRenderer - not by QwtLegend itself
+*/
+void QwtLegend::renderLegend( QPainter *painter, 
+    const QRectF &rect, bool fillBackground ) const
+{
+    if ( d_data->itemMap.isEmpty() )
+        return;
+
+    if ( fillBackground )
+    {
+        if ( autoFillBackground() ||
+            testAttribute( Qt::WA_StyledBackground ) )
+        {
+            QwtPainter::drawBackgound( painter, rect, this );
+        }
+    }
+
+    const QwtDynGridLayout *legendLayout = 
+        qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
+    if ( legendLayout == NULL )
+        return;
+
+    int left, right, top, bottom;
+    getContentsMargins( &left, &top, &right, &bottom );
+
+    QRect layoutRect; 
+    layoutRect.setLeft( qCeil( rect.left() ) + left );
+    layoutRect.setTop( qCeil( rect.top() ) + top );
+    layoutRect.setRight( qFloor( rect.right() ) - right );
+    layoutRect.setBottom( qFloor( rect.bottom() ) - bottom );
+
+    uint numCols = legendLayout->columnsForWidth( layoutRect.width() );
+    QList<QRect> itemRects =
+        legendLayout->layoutItems( layoutRect, numCols );
+
+    int index = 0;
+
+    for ( int i = 0; i < legendLayout->count(); i++ )
+    {
+        QLayoutItem *item = legendLayout->itemAt( i );
+        QWidget *w = item->widget();
+        if ( w )
+        {
+            painter->save();
+
+            painter->setClipRect( itemRects[index], Qt::IntersectClip );
+            renderItem( painter, w, itemRects[index], fillBackground );
+
+            index++;
+            painter->restore();
+        }
+    }
+}
+
+/*!
+  Render a legend entry into a given rectangle.
+
+  \param painter Painter
+  \param widget Widget representing a legend entry
+  \param rect Bounding rectangle
+  \param fillBackground When true, fill rect with the widget background 
+
+  \note When widget is not derived from QwtLegendLabel renderItem
+        does nothing beside the background
+*/
+void QwtLegend::renderItem( QPainter *painter, 
+    const QWidget *widget, const QRectF &rect, bool fillBackground ) const
+{
+    if ( fillBackground )
+    {
+        if ( widget->autoFillBackground() ||
+            widget->testAttribute( Qt::WA_StyledBackground ) )
+        {
+            QwtPainter::drawBackgound( painter, rect, widget );
+        }
+    }
+
+    const QwtLegendLabel *label = qobject_cast<const QwtLegendLabel *>( widget );
+    if ( label )
+    {
+        // icon
+
+        const QwtGraphic &icon = label->data().icon();
+        const QSizeF sz = icon.defaultSize();
+
+        const QRectF iconRect( rect.x() + label->margin(),
+            rect.center().y() - 0.5 * sz.height(), 
+            sz.width(), sz.height() );
+
+        icon.render( painter, iconRect, Qt::KeepAspectRatio );
+
+        // title
+
+        QRectF titleRect = rect;
+        titleRect.setX( iconRect.right() + 2 * label->spacing() );
+
+        painter->setFont( label->font() );
+        painter->setPen( label->palette().color( QPalette::Text ) );
+        const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect );
+    }
+}
+
+/*!
+  \return List of widgets associated to a item
+  \param itemInfo Info about an item
+  \sa legendWidget(), itemInfo(), QwtPlot::itemToInfo()
+ */
+QList<QWidget *> QwtLegend::legendWidgets( const QVariant &itemInfo ) const
+{
+    return d_data->itemMap.legendWidgets( itemInfo );
+}
+
+/*!
+  \return First widget in the list of widgets associated to an item
+  \param itemInfo Info about an item
+  \sa itemInfo(), QwtPlot::itemToInfo()
+  \note Almost all types of items have only one widget
+*/
+QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const
+{
+    const QList<QWidget *> list = d_data->itemMap.legendWidgets( itemInfo );
+    if ( list.isEmpty() )
+        return NULL;
+
+    return list[0];
+}
+
+/*!
+  Find the item that is associated to a widget
+
+  \param widget Widget on the legend
+  \return Associated item info
+  \sa legendWidget()
+ */
+QVariant QwtLegend::itemInfo( const QWidget *widget ) const
+{
+    return d_data->itemMap.itemInfo( widget );
+}
+
+//! \return True, when no item is inserted
+bool QwtLegend::isEmpty() const
+{
+    return d_data->itemMap.isEmpty();
+}
+
+/*!
+    Return the extent, that is needed for the scrollbars
+
+    \param orientation Orientation ( 
+    \return The width of the vertical scrollbar for Qt::Horizontal and v.v.
+ */
+int QwtLegend::scrollExtent( Qt::Orientation orientation ) const
+{
+    int extent = 0;
+
+    if ( orientation == Qt::Horizontal )
+        extent = verticalScrollBar()->sizeHint().width();
+    else
+        extent = horizontalScrollBar()->sizeHint().height();
+
+    return extent;
+}
+
diff --git a/source/third_party/qwt/qwt_legend_data.cpp b/source/third_party/qwt/qwt_legend_data.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d4852ef1302232b1a7370184b3b2a60225c8fee7
--- /dev/null
+++ b/source/third_party/qwt/qwt_legend_data.cpp
@@ -0,0 +1,129 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_legend_data.h"
+
+//! Constructor
+QwtLegendData::QwtLegendData()
+{
+}
+
+//! Destructor
+QwtLegendData::~QwtLegendData()
+{
+}
+
+/*!
+  Set the legend attributes
+
+  QwtLegendData actually is a QMap<int, QVariant> with some
+  convenience interfaces
+
+  \param map Values
+  \sa values()
+ */
+void QwtLegendData::setValues( const QMap<int, QVariant> &map )
+{
+    d_map = map;
+}
+
+/*!
+  \return Legend attributes
+  \sa setValues()
+ */
+const QMap<int, QVariant> &QwtLegendData::values() const
+{
+    return d_map;
+}
+
+/*!
+  \param role Attribute role
+  \return True, when the internal map has an entry for role
+ */
+bool QwtLegendData::hasRole( int role ) const
+{
+    return d_map.contains( role );
+}
+
+/*!
+  Set an attribute value
+
+  \param role Attribute role
+  \param data Attribute value
+
+  \sa value()
+ */
+void QwtLegendData::setValue( int role, const QVariant &data )
+{
+    d_map[role] = data;
+}
+
+/*!
+  \param role Attribute role
+  \return Attribute value for a specific role
+ */
+QVariant QwtLegendData::value( int role ) const
+{
+    if ( !d_map.contains( role ) )
+        return QVariant();
+
+    return d_map[role];
+}
+
+//! \return True, when the internal map is empty
+bool QwtLegendData::isValid() const
+{
+    return !d_map.isEmpty();
+}
+
+//! \return Value of the TitleRole attribute
+QwtText QwtLegendData::title() const
+{
+    QwtText text;
+
+    const QVariant titleValue = value( QwtLegendData::TitleRole );
+    if ( titleValue.canConvert<QwtText>() )
+    {
+        text = qvariant_cast<QwtText>( titleValue );
+    }
+    else if ( titleValue.canConvert<QString>() )
+    {
+        text.setText( qvariant_cast<QString>( titleValue ) );
+    }
+
+    return text;
+}
+
+//! \return Value of the IconRole attribute
+QwtGraphic QwtLegendData::icon() const
+{
+    const QVariant iconValue = value( QwtLegendData::IconRole );
+
+    QwtGraphic graphic;
+    if ( iconValue.canConvert<QwtGraphic>() )
+    {
+        graphic = qvariant_cast<QwtGraphic>( iconValue );
+    }
+
+    return graphic;
+}
+
+//! \return Value of the ModeRole attribute
+QwtLegendData::Mode QwtLegendData::mode() const
+{
+    const QVariant modeValue = value( QwtLegendData::ModeRole );
+    if ( modeValue.canConvert<int>() )
+    {
+        const int mode = qvariant_cast<int>( modeValue );
+        return static_cast<QwtLegendData::Mode>( mode );
+    }
+    
+    return QwtLegendData::ReadOnly;
+}
+
diff --git a/source/third_party/qwt/qwt_legend_label.cpp b/source/third_party/qwt/qwt_legend_label.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f99646140a5bae8c5be5b09b1774cc9b5b81a102
--- /dev/null
+++ b/source/third_party/qwt/qwt_legend_label.cpp
@@ -0,0 +1,421 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_legend_label.h"
+#include "qwt/qwt_legend_data.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_symbol.h"
+#include "qwt/qwt_graphic.h"
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include <qstyle.h>
+#include <qpen.h>
+#include <qevent.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+
+static const int ButtonFrame = 2;
+static const int Margin = 2;
+
+static QSize buttonShift( const QwtLegendLabel *w )
+{
+    QStyleOption option;
+    option.init( w );
+
+    const int ph = w->style()->pixelMetric(
+        QStyle::PM_ButtonShiftHorizontal, &option, w );
+    const int pv = w->style()->pixelMetric(
+        QStyle::PM_ButtonShiftVertical, &option, w );
+    return QSize( ph, pv );
+}
+
+class QwtLegendLabel::PrivateData
+{
+public:
+    PrivateData():
+        itemMode( QwtLegendData::ReadOnly ),
+        isDown( false ),
+        spacing( Margin )
+    {
+    }
+
+    QwtLegendData::Mode itemMode;
+    QwtLegendData legendData;
+    bool isDown;
+
+    QPixmap icon;
+
+    int spacing;
+};
+
+/*!
+  Set the attributes of the legend label
+
+  \param legendData Attributes of the label
+  \sa data()
+ */
+void QwtLegendLabel::setData( const QwtLegendData &legendData )
+{
+    d_data->legendData = legendData;
+
+    const bool doUpdate = updatesEnabled();
+    setUpdatesEnabled( false );
+
+    setText( legendData.title() );
+    setIcon( legendData.icon().toPixmap() );
+
+    if ( legendData.hasRole( QwtLegendData::ModeRole ) )
+        setItemMode( legendData.mode() );
+
+    if ( doUpdate )
+    {
+        setUpdatesEnabled( true );
+        update();
+    }
+}
+
+/*!
+  \return Attributes of the label
+  \sa setData(), QwtPlotItem::legendData()
+ */
+const QwtLegendData &QwtLegendLabel::data() const
+{
+    return d_data->legendData;
+}
+
+/*!
+  \param parent Parent widget
+*/
+QwtLegendLabel::QwtLegendLabel( QWidget *parent ):
+    QwtTextLabel( parent )
+{
+    d_data = new PrivateData;
+    setMargin( Margin );
+    setIndent( Margin );
+}
+
+//! Destructor
+QwtLegendLabel::~QwtLegendLabel()
+{
+    delete d_data;
+    d_data = NULL;
+}
+
+/*!
+   Set the text to the legend item
+
+   \param text Text label
+    \sa QwtTextLabel::text()
+*/
+void QwtLegendLabel::setText( const QwtText &text )
+{
+    const int flags = Qt::AlignLeft | Qt::AlignVCenter
+        | Qt::TextExpandTabs | Qt::TextWordWrap;
+
+    QwtText txt = text;
+    txt.setRenderFlags( flags );
+
+    QwtTextLabel::setText( txt );
+}
+
+/*!
+   Set the item mode
+   The default is QwtLegendData::ReadOnly
+
+   \param mode Item mode
+   \sa itemMode()
+*/
+void QwtLegendLabel::setItemMode( QwtLegendData::Mode mode )
+{
+    if ( mode != d_data->itemMode )
+    {
+        d_data->itemMode = mode;
+        d_data->isDown = false;
+
+        setFocusPolicy( ( mode != QwtLegendData::ReadOnly ) 
+            ? Qt::TabFocus : Qt::NoFocus );
+        setMargin( ButtonFrame + Margin );
+
+        updateGeometry();
+    }
+}
+
+/*!
+   \return Item mode
+   \sa setItemMode()
+*/
+QwtLegendData::Mode QwtLegendLabel::itemMode() const
+{
+    return d_data->itemMode;
+}
+
+/*!
+  Assign the icon
+
+  \param icon Pixmap representing a plot item
+
+  \sa icon(), QwtPlotItem::legendIcon()
+*/
+void QwtLegendLabel::setIcon( const QPixmap &icon )
+{
+    d_data->icon = icon;
+
+    int indent = margin() + d_data->spacing;
+    if ( icon.width() > 0 )
+        indent += icon.width() + d_data->spacing;
+
+    setIndent( indent );
+}
+
+/*!
+  \return Pixmap representing a plot item
+  \sa setIcon()
+*/
+QPixmap QwtLegendLabel::icon() const
+{
+    return d_data->icon;
+}
+
+/*!
+   \brief Change the spacing between icon and text
+
+   \param spacing Spacing
+   \sa spacing(), QwtTextLabel::margin()
+*/
+void QwtLegendLabel::setSpacing( int spacing )
+{
+    spacing = qMax( spacing, 0 );
+    if ( spacing != d_data->spacing )
+    {
+        d_data->spacing = spacing;
+
+        int indent = margin() + d_data->spacing;
+        if ( d_data->icon.width() > 0 )
+            indent += d_data->icon.width() + d_data->spacing;
+
+        setIndent( indent );
+    }
+}
+
+/*!
+   \return Spacing between icon and text
+   \sa setSpacing(), QwtTextLabel::margin()
+*/
+int QwtLegendLabel::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+    Check/Uncheck a the item
+
+    \param on check/uncheck
+    \sa setItemMode()
+*/
+void QwtLegendLabel::setChecked( bool on )
+{
+    if ( d_data->itemMode == QwtLegendData::Checkable )
+    {
+        const bool isBlocked = signalsBlocked();
+        blockSignals( true );
+
+        setDown( on );
+
+        blockSignals( isBlocked );
+    }
+}
+
+//! Return true, if the item is checked
+bool QwtLegendLabel::isChecked() const
+{
+    return d_data->itemMode == QwtLegendData::Checkable && isDown();
+}
+
+//! Set the item being down
+void QwtLegendLabel::setDown( bool down )
+{
+    if ( down == d_data->isDown )
+        return;
+
+    d_data->isDown = down;
+    update();
+
+    if ( d_data->itemMode == QwtLegendData::Clickable )
+    {
+        if ( d_data->isDown )
+            Q_EMIT pressed();
+        else
+        {
+            Q_EMIT released();
+            Q_EMIT clicked();
+        }
+    }
+
+    if ( d_data->itemMode == QwtLegendData::Checkable )
+        Q_EMIT checked( d_data->isDown );
+}
+
+//! Return true, if the item is down
+bool QwtLegendLabel::isDown() const
+{
+    return d_data->isDown;
+}
+
+//! Return a size hint
+QSize QwtLegendLabel::sizeHint() const
+{
+    QSize sz = QwtTextLabel::sizeHint();
+    sz.setHeight( qMax( sz.height(), d_data->icon.height() + 4 ) );
+
+    if ( d_data->itemMode != QwtLegendData::ReadOnly )
+    {
+        sz += buttonShift( this );
+        sz = sz.expandedTo( QApplication::globalStrut() );
+    }
+
+    return sz;
+}
+
+//! Paint event
+void QwtLegendLabel::paintEvent( QPaintEvent *e )
+{
+    const QRect cr = contentsRect();
+
+    QPainter painter( this );
+    painter.setClipRegion( e->region() );
+
+    if ( d_data->isDown )
+    {
+        qDrawWinButton( &painter, 0, 0, width(), height(),
+            palette(), true );
+    }
+
+    painter.save();
+
+    if ( d_data->isDown )
+    {
+        const QSize shiftSize = buttonShift( this );
+        painter.translate( shiftSize.width(), shiftSize.height() );
+    }
+
+    painter.setClipRect( cr );
+
+    drawContents( &painter );
+
+    if ( !d_data->icon.isNull() )
+    {
+        QRect iconRect = cr;
+        iconRect.setX( iconRect.x() + margin() );
+        if ( d_data->itemMode != QwtLegendData::ReadOnly )
+            iconRect.setX( iconRect.x() + ButtonFrame );
+
+        iconRect.setSize( d_data->icon.size() );
+        iconRect.moveCenter( QPoint( iconRect.center().x(), cr.center().y() ) );
+
+        painter.drawPixmap( iconRect, d_data->icon );
+    }
+
+    painter.restore();
+}
+
+//! Handle mouse press events
+void QwtLegendLabel::mousePressEvent( QMouseEvent *e )
+{
+    if ( e->button() == Qt::LeftButton )
+    {
+        switch ( d_data->itemMode )
+        {
+            case QwtLegendData::Clickable:
+            {
+                setDown( true );
+                return;
+            }
+            case QwtLegendData::Checkable:
+            {
+                setDown( !isDown() );
+                return;
+            }
+            default:;
+        }
+    }
+    QwtTextLabel::mousePressEvent( e );
+}
+
+//! Handle mouse release events
+void QwtLegendLabel::mouseReleaseEvent( QMouseEvent *e )
+{
+    if ( e->button() == Qt::LeftButton )
+    {
+        switch ( d_data->itemMode )
+        {
+            case QwtLegendData::Clickable:
+            {
+                setDown( false );
+                return;
+            }
+            case QwtLegendData::Checkable:
+            {
+                return; // do nothing, but accept
+            }
+            default:;
+        }
+    }
+    QwtTextLabel::mouseReleaseEvent( e );
+}
+
+//! Handle key press events
+void QwtLegendLabel::keyPressEvent( QKeyEvent *e )
+{
+    if ( e->key() == Qt::Key_Space )
+    {
+        switch ( d_data->itemMode )
+        {
+            case QwtLegendData::Clickable:
+            {
+                if ( !e->isAutoRepeat() )
+                    setDown( true );
+                return;
+            }
+            case QwtLegendData::Checkable:
+            {
+                if ( !e->isAutoRepeat() )
+                    setDown( !isDown() );
+                return;
+            }
+            default:;
+        }
+    }
+
+    QwtTextLabel::keyPressEvent( e );
+}
+
+//! Handle key release events
+void QwtLegendLabel::keyReleaseEvent( QKeyEvent *e )
+{
+    if ( e->key() == Qt::Key_Space )
+    {
+        switch ( d_data->itemMode )
+        {
+            case QwtLegendData::Clickable:
+            {
+                if ( !e->isAutoRepeat() )
+                    setDown( false );
+                return;
+            }
+            case QwtLegendData::Checkable:
+            {
+                return; // do nothing, but accept
+            }
+            default:;
+        }
+    }
+
+    QwtTextLabel::keyReleaseEvent( e );
+}
diff --git a/source/third_party/qwt/qwt_magnifier.cpp b/source/third_party/qwt/qwt_magnifier.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..40c6c41047315f94924cad6a9e6bd8568b736b40
--- /dev/null
+++ b/source/third_party/qwt/qwt_magnifier.cpp
@@ -0,0 +1,492 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_magnifier.h"
+#include "qwt/qwt_math.h"
+#include <qevent.h>
+#include <qwidget.h>
+
+class QwtMagnifier::PrivateData
+{
+public:
+    PrivateData():
+        isEnabled( false ),
+        wheelFactor( 0.9 ),
+        wheelModifiers( Qt::NoModifier ),
+        mouseFactor( 0.95 ),
+        mouseButton( Qt::RightButton ),
+        mouseButtonModifiers( Qt::NoModifier ),
+        keyFactor( 0.9 ),
+        zoomInKey( Qt::Key_Plus ),
+        zoomInKeyModifiers( Qt::NoModifier ),
+        zoomOutKey( Qt::Key_Minus ),
+        zoomOutKeyModifiers( Qt::NoModifier ),
+        mousePressed( false )
+    {
+    }
+
+    bool isEnabled;
+
+    double wheelFactor;
+    Qt::KeyboardModifiers wheelModifiers;
+
+    double mouseFactor;
+
+    Qt::MouseButton mouseButton;
+    Qt::KeyboardModifiers mouseButtonModifiers;
+
+    double keyFactor;
+
+    int zoomInKey;
+    Qt::KeyboardModifiers zoomInKeyModifiers;
+
+    int zoomOutKey;
+    Qt::KeyboardModifiers  zoomOutKeyModifiers;
+
+    bool mousePressed;
+    bool hasMouseTracking;
+    QPoint mousePos;
+};
+
+/*!
+   Constructor
+   \param parent Widget to be magnified
+*/
+QwtMagnifier::QwtMagnifier( QWidget *parent ):
+    QObject( parent )
+{
+    d_data = new PrivateData();
+    setEnabled( true );
+}
+
+//! Destructor
+QwtMagnifier::~QwtMagnifier()
+{
+    delete d_data;
+}
+
+/*!
+  \brief En/disable the magnifier
+
+  When enabled is true an event filter is installed for
+  the observed widget, otherwise the event filter is removed.
+
+  \param on true or false
+  \sa isEnabled(), eventFilter()
+*/
+void QwtMagnifier::setEnabled( bool on )
+{
+    if ( d_data->isEnabled != on )
+    {
+        d_data->isEnabled = on;
+
+        QObject *o = parent();
+        if ( o )
+        {
+            if ( d_data->isEnabled )
+                o->installEventFilter( this );
+            else
+                o->removeEventFilter( this );
+        }
+    }
+}
+
+/*!
+  \return true when enabled, false otherwise
+  \sa setEnabled(), eventFilter()
+*/
+bool QwtMagnifier::isEnabled() const
+{
+    return d_data->isEnabled;
+}
+
+/*!
+   \brief Change the wheel factor
+
+   The wheel factor defines the ratio between the current range
+   on the parent widget and the zoomed range for each step of the wheel.
+
+   Use values > 1 for magnification (i.e. 2.0) and values < 1 for
+   scaling down (i.e. 1/2.0 = 0.5). You can use this feature for
+   inverting the direction of the wheel.
+
+   The default value is 0.9.
+
+   \param factor Wheel factor
+   \sa wheelFactor(), setWheelButtonState(),
+       setMouseFactor(), setKeyFactor()
+*/
+void QwtMagnifier::setWheelFactor( double factor )
+{
+    d_data->wheelFactor = factor;
+}
+
+/*!
+   \return Wheel factor
+   \sa setWheelFactor()
+*/
+double QwtMagnifier::wheelFactor() const
+{
+    return d_data->wheelFactor;
+}
+
+/*!
+   Assign keyboard modifiers for zooming in/out using the wheel.
+   The default modifiers are Qt::NoModifiers.
+
+   \param modifiers Keyboard modifiers
+   \sa wheelModifiers()
+*/
+void QwtMagnifier::setWheelModifiers( Qt::KeyboardModifiers modifiers )
+{
+    d_data->wheelModifiers = modifiers;
+}
+
+/*!
+   \return Wheel modifiers
+   \sa setWheelModifiers()
+*/
+Qt::KeyboardModifiers QwtMagnifier::wheelModifiers() const
+{
+    return d_data->wheelModifiers;
+}
+
+/*!
+   \brief Change the mouse factor
+
+   The mouse factor defines the ratio between the current range
+   on the parent widget and the zoomed range for each vertical mouse movement.
+   The default value is 0.95.
+
+   \param factor Wheel factor
+   \sa mouseFactor(), setMouseButton(), setWheelFactor(), setKeyFactor()
+*/
+void QwtMagnifier::setMouseFactor( double factor )
+{
+    d_data->mouseFactor = factor;
+}
+
+/*!
+   \return Mouse factor
+   \sa setMouseFactor()
+*/
+double QwtMagnifier::mouseFactor() const
+{
+    return d_data->mouseFactor;
+}
+
+/*!
+   Assign the mouse button, that is used for zooming in/out.
+   The default value is Qt::RightButton.
+
+   \param button Button
+   \param modifiers Keyboard modifiers
+
+   \sa getMouseButton()
+*/
+void QwtMagnifier::setMouseButton( 
+    Qt::MouseButton button, Qt::KeyboardModifiers modifiers )
+{
+    d_data->mouseButton = button;
+    d_data->mouseButtonModifiers = modifiers;
+}
+
+//! \sa setMouseButton()
+void QwtMagnifier::getMouseButton(
+    Qt::MouseButton &button, Qt::KeyboardModifiers &modifiers ) const
+{
+    button = d_data->mouseButton;
+    modifiers = d_data->mouseButtonModifiers;
+}
+
+/*!
+   \brief Change the key factor
+
+   The key factor defines the ratio between the current range
+   on the parent widget and the zoomed range for each key press of
+   the zoom in/out keys. The default value is 0.9.
+
+   \param factor Key factor
+   \sa keyFactor(), setZoomInKey(), setZoomOutKey(),
+       setWheelFactor, setMouseFactor()
+*/
+void QwtMagnifier::setKeyFactor( double factor )
+{
+    d_data->keyFactor = factor;
+}
+
+/*!
+   \return Key factor
+   \sa setKeyFactor()
+*/
+double QwtMagnifier::keyFactor() const
+{
+    return d_data->keyFactor;
+}
+
+/*!
+   Assign the key, that is used for zooming in.
+   The default combination is Qt::Key_Plus + Qt::NoModifier.
+
+   \param key
+   \param modifiers
+   \sa getZoomInKey(), setZoomOutKey()
+*/
+void QwtMagnifier::setZoomInKey( int key, 
+    Qt::KeyboardModifiers modifiers )
+{
+    d_data->zoomInKey = key;
+    d_data->zoomInKeyModifiers = modifiers;
+}
+
+/*! 
+   \brief Retrieve the settings of the zoom in key
+
+   \param key Key code, see Qt::Key
+   \param modifiers Keyboard modifiers
+
+   \sa setZoomInKey()
+*/
+void QwtMagnifier::getZoomInKey( int &key, 
+    Qt::KeyboardModifiers &modifiers ) const
+{
+    key = d_data->zoomInKey;
+    modifiers = d_data->zoomInKeyModifiers;
+}
+
+/*!
+   Assign the key, that is used for zooming out.
+   The default combination is Qt::Key_Minus + Qt::NoModifier.
+
+   \param key
+   \param modifiers
+   \sa getZoomOutKey(), setZoomOutKey()
+*/
+void QwtMagnifier::setZoomOutKey( int key, 
+    Qt::KeyboardModifiers modifiers )
+{
+    d_data->zoomOutKey = key;
+    d_data->zoomOutKeyModifiers = modifiers;
+}
+
+/*! 
+   \brief Retrieve the settings of the zoom out key
+
+   \param key Key code, see Qt::Key
+   \param modifiers Keyboard modifiers
+
+   \sa setZoomOutKey()
+*/
+void QwtMagnifier::getZoomOutKey( int &key, 
+    Qt::KeyboardModifiers &modifiers ) const
+{
+    key = d_data->zoomOutKey;
+    modifiers = d_data->zoomOutKeyModifiers;
+}
+
+/*!
+  \brief Event filter
+
+  When isEnabled() is true, the mouse events of the
+  observed widget are filtered.
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return Forwarded to QObject::eventFilter()
+
+  \sa widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent()
+      widgetKeyReleaseEvent()
+*/
+bool QwtMagnifier::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object && object == parent() )
+    {
+        switch ( event->type() )
+        {
+            case QEvent::MouseButtonPress:
+            {
+                widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseMove:
+            {
+                widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseButtonRelease:
+            {
+                widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::Wheel:
+            {
+                widgetWheelEvent( static_cast<QWheelEvent *>( event ) );
+                break;
+            }
+            case QEvent::KeyPress:
+            {
+                widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
+                break;
+            }
+            case QEvent::KeyRelease:
+            {
+                widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
+                break;
+            }
+            default:;
+        }
+    }
+    return QObject::eventFilter( object, event );
+}
+
+/*!
+  Handle a mouse press event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent()
+*/
+void QwtMagnifier::widgetMousePressEvent( QMouseEvent *mouseEvent )
+{
+    if ( parentWidget() == NULL )
+        return;
+
+    if ( ( mouseEvent->button() != d_data->mouseButton ) ||
+        ( mouseEvent->modifiers() != d_data->mouseButtonModifiers ) )
+    {
+        return;
+    }
+
+    d_data->hasMouseTracking = parentWidget()->hasMouseTracking();
+
+    parentWidget()->setMouseTracking( true );
+    d_data->mousePos = mouseEvent->pos();
+    d_data->mousePressed = true;
+}
+
+/*!
+  Handle a mouse release event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(),
+*/
+void QwtMagnifier::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
+{
+    Q_UNUSED( mouseEvent );
+
+    if ( d_data->mousePressed && parentWidget() )
+    {
+        d_data->mousePressed = false;
+        parentWidget()->setMouseTracking( d_data->hasMouseTracking );
+    }
+}
+
+/*!
+  Handle a mouse move event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+*/
+void QwtMagnifier::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
+{
+    if ( !d_data->mousePressed )
+        return;
+
+    const int dy = mouseEvent->pos().y() - d_data->mousePos.y();
+    if ( dy != 0 )
+    {
+        double f = d_data->mouseFactor;
+        if ( dy < 0 )
+            f = 1 / f;
+
+        rescale( f );
+    }
+
+    d_data->mousePos = mouseEvent->pos();
+}
+
+/*!
+  Handle a wheel event for the observed widget.
+
+  \param wheelEvent Wheel event
+  \sa eventFilter()
+*/
+void QwtMagnifier::widgetWheelEvent( QWheelEvent *wheelEvent )
+{
+    if ( wheelEvent->modifiers() != d_data->wheelModifiers )
+    {
+        return;
+    }
+
+    if ( d_data->wheelFactor != 0.0 )
+    {
+        /*
+            A positive delta indicates that the wheel was
+            rotated forwards away from the user; a negative
+            value indicates that the wheel was rotated
+            backwards toward the user.
+            Most mouse types work in steps of 15 degrees,
+            in which case the delta value is a multiple
+            of 120 (== 15 * 8).
+         */
+        double f = qPow( d_data->wheelFactor, 
+            qAbs( wheelEvent->delta() / 120.0 ) );
+
+        if ( wheelEvent->delta() > 0 )
+            f = 1 / f;
+
+        rescale( f );
+    }
+}
+
+/*!
+  Handle a key press event for the observed widget.
+
+  \param keyEvent Key event
+  \sa eventFilter(), widgetKeyReleaseEvent()
+*/
+void QwtMagnifier::widgetKeyPressEvent( QKeyEvent *keyEvent )
+{
+    if ( keyEvent->key() == d_data->zoomInKey &&
+        keyEvent->modifiers() == d_data->zoomInKeyModifiers )
+    {
+        rescale( d_data->keyFactor );
+    }
+    else if ( keyEvent->key() == d_data->zoomOutKey &&
+        keyEvent->modifiers() == d_data->zoomOutKeyModifiers )
+    {
+        rescale( 1.0 / d_data->keyFactor );
+    }
+}
+
+/*!
+  Handle a key release event for the observed widget.
+
+  \param keyEvent Key event
+  \sa eventFilter(), widgetKeyReleaseEvent()
+*/
+void QwtMagnifier::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
+{
+    Q_UNUSED( keyEvent );
+}
+
+//! \return Parent widget, where the rescaling happens
+QWidget *QwtMagnifier::parentWidget()
+{
+    return qobject_cast<QWidget *>( parent() );
+}
+
+//! \return Parent widget, where the rescaling happens
+const QWidget *QwtMagnifier::parentWidget() const
+{
+    return qobject_cast<const QWidget *>( parent() );
+}
+
diff --git a/source/third_party/qwt/qwt_math.cpp b/source/third_party/qwt/qwt_math.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..877f120e0d9d3a06bd85d306d8d98aa4326184af
--- /dev/null
+++ b/source/third_party/qwt/qwt_math.cpp
@@ -0,0 +1,74 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_math.h"
+
+/*!
+  \brief Find the smallest value in an array
+  \param array Pointer to an array
+  \param size Array size
+*/
+double qwtGetMin( const double *array, int size )
+{
+    if ( size <= 0 )
+        return 0.0;
+
+    double rv = array[0];
+    for ( int i = 1; i < size; i++ )
+        rv = qMin( rv, array[i] );
+
+    return rv;
+}
+
+
+/*!
+  \brief Find the largest value in an array
+  \param array Pointer to an array
+  \param size Array size
+*/
+double qwtGetMax( const double *array, int size )
+{
+    if ( size <= 0 )
+        return 0.0;
+
+    double rv = array[0];
+    for ( int i = 1; i < size; i++ )
+        rv = qMax( rv, array[i] );
+
+    return rv;
+}
+
+/*!
+  \brief Normalize an angle to be int the range [0.0, 2 * PI[
+  \param radians Angle in radians
+  \return Normalized angle in radians
+*/
+double qwtNormalizeRadians( double radians )
+{
+    double a = ::fmod( radians, 2.0 * M_PI );
+    if ( a < 0.0 )
+        a += 2.0 * M_PI;
+
+    return a;
+
+}
+
+/*!
+  \brief Normalize an angle to be int the range [0.0, 360.0[
+  \param radians Angle in degrees
+  \return Normalized angle in degrees
+*/
+double qwtNormalizeDegrees( double degrees )
+{
+    double a = ::fmod( degrees, 360.0 );
+    if ( a < 0.0 )
+        a += 360.0;
+
+    return a;
+}
diff --git a/source/third_party/qwt/qwt_matrix_raster_data.cpp b/source/third_party/qwt/qwt_matrix_raster_data.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d7cc69e62e32d8f933f9b28e0c3d72317b85462
--- /dev/null
+++ b/source/third_party/qwt/qwt_matrix_raster_data.cpp
@@ -0,0 +1,298 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_matrix_raster_data.h"
+#include <qnumeric.h>
+#include <qmath.h>
+
+class QwtMatrixRasterData::PrivateData
+{
+public:
+    PrivateData():
+        resampleMode(QwtMatrixRasterData::NearestNeighbour),
+        numColumns(0)
+    {
+    }
+
+    inline double value(int row, int col) const
+    {
+        return values.data()[ row * numColumns + col ];
+    }
+
+    QwtMatrixRasterData::ResampleMode resampleMode;
+
+    QVector<double> values;
+    int numColumns;
+    int numRows;
+
+    double dx;
+    double dy;
+};
+
+//! Constructor
+QwtMatrixRasterData::QwtMatrixRasterData()
+{
+    d_data = new PrivateData();
+    update();
+}
+
+//! Destructor
+QwtMatrixRasterData::~QwtMatrixRasterData()
+{
+    delete d_data;
+}
+
+/*!
+   \brief Set the resampling algorithm
+
+   \param mode Resampling mode
+   \sa resampleMode(), value()
+*/
+void QwtMatrixRasterData::setResampleMode( ResampleMode mode )
+{
+    d_data->resampleMode = mode;
+}
+
+/*!
+   \return resampling algorithm
+   \sa setResampleMode(), value()
+*/
+QwtMatrixRasterData::ResampleMode QwtMatrixRasterData::resampleMode() const
+{
+    return d_data->resampleMode;
+}
+
+/*!
+   \brief Assign the bounding interval for an axis
+
+   Setting the bounding intervals for the X/Y axis is mandatory
+   to define the positions for the values of the value matrix.
+   The interval in Z direction defines the possible range for
+   the values in the matrix, what is f.e used by QwtPlotSpectrogram
+   to map values to colors. The Z-interval might be the bounding
+   interval of the values in the matrix, but usually it isn't.
+   ( f.e a interval of 0.0-100.0 for values in percentage )
+
+   \param axis X, Y or Z axis
+   \param interval Interval
+   
+   \sa QwtRasterData::interval(), setValueMatrix()
+*/
+void QwtMatrixRasterData::setInterval( 
+    Qt::Axis axis, const QwtInterval &interval )
+{
+    QwtRasterData::setInterval( axis, interval );
+    update();
+}
+
+/*!
+   \brief Assign a value matrix
+
+   The positions of the values are calculated by dividing
+   the bounding rectangle of the X/Y intervals into equidistant
+   rectangles ( pixels ). Each value corresponds to the center of 
+   a pixel.
+
+   \param values Vector of values
+   \param numColumns Number of columns
+
+   \sa valueMatrix(), numColumns(), numRows(), setInterval()()
+*/
+void QwtMatrixRasterData::setValueMatrix( 
+    const QVector<double> &values, int numColumns )
+{
+    d_data->values = values;
+    d_data->numColumns = qMax( numColumns, 0 );
+    update();
+}
+
+/*!
+   \return Value matrix
+   \sa setValueMatrix(), numColumns(), numRows(), setInterval()
+*/
+const QVector<double> QwtMatrixRasterData::valueMatrix() const
+{
+    return d_data->values;
+}
+
+/*!
+  \brief Change a single value in the matrix
+
+  \param row Row index
+  \param col Column index
+  \param value New value
+
+  \sa value(), setValueMatrix()
+*/
+void QwtMatrixRasterData::setValue( int row, int col, double value )
+{
+    if ( row >= 0 && row < d_data->numRows &&
+        col >= 0 && col < d_data->numColumns )
+    {
+        const int index = row * d_data->numColumns + col;
+        d_data->values.data()[ index ] = value;
+    }
+}
+
+/*!
+   \return Number of columns of the value matrix
+   \sa valueMatrix(), numRows(), setValueMatrix()
+*/
+int QwtMatrixRasterData::numColumns() const
+{
+    return d_data->numColumns;
+}
+
+/*!
+   \return Number of rows of the value matrix
+   \sa valueMatrix(), numColumns(), setValueMatrix()
+*/
+int QwtMatrixRasterData::numRows() const
+{
+    return d_data->numRows;
+}
+
+/*!
+   \brief Calculate the pixel hint
+
+   pixelHint() returns the geometry of a pixel, that can be used 
+   to calculate the resolution and alignment of the plot item, that is
+   representing the data. 
+
+   - NearestNeighbour\n
+     pixelHint() returns the surrounding pixel of the top left value 
+     in the matrix.
+
+   - BilinearInterpolation\n
+     Returns an empty rectangle recommending
+     to render in target device ( f.e. screen ) resolution. 
+
+   \param area Requested area, ignored
+   \return Calculated hint
+
+   \sa ResampleMode, setMatrix(), setInterval()
+*/
+QRectF QwtMatrixRasterData::pixelHint( const QRectF &area ) const
+{
+    Q_UNUSED( area )
+
+    QRectF rect;
+    if ( d_data->resampleMode == NearestNeighbour )
+    {
+        const QwtInterval intervalX = interval( Qt::XAxis );
+        const QwtInterval intervalY = interval( Qt::YAxis );
+        if ( intervalX.isValid() && intervalY.isValid() )
+        {
+            rect = QRectF( intervalX.minValue(), intervalY.minValue(),
+                d_data->dx, d_data->dy );
+        }
+    }
+
+    return rect;
+}
+
+/*!
+   \return the value at a raster position
+
+   \param x X value in plot coordinates
+   \param y Y value in plot coordinates
+
+   \sa ResampleMode
+*/
+double QwtMatrixRasterData::value( double x, double y ) const
+{
+    const QwtInterval xInterval = interval( Qt::XAxis );
+    const QwtInterval yInterval = interval( Qt::YAxis );
+
+    if ( !( xInterval.contains(x) && yInterval.contains(y) ) )
+        return qQNaN();
+
+    double value;
+
+    switch( d_data->resampleMode )
+    {
+        case BilinearInterpolation:
+        {
+            int col1 = qRound( (x - xInterval.minValue() ) / d_data->dx ) - 1;
+            int row1 = qRound( (y - yInterval.minValue() ) / d_data->dy ) - 1;
+            int col2 = col1 + 1;
+            int row2 = row1 + 1;
+
+            if ( col1 < 0 )
+                col1 = col2;
+            else if ( col2 >= static_cast<int>( d_data->numColumns ) )
+                col2 = col1;
+
+            if ( row1 < 0 )
+                row1 = row2;
+            else if ( row2 >= static_cast<int>( d_data->numRows ) )
+                row2 = row1;
+
+            const double v11 = d_data->value( row1, col1 );
+            const double v21 = d_data->value( row1, col2 );
+            const double v12 = d_data->value( row2, col1 );
+            const double v22 = d_data->value( row2, col2 );
+
+            const double x2 = xInterval.minValue() + 
+                ( col2 + 0.5 ) * d_data->dx;
+            const double y2 = yInterval.minValue() + 
+                ( row2 + 0.5 ) * d_data->dy;
+                
+            const double rx = ( x2 - x ) / d_data->dx;
+            const double ry = ( y2 - y ) / d_data->dy;
+
+            const double vr1 = rx * v11 + ( 1.0 - rx ) * v21;
+            const double vr2 = rx * v12 + ( 1.0 - rx ) * v22;
+
+            value = ry * vr1 + ( 1.0 - ry ) * vr2;
+
+            break;
+        }
+        case NearestNeighbour:
+        default:
+        {
+            int row = int( (y - yInterval.minValue() ) / d_data->dy );
+            int col = int( (x - xInterval.minValue() ) / d_data->dx );
+
+            // In case of intervals, where the maximum is included
+            // we get out of bound for row/col, when the value for the
+            // maximum is requested. Instead we return the value
+            // from the last row/col
+
+            if ( row >= d_data->numRows )
+                row = d_data->numRows - 1;
+
+            if ( col >= d_data->numColumns )
+                col = d_data->numColumns - 1;
+
+            value = d_data->value( row, col );
+        }
+    }
+
+    return value;
+}
+
+void QwtMatrixRasterData::update()
+{
+    d_data->numRows = 0;
+    d_data->dx = 0.0;
+    d_data->dy = 0.0;
+
+    if ( d_data->numColumns > 0 )
+    {
+        d_data->numRows = d_data->values.size() / d_data->numColumns;
+
+        const QwtInterval xInterval = interval( Qt::XAxis );
+        const QwtInterval yInterval = interval( Qt::YAxis );
+        if ( xInterval.isValid() )
+            d_data->dx = xInterval.width() / d_data->numColumns;
+        if ( yInterval.isValid() )
+            d_data->dy = yInterval.width() / d_data->numRows;
+    }
+}
diff --git a/source/third_party/qwt/qwt_null_paintdevice.cpp b/source/third_party/qwt/qwt_null_paintdevice.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e7c77aa14081b62bb233f967c8d69629ad43f536
--- /dev/null
+++ b/source/third_party/qwt/qwt_null_paintdevice.cpp
@@ -0,0 +1,593 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_null_paintdevice.h"
+#include <qpaintengine.h>
+#include <qpixmap.h>
+
+class QwtNullPaintDevice::PrivateData
+{
+public:
+    PrivateData():
+        mode( QwtNullPaintDevice::NormalMode )
+    {
+    }
+
+    QwtNullPaintDevice::Mode mode;
+};
+
+class QwtNullPaintDevice::PaintEngine: public QPaintEngine
+{
+public:
+    PaintEngine();
+
+    virtual bool begin( QPaintDevice * );
+    virtual bool end();
+
+    virtual Type type () const;
+    virtual void updateState(const QPaintEngineState &);
+
+    virtual void drawRects(const QRect *, int );
+    virtual void drawRects(const QRectF *, int );
+
+    virtual void drawLines(const QLine *, int );
+    virtual void drawLines(const QLineF *, int );
+
+    virtual void drawEllipse(const QRectF &);
+    virtual void drawEllipse(const QRect &);
+
+    virtual void drawPath(const QPainterPath &);
+
+    virtual void drawPoints(const QPointF *, int );
+    virtual void drawPoints(const QPoint *, int );
+
+    virtual void drawPolygon(const QPointF *, int , PolygonDrawMode );
+    virtual void drawPolygon(const QPoint *, int , PolygonDrawMode );
+
+    virtual void drawPixmap(const QRectF &, 
+        const QPixmap &, const QRectF &);
+
+    virtual void drawTextItem(const QPointF &, const QTextItem &);
+
+    virtual void drawTiledPixmap(const QRectF &, 
+        const QPixmap &, const QPointF &s);
+
+    virtual void drawImage(const QRectF &, 
+        const QImage &, const QRectF &, Qt::ImageConversionFlags );
+
+private:
+    QwtNullPaintDevice *nullDevice();
+};
+    
+QwtNullPaintDevice::PaintEngine::PaintEngine():
+    QPaintEngine( QPaintEngine::AllFeatures )
+{
+}
+
+bool QwtNullPaintDevice::PaintEngine::begin( QPaintDevice * )
+{
+    setActive( true );
+    return true;
+}
+
+bool QwtNullPaintDevice::PaintEngine::end()
+{
+    setActive( false );
+    return true;
+}
+
+QPaintEngine::Type QwtNullPaintDevice::PaintEngine::type() const
+{
+    return QPaintEngine::User;
+}
+
+void QwtNullPaintDevice::PaintEngine::drawRects(
+    const QRect *rects, int rectCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawRects( rects, rectCount );
+        return;
+    }
+
+    device->drawRects( rects, rectCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawRects(
+    const QRectF *rects, int rectCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawRects( rects, rectCount );
+        return;
+    }
+
+    device->drawRects( rects, rectCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawLines(
+    const QLine *lines, int lineCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawLines( lines, lineCount );
+        return;
+    }
+
+    device->drawLines( lines, lineCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawLines(
+    const QLineF *lines, int lineCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawLines( lines, lineCount );
+        return;
+    }
+
+    device->drawLines( lines, lineCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawEllipse(
+    const QRectF &rect)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawEllipse( rect );
+        return;
+    }
+
+    device->drawEllipse( rect );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawEllipse(
+    const QRect &rect)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawEllipse( rect );
+        return;
+    }
+
+    device->drawEllipse( rect );
+}
+
+
+void QwtNullPaintDevice::PaintEngine::drawPath(
+    const QPainterPath &path)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    device->drawPath( path );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPoints(
+    const QPointF *points, int pointCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawPoints( points, pointCount );
+        return;
+    }
+
+    device->drawPoints( points, pointCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPoints(
+    const QPoint *points, int pointCount)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawPoints( points, pointCount );
+        return;
+    }
+
+    device->drawPoints( points, pointCount );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPolygon(
+    const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() == QwtNullPaintDevice::PathMode )
+    {
+        QPainterPath path;
+
+        if ( pointCount > 0 )
+        {
+            path.moveTo( points[0] );
+            for ( int i = 1; i < pointCount; i++ )
+                path.lineTo( points[i] );
+
+            if ( mode != PolylineMode )
+                path.closeSubpath();
+        }
+
+        device->drawPath( path );
+        return;
+    }
+
+    device->drawPolygon( points, pointCount, mode );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPolygon(
+    const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() == QwtNullPaintDevice::PathMode )
+    {
+        QPainterPath path;
+
+        if ( pointCount > 0 )
+        {
+            path.moveTo( points[0] );
+            for ( int i = 1; i < pointCount; i++ )
+                path.lineTo( points[i] );
+
+            if ( mode != PolylineMode )
+                path.closeSubpath();
+        }
+
+        device->drawPath( path );
+        return;
+    }
+
+    device->drawPolygon( points, pointCount, mode );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawPixmap( 
+    const QRectF &rect, const QPixmap &pm, const QRectF &subRect )
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    device->drawPixmap( rect, pm, subRect );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawTextItem(
+    const QPointF &pos, const QTextItem &textItem)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawTextItem( pos, textItem );
+        return;
+    }
+
+    device->drawTextItem( pos, textItem );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawTiledPixmap(
+    const QRectF &rect, const QPixmap &pixmap, 
+    const QPointF &subRect)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    if ( device->mode() != QwtNullPaintDevice::NormalMode )
+    {
+        QPaintEngine::drawTiledPixmap( rect, pixmap, subRect );
+        return;
+    }   
+
+    device->drawTiledPixmap( rect, pixmap, subRect );
+}
+
+void QwtNullPaintDevice::PaintEngine::drawImage(
+    const QRectF &rect, const QImage &image, 
+    const QRectF &subRect, Qt::ImageConversionFlags flags)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    device->drawImage( rect, image, subRect, flags );
+}
+
+void QwtNullPaintDevice::PaintEngine::updateState(
+    const QPaintEngineState &state)
+{
+    QwtNullPaintDevice *device = nullDevice();
+    if ( device == NULL )
+        return;
+
+    device->updateState( state );
+}
+
+inline QwtNullPaintDevice *QwtNullPaintDevice::PaintEngine::nullDevice()
+{
+    if ( !isActive() )
+        return NULL;
+
+    return static_cast<QwtNullPaintDevice *>( paintDevice() );
+}
+
+//! Constructor
+QwtNullPaintDevice::QwtNullPaintDevice():
+    d_engine( NULL )
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtNullPaintDevice::~QwtNullPaintDevice()
+{
+    delete d_engine;
+    delete d_data;
+}
+
+/*!
+    Set the render mode
+
+    \param mode New mode
+    \sa mode()
+ */
+void QwtNullPaintDevice::setMode( Mode mode )
+{
+    d_data->mode = mode;
+}
+
+/*! 
+    \return Render mode
+    \sa setMode()
+*/
+QwtNullPaintDevice::Mode QwtNullPaintDevice::mode() const
+{
+    return d_data->mode;
+}
+
+//! See QPaintDevice::paintEngine()
+QPaintEngine *QwtNullPaintDevice::paintEngine() const
+{
+    if ( d_engine == NULL )
+    {
+        QwtNullPaintDevice *that = 
+            const_cast< QwtNullPaintDevice * >( this );
+
+        that->d_engine = new PaintEngine();
+    }
+
+    return d_engine;
+}
+
+/*! 
+    See QPaintDevice::metric()
+
+    \param deviceMetric Type of metric
+    \return Metric information for the given paint device metric.
+
+    \sa sizeMetrics()
+*/
+int QwtNullPaintDevice::metric( PaintDeviceMetric deviceMetric ) const
+{
+    int value;
+
+    switch ( deviceMetric ) 
+    {
+        case PdmWidth:
+        {
+            value = sizeMetrics().width();
+            break;
+        }
+        case PdmHeight:
+        {
+            value = sizeMetrics().height();
+            break;
+        }
+        case PdmNumColors:
+        {
+            value = 0xffffffff;
+            break;
+        }
+        case PdmDepth:
+        {
+            value = 32;
+            break;
+        }
+        case PdmPhysicalDpiX:
+        case PdmPhysicalDpiY:
+        case PdmDpiY:
+        case PdmDpiX:
+        {
+            value = 72;
+            break;
+        }
+        case PdmWidthMM:
+        {
+            value = qRound( metric( PdmWidth ) * 25.4 / metric( PdmDpiX ) );
+            break;
+        }
+        case PdmHeightMM:
+        {
+            value = qRound( metric( PdmHeight ) * 25.4 / metric( PdmDpiY ) );
+            break;
+        }
+        default:
+            value = 0;
+    }
+    return value;
+
+}
+
+//! See QPaintEngine::drawRects()
+void QwtNullPaintDevice::drawRects(
+    const QRect *rects, int rectCount)
+{
+    Q_UNUSED(rects);
+    Q_UNUSED(rectCount);
+}
+
+//! See QPaintEngine::drawRects()
+void QwtNullPaintDevice::drawRects(
+    const QRectF *rects, int rectCount)
+{
+    Q_UNUSED(rects);
+    Q_UNUSED(rectCount);
+}
+
+//! See QPaintEngine::drawLines()
+void QwtNullPaintDevice::drawLines(
+    const QLine *lines, int lineCount)
+{
+    Q_UNUSED(lines);
+    Q_UNUSED(lineCount);
+}
+
+//! See QPaintEngine::drawLines()
+void QwtNullPaintDevice::drawLines(
+    const QLineF *lines, int lineCount)
+{
+    Q_UNUSED(lines);
+    Q_UNUSED(lineCount);
+}
+
+//! See QPaintEngine::drawEllipse()
+void QwtNullPaintDevice::drawEllipse( const QRectF &rect )
+{
+    Q_UNUSED(rect);
+}
+
+//! See QPaintEngine::drawEllipse()
+void QwtNullPaintDevice::drawEllipse( const QRect &rect )
+{
+    Q_UNUSED(rect);
+}
+
+//! See QPaintEngine::drawPath()
+void QwtNullPaintDevice::drawPath( const QPainterPath &path )
+{
+    Q_UNUSED(path);
+}
+
+//! See QPaintEngine::drawPoints()
+void QwtNullPaintDevice::drawPoints(
+    const QPointF *points, int pointCount)
+{
+    Q_UNUSED(points);
+    Q_UNUSED(pointCount);
+}
+
+//! See QPaintEngine::drawPoints()
+void QwtNullPaintDevice::drawPoints(
+    const QPoint *points, int pointCount)
+{
+    Q_UNUSED(points);
+    Q_UNUSED(pointCount);
+}
+
+//! See QPaintEngine::drawPolygon()
+void QwtNullPaintDevice::drawPolygon(
+    const QPointF *points, int pointCount, 
+    QPaintEngine::PolygonDrawMode mode)
+{
+    Q_UNUSED(points);
+    Q_UNUSED(pointCount);
+    Q_UNUSED(mode);
+}
+
+//! See QPaintEngine::drawPolygon()
+void QwtNullPaintDevice::drawPolygon(
+    const QPoint *points, int pointCount, 
+    QPaintEngine::PolygonDrawMode mode)
+{
+    Q_UNUSED(points);
+    Q_UNUSED(pointCount);
+    Q_UNUSED(mode);
+}
+
+//! See QPaintEngine::drawPixmap()
+void QwtNullPaintDevice::drawPixmap( const QRectF &rect, 
+    const QPixmap &pm, const QRectF &subRect )
+{
+    Q_UNUSED(rect);
+    Q_UNUSED(pm);
+    Q_UNUSED(subRect);
+}
+
+//! See QPaintEngine::drawTextItem()
+void QwtNullPaintDevice::drawTextItem(
+    const QPointF &pos, const QTextItem &textItem)
+{
+    Q_UNUSED(pos);
+    Q_UNUSED(textItem);
+}
+
+//! See QPaintEngine::drawTiledPixmap()
+void QwtNullPaintDevice::drawTiledPixmap(
+    const QRectF &rect, const QPixmap &pixmap, 
+    const QPointF &subRect)
+{
+    Q_UNUSED(rect);
+    Q_UNUSED(pixmap);
+    Q_UNUSED(subRect);
+}
+
+//! See QPaintEngine::drawImage()
+void QwtNullPaintDevice::drawImage(
+    const QRectF &rect, const QImage &image, 
+    const QRectF &subRect, Qt::ImageConversionFlags flags)
+{
+    Q_UNUSED(rect);
+    Q_UNUSED(image);
+    Q_UNUSED(subRect);
+    Q_UNUSED(flags);
+}
+
+//! See QPaintEngine::updateState()
+void QwtNullPaintDevice::updateState( 
+    const QPaintEngineState &state )
+{
+    Q_UNUSED(state);
+}
diff --git a/source/third_party/qwt/qwt_painter.cpp b/source/third_party/qwt/qwt_painter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f9a461a272ab3983c1d5c819ca5d74b4ad8c8d4c
--- /dev/null
+++ b/source/third_party/qwt/qwt_painter.cpp
@@ -0,0 +1,1298 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_clipper.h"
+#include "qwt/qwt_color_map.h"
+#include "qwt/qwt_scale_map.h"
+#include <qwindowdefs.h>
+#include <qwidget.h>
+#include <qframe.h>
+#include <qrect.h>
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qpaintdevice.h>
+#include <qpixmap.h>
+#include <qstyle.h>
+#include <qtextdocument.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qstyleoption.h>
+#include <qpaintengine.h>
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+
+#if QT_VERSION >= 0x050000
+#include <qwindow.h>
+#endif
+
+#if QT_VERSION < 0x050000 
+
+#ifdef Q_WS_X11
+#include <qx11info_x11.h>
+#endif
+
+#endif
+
+bool QwtPainter::d_polylineSplitting = true;
+bool QwtPainter::d_roundingAlignment = true;
+
+static inline bool qwtIsClippingNeeded( 
+    const QPainter *painter, QRectF &clipRect )
+{
+    bool doClipping = false;
+    const QPaintEngine *pe = painter->paintEngine();
+    if ( pe && pe->type() == QPaintEngine::SVG )
+    {
+        // The SVG paint engine ignores any clipping,
+
+        if ( painter->hasClipping() )
+        {
+            doClipping = true;
+            clipRect = painter->clipRegion().boundingRect();
+        }
+    }
+
+    return doClipping;
+}
+
+template <class T>
+static inline void qwtDrawPolyline( QPainter *painter,
+    const T *points, int pointCount, bool polylineSplitting )
+{
+    bool doSplit = false;
+    if ( polylineSplitting )
+    {
+        const QPaintEngine *pe = painter->paintEngine();
+        if ( pe && pe->type() == QPaintEngine::Raster )
+        {
+            /*
+                The raster paint engine seems to use some algo with O(n*n).
+                ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
+                To work around this problem, we have to split the polygon into
+                smaller pieces.
+             */
+            doSplit = true;
+        }
+    }
+
+    if ( doSplit )
+    {
+        const int splitSize = 6;
+
+        for ( int i = 0; i < pointCount; i += splitSize )
+        {
+            const int n = qMin( splitSize + 1, pointCount - i );
+            painter->drawPolyline( points + i, n );
+        }
+    }
+    else
+    {
+        painter->drawPolyline( points, pointCount );
+    }
+}
+
+static inline QSize qwtScreenResolution()
+{
+    static QSize screenResolution;
+    if ( !screenResolution.isValid() )
+    {
+        QDesktopWidget *desktop = QApplication::desktop();
+        if ( desktop )
+        {
+            screenResolution.setWidth( desktop->logicalDpiX() );
+            screenResolution.setHeight( desktop->logicalDpiY() );
+        }
+    }
+
+    return screenResolution;
+}
+
+static inline void qwtUnscaleFont( QPainter *painter )
+{
+    if ( painter->font().pixelSize() >= 0 )
+        return;
+
+    const QSize screenResolution = qwtScreenResolution();
+
+    const QPaintDevice *pd = painter->device();
+    if ( pd->logicalDpiX() != screenResolution.width() ||
+        pd->logicalDpiY() != screenResolution.height() )
+    {
+        QFont pixelFont( painter->font(), QApplication::desktop() );
+        pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
+
+        painter->setFont( pixelFont );
+    }
+}
+
+/*!
+  Check is the application is running with the X11 graphics system
+  that has some special capabilities that can be used for incremental
+  painting to a widget.
+
+  \return True, when the graphics system is X11
+*/
+bool QwtPainter::isX11GraphicsSystem()
+{
+    static int onX11 = -1;
+    if ( onX11 < 0 )
+    {
+        QPixmap pm( 1, 1 );
+        QPainter painter( &pm );
+
+        onX11 = ( painter.paintEngine()->type() == QPaintEngine::X11 ) ? 1 : 0;
+    }
+
+    return onX11 == 1;
+}
+
+/*!
+  Check if the painter is using a paint engine, that aligns
+  coordinates to integers. Today these are all paint engines
+  beside QPaintEngine::Pdf and QPaintEngine::SVG.
+
+  If we have an integer based paint engine it is also
+  checked if the painter has a transformation matrix,
+  that rotates or scales.
+
+  \param  painter Painter
+  \return true, when the painter is aligning
+
+  \sa setRoundingAlignment()
+*/
+bool QwtPainter::isAligning( QPainter *painter )
+{
+    if ( painter && painter->isActive() )
+    {
+        switch ( painter->paintEngine()->type() )
+        {
+            case QPaintEngine::Pdf:
+            case QPaintEngine::SVG:
+                return false;
+
+            default:;
+        }
+
+        const QTransform tr = painter->transform();
+        if ( tr.isRotating() || tr.isScaling() )
+        {
+            // we might have to check translations too
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*!
+  Enable whether coordinates should be rounded, before they are painted
+  to a paint engine that floors to integer values. For other paint engines
+  ( PDF, SVG ) this flag has no effect.
+  QwtPainter stores this flag only, the rounding itself is done in 
+  the painting code ( f.e the plot items ).
+
+  The default setting is true. 
+
+  \sa roundingAlignment(), isAligning()
+*/
+void QwtPainter::setRoundingAlignment( bool enable )
+{
+    d_roundingAlignment = enable;
+}
+
+/*!
+  \brief En/Disable line splitting for the raster paint engine
+
+  In some Qt versions the raster paint engine paints polylines of many points
+  much faster when they are split in smaller chunks: f.e all supported Qt versions
+  >= Qt 5.0 when drawing an antialiased polyline with a pen width >=2.
+
+  The default setting is true.
+
+  \sa polylineSplitting()
+*/
+void QwtPainter::setPolylineSplitting( bool enable )
+{
+    d_polylineSplitting = enable;
+}
+
+//! Wrapper for QPainter::drawPath()
+void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path )
+{
+    painter->drawPath( path );
+}
+
+//! Wrapper for QPainter::drawRect()
+void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
+{
+    drawRect( painter, QRectF( x, y, w, h ) );
+}
+
+//! Wrapper for QPainter::drawRect()
+void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
+{
+    const QRectF r = rect;
+
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        if ( !clipRect.intersects( r ) )
+            return;
+
+        if ( !clipRect.contains( r ) )
+        {
+            fillRect( painter, r & clipRect, painter->brush() );
+
+            painter->save();
+            painter->setBrush( Qt::NoBrush );
+            drawPolyline( painter, QPolygonF( r ) );
+            painter->restore();
+
+            return;
+        }
+    }
+
+    painter->drawRect( r );
+}
+
+//! Wrapper for QPainter::fillRect()
+void QwtPainter::fillRect( QPainter *painter,
+    const QRectF &rect, const QBrush &brush )
+{
+    if ( !rect.isValid() )
+        return;
+
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    /*
+      Performance of Qt4 is horrible for a non trivial brush. Without
+      clipping expect minutes or hours for repainting large rectangles
+      (might result from zooming)
+    */
+
+    if ( deviceClipping )
+        clipRect &= painter->window();
+    else
+        clipRect = painter->window();
+
+    if ( painter->hasClipping() )
+        clipRect &= painter->clipRegion().boundingRect();
+
+    QRectF r = rect;
+    if ( deviceClipping )
+        r = r.intersected( clipRect );
+
+    if ( r.isValid() )
+        painter->fillRect( r, brush );
+}
+
+//! Wrapper for QPainter::drawPie()
+void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
+    int a, int alen )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+    if ( deviceClipping && !clipRect.contains( rect ) )
+        return;
+
+    painter->drawPie( rect, a, alen );
+}
+
+//! Wrapper for QPainter::drawEllipse()
+void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping && !clipRect.contains( rect ) )
+        return;
+
+    painter->drawEllipse( rect );
+}
+
+//! Wrapper for QPainter::drawText()
+void QwtPainter::drawText( QPainter *painter, double x, double y,
+        const QString &text )
+{
+    drawText( painter, QPointF( x, y ), text );
+}
+
+//! Wrapper for QPainter::drawText()
+void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
+        const QString &text )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping && !clipRect.contains( pos ) )
+        return;
+
+
+    painter->save();
+    qwtUnscaleFont( painter );
+    painter->drawText( pos, text );
+    painter->restore();
+}
+
+//! Wrapper for QPainter::drawText()
+void QwtPainter::drawText( QPainter *painter,
+    double x, double y, double w, double h,
+    int flags, const QString &text )
+{
+    drawText( painter, QRectF( x, y, w, h ), flags, text );
+}
+
+//! Wrapper for QPainter::drawText()
+void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
+        int flags, const QString &text )
+{
+    painter->save();
+    qwtUnscaleFont( painter );
+    painter->drawText( rect, flags, text );
+    painter->restore();
+}
+
+#ifndef QT_NO_RICHTEXT
+
+/*!
+  Draw a text document into a rectangle
+
+  \param painter Painter
+  \param rect Traget rectangle
+  \param flags Alignments/Text flags, see QPainter::drawText()
+  \param text Text document
+*/
+void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
+    int flags, const QTextDocument &text )
+{
+    QTextDocument *txt = text.clone();
+
+    painter->save();
+
+    QRectF unscaledRect = rect;
+
+    if ( painter->font().pixelSize() < 0 )
+    {
+        const QSize res = qwtScreenResolution();
+
+        const QPaintDevice *pd = painter->device();
+        if ( pd->logicalDpiX() != res.width() ||
+            pd->logicalDpiY() != res.height() )
+        {
+            QTransform transform;
+            transform.scale( res.width() / double( pd->logicalDpiX() ),
+                res.height() / double( pd->logicalDpiY() ));
+
+            painter->setWorldTransform( transform, true );
+            unscaledRect = transform.inverted().mapRect(rect);
+        }
+    }  
+
+    txt->setDefaultFont( painter->font() );
+    txt->setPageSize( QSizeF( unscaledRect.width(), QWIDGETSIZE_MAX ) );
+
+    QAbstractTextDocumentLayout* layout = txt->documentLayout();
+
+    const double height = layout->documentSize().height();
+    double y = unscaledRect.y();
+    if ( flags & Qt::AlignBottom )
+        y += ( unscaledRect.height() - height );
+    else if ( flags & Qt::AlignVCenter )
+        y += ( unscaledRect.height() - height ) / 2;
+
+    QAbstractTextDocumentLayout::PaintContext context;
+    context.palette.setColor( QPalette::Text, painter->pen().color() );
+
+    painter->translate( unscaledRect.x(), y );
+    layout->draw( painter, context );
+
+    painter->restore();
+    delete txt;
+}
+
+#endif // !QT_NO_RICHTEXT
+
+
+//! Wrapper for QPainter::drawLine()
+void QwtPainter::drawLine( QPainter *painter,
+    const QPointF &p1, const QPointF &p2 )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping &&
+        !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
+    {
+        QPolygonF polygon;
+        polygon += p1;
+        polygon += p2;
+        drawPolyline( painter, polygon );
+        return;
+    }
+
+    painter->drawLine( p1, p2 );
+}
+
+//! Wrapper for QPainter::drawPolygon()
+void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    QPolygonF cpa = polygon;
+    if ( deviceClipping )
+        cpa = QwtClipper::clipPolygonF( clipRect, polygon );
+
+    painter->drawPolygon( cpa );
+}
+
+//! Wrapper for QPainter::drawPolyline()
+void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    QPolygonF cpa = polygon;
+    if ( deviceClipping )
+        cpa = QwtClipper::clipPolygonF( clipRect, cpa );
+
+    qwtDrawPolyline<QPointF>( painter,
+        cpa.constData(), cpa.size(), d_polylineSplitting );
+}
+
+//! Wrapper for QPainter::drawPolyline()
+void QwtPainter::drawPolyline( QPainter *painter,
+    const QPointF *points, int pointCount )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        QPolygonF polygon( pointCount );
+        ::memcpy( polygon.data(), points, pointCount * sizeof( QPointF ) );
+
+        polygon = QwtClipper::clipPolygonF( clipRect, polygon );
+        qwtDrawPolyline<QPointF>( painter,
+            polygon.constData(), polygon.size(), d_polylineSplitting );
+    }
+    else
+    {
+        qwtDrawPolyline<QPointF>( painter, points, pointCount, d_polylineSplitting );
+    }
+}
+
+//! Wrapper for QPainter::drawPolygon()
+void QwtPainter::drawPolygon( QPainter *painter, const QPolygon &polygon )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    QPolygon cpa = polygon;
+    if ( deviceClipping )
+        cpa = QwtClipper::clipPolygon( clipRect, polygon );
+
+    painter->drawPolygon( cpa );
+}
+
+//! Wrapper for QPainter::drawPolyline()
+void QwtPainter::drawPolyline( QPainter *painter, const QPolygon &polygon )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    QPolygon cpa = polygon;
+    if ( deviceClipping )
+        cpa = QwtClipper::clipPolygon( clipRect, cpa );
+
+    qwtDrawPolyline<QPoint>( painter,
+        cpa.constData(), cpa.size(), d_polylineSplitting );
+}
+
+//! Wrapper for QPainter::drawPolyline()
+void QwtPainter::drawPolyline( QPainter *painter,
+    const QPoint *points, int pointCount )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        QPolygon polygon( pointCount );
+        ::memcpy( polygon.data(), points, pointCount * sizeof( QPoint ) );
+
+        polygon = QwtClipper::clipPolygon( clipRect, polygon );
+        qwtDrawPolyline<QPoint>( painter,
+            polygon.constData(), polygon.size(), d_polylineSplitting );
+    }
+    else
+        qwtDrawPolyline<QPoint>( painter, points, pointCount, d_polylineSplitting );
+}
+
+//! Wrapper for QPainter::drawPoint()
+void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping && !clipRect.contains( pos ) )
+        return;
+
+    painter->drawPoint( pos );
+}
+
+//! Wrapper for QPainter::drawPoint()
+void QwtPainter::drawPoint( QPainter *painter, const QPoint &pos )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        const int minX = qCeil( clipRect.left() );
+        const int maxX = qFloor( clipRect.right() );
+        const int minY = qCeil( clipRect.top() );
+        const int maxY = qFloor( clipRect.bottom() );
+
+        if ( pos.x() < minX || pos.x() > maxX 
+            || pos.y() < minY || pos.y() > maxY )
+        {
+            return;
+        }
+    }
+
+    painter->drawPoint( pos );
+}
+
+//! Wrapper for QPainter::drawPoints()
+void QwtPainter::drawPoints( QPainter *painter, 
+    const QPoint *points, int pointCount )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        const int minX = qCeil( clipRect.left() );
+        const int maxX = qFloor( clipRect.right() );
+        const int minY = qCeil( clipRect.top() );
+        const int maxY = qFloor( clipRect.bottom() );
+
+        const QRect r( minX, minY, maxX - minX, maxY - minY );
+
+        QPolygon clippedPolygon( pointCount );
+        QPoint *clippedData = clippedPolygon.data();
+
+        int numClippedPoints = 0;
+        for ( int i = 0; i < pointCount; i++ )
+        {
+            if ( r.contains( points[i] ) )
+                clippedData[ numClippedPoints++ ] = points[i];
+        }
+        painter->drawPoints( clippedData, numClippedPoints );
+    }
+    else
+    {
+        painter->drawPoints( points, pointCount );
+    }
+}
+
+//! Wrapper for QPainter::drawPoints()
+void QwtPainter::drawPoints( QPainter *painter, 
+    const QPointF *points, int pointCount )
+{
+    QRectF clipRect;
+    const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
+
+    if ( deviceClipping )
+    {
+        QPolygonF clippedPolygon( pointCount );
+        QPointF *clippedData = clippedPolygon.data();
+
+        int numClippedPoints = 0;
+        for ( int i = 0; i < pointCount; i++ )
+        {
+            if ( clipRect.contains( points[i] ) )
+                clippedData[ numClippedPoints++ ] = points[i];
+        }
+        painter->drawPoints( clippedData, numClippedPoints );
+    }
+    else
+    {
+        painter->drawPoints( points, pointCount );
+    }
+}
+
+//! Wrapper for QPainter::drawImage()
+void QwtPainter::drawImage( QPainter *painter,
+    const QRectF &rect, const QImage &image )
+{
+    const QRect alignedRect = rect.toAlignedRect();
+
+    if ( alignedRect != rect )
+    {
+        const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+
+        painter->save();
+        painter->setClipRect( clipRect, Qt::IntersectClip );
+        painter->drawImage( alignedRect, image );
+        painter->restore();
+    }
+    else
+    {
+        painter->drawImage( alignedRect, image );
+    }
+}
+
+//! Wrapper for QPainter::drawPixmap()
+void QwtPainter::drawPixmap( QPainter *painter,
+    const QRectF &rect, const QPixmap &pixmap )
+{
+    const QRect alignedRect = rect.toAlignedRect();
+
+    if ( alignedRect != rect )
+    {
+        const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+
+        painter->save();
+        painter->setClipRect( clipRect, Qt::IntersectClip );
+        painter->drawPixmap( alignedRect, pixmap );
+        painter->restore();
+    }
+    else
+    {
+        painter->drawPixmap( alignedRect, pixmap );
+    }
+}
+
+//! Draw a focus rectangle on a widget using its style.
+void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget )
+{
+    drawFocusRect( painter, widget, widget->rect() );
+}
+
+//! Draw a focus rectangle on a widget using its style.
+void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget,
+    const QRect &rect )
+{
+    QStyleOptionFocusRect opt;
+    opt.init( widget );
+    opt.rect = rect;
+    opt.state |= QStyle::State_HasFocus;
+
+    widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect,
+        &opt, painter, widget );
+}
+
+/*!
+  Draw a round frame 
+
+  \param painter Painter
+  \param rect Frame rectangle
+  \param palette QPalette::WindowText is used for plain borders
+                 QPalette::Dark and QPalette::Light for raised
+                 or sunken borders
+  \param lineWidth Line width
+  \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
+*/
+void QwtPainter::drawRoundFrame( QPainter *painter,
+    const QRectF &rect, const QPalette &palette, 
+    int lineWidth, int frameStyle )
+{
+    enum Style
+    {
+        Plain,
+        Sunken,
+        Raised
+    };
+
+    Style style = Plain;
+    if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
+        style = Sunken;
+    else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
+        style = Raised;
+
+    const double lw2 = 0.5 * lineWidth;
+    QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
+
+    QBrush brush;
+
+    if ( style != Plain )
+    {
+        QColor c1 = palette.color( QPalette::Light );
+        QColor c2 = palette.color( QPalette::Dark );
+
+        if ( style == Sunken )
+            qSwap( c1, c2 );
+
+        QLinearGradient gradient( r.topLeft(), r.bottomRight() );
+        gradient.setColorAt( 0.0, c1 );
+#if 0
+        gradient.setColorAt( 0.3, c1 );
+        gradient.setColorAt( 0.7, c2 );
+#endif
+        gradient.setColorAt( 1.0, c2 );
+
+        brush = QBrush( gradient );
+    }
+    else // Plain
+    {
+        brush = palette.brush( QPalette::WindowText );
+    }
+
+    painter->save();
+
+    painter->setPen( QPen( brush, lineWidth ) );
+    painter->setBrush( Qt::NoBrush );
+
+    painter->drawEllipse( r );
+
+    painter->restore();
+}
+
+/*!
+  Draw a rectangular frame
+
+  \param painter Painter
+  \param rect Frame rectangle
+  \param palette Palette
+  \param foregroundRole Foreground role used for QFrame::Plain
+  \param frameWidth Frame width
+  \param midLineWidth Used for QFrame::Box
+  \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
+*/
+void QwtPainter::drawFrame( QPainter *painter, const QRectF &rect,
+    const QPalette &palette, QPalette::ColorRole foregroundRole,
+    int frameWidth, int midLineWidth, int frameStyle )
+{
+    if ( frameWidth <= 0 || rect.isEmpty() )
+        return;
+
+    const int shadow = frameStyle & QFrame::Shadow_Mask;
+
+    painter->save();
+
+    if ( shadow == QFrame::Plain )
+    {
+        const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+        const QRectF innerRect = outerRect.adjusted( 
+            frameWidth, frameWidth, -frameWidth, -frameWidth );
+
+        QPainterPath path;
+        path.addRect( outerRect );
+        path.addRect( innerRect );
+
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( palette.color( foregroundRole ) );
+
+        painter->drawPath( path );
+    }
+    else
+    {
+        const int shape = frameStyle & QFrame::Shape_Mask;
+
+        if ( shape == QFrame::Box )
+        {
+            const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+            const QRectF midRect1 = outerRect.adjusted( 
+                frameWidth, frameWidth, -frameWidth, -frameWidth );
+            const QRectF midRect2 = midRect1.adjusted( 
+                midLineWidth, midLineWidth, -midLineWidth, -midLineWidth );
+
+            const QRectF innerRect = midRect2.adjusted( 
+                frameWidth, frameWidth, -frameWidth, -frameWidth );
+
+            QPainterPath path1;
+            path1.moveTo( outerRect.bottomLeft() );
+            path1.lineTo( outerRect.topLeft() );
+            path1.lineTo( outerRect.topRight() );
+            path1.lineTo( midRect1.topRight() );
+            path1.lineTo( midRect1.topLeft() );
+            path1.lineTo( midRect1.bottomLeft() );
+
+            QPainterPath path2;
+            path2.moveTo( outerRect.bottomLeft() );
+            path2.lineTo( outerRect.bottomRight() );
+            path2.lineTo( outerRect.topRight() );
+            path2.lineTo( midRect1.topRight() );
+            path2.lineTo( midRect1.bottomRight() );
+            path2.lineTo( midRect1.bottomLeft() );
+
+            QPainterPath path3;
+            path3.moveTo( midRect2.bottomLeft() );
+            path3.lineTo( midRect2.topLeft() );
+            path3.lineTo( midRect2.topRight() );
+            path3.lineTo( innerRect.topRight() );
+            path3.lineTo( innerRect.topLeft() );
+            path3.lineTo( innerRect.bottomLeft() );
+
+            QPainterPath path4;
+            path4.moveTo( midRect2.bottomLeft() );
+            path4.lineTo( midRect2.bottomRight() );
+            path4.lineTo( midRect2.topRight() );
+            path4.lineTo( innerRect.topRight() );
+            path4.lineTo( innerRect.bottomRight() );
+            path4.lineTo( innerRect.bottomLeft() );
+
+            QPainterPath path5;
+            path5.addRect( midRect1 );
+            path5.addRect( midRect2 );
+
+            painter->setPen( Qt::NoPen );
+
+            QBrush brush1 = palette.dark().color();
+            QBrush brush2 = palette.light().color();
+
+            if ( shadow == QFrame::Raised )
+                qSwap( brush1, brush2 );
+
+            painter->setBrush( brush1 );
+            painter->drawPath( path1 );
+            painter->drawPath( path4 );
+
+            painter->setBrush( brush2 );
+            painter->drawPath( path2 );
+            painter->drawPath( path3 );
+
+            painter->setBrush( palette.mid() );
+            painter->drawPath( path5 );
+        }
+#if 0
+        // qDrawWinPanel doesn't result in something nice
+        // on a scalable document like PDF. Better draw a
+        // Panel.
+
+        else if ( shape == QFrame::WinPanel )
+        {
+            painter->setRenderHint( QPainter::NonCosmeticDefaultPen, true );
+            qDrawWinPanel ( painter, rect.toRect(), palette,
+                frameStyle & QFrame::Sunken );
+        }
+        else if ( shape == QFrame::StyledPanel )
+        {
+        }
+#endif
+        else
+        {
+            const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+            const QRectF innerRect = outerRect.adjusted( 
+                frameWidth - 1.0, frameWidth - 1.0, 
+                -( frameWidth - 1.0 ), -( frameWidth - 1.0 ) );
+
+            QPainterPath path1;
+            path1.moveTo( outerRect.bottomLeft() );
+            path1.lineTo( outerRect.topLeft() );
+            path1.lineTo( outerRect.topRight() );
+            path1.lineTo( innerRect.topRight() );
+            path1.lineTo( innerRect.topLeft() );
+            path1.lineTo( innerRect.bottomLeft() );
+
+
+            QPainterPath path2;
+            path2.moveTo( outerRect.bottomLeft() );
+            path2.lineTo( outerRect.bottomRight() );
+            path2.lineTo( outerRect.topRight() );
+            path2.lineTo( innerRect.topRight() );
+            path2.lineTo( innerRect.bottomRight() );
+            path2.lineTo( innerRect.bottomLeft() );
+
+            painter->setPen( Qt::NoPen );
+
+            QBrush brush1 = palette.dark().color();
+            QBrush brush2 = palette.light().color();
+
+            if ( shadow == QFrame::Raised )
+                qSwap( brush1, brush2 );
+
+            painter->setBrush( brush1 );
+            painter->drawPath( path1 );
+
+            painter->setBrush( brush2 );
+            painter->drawPath( path2 );
+        }
+
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw a rectangular frame with rounded borders
+
+  \param painter Painter
+  \param rect Frame rectangle
+  \param xRadius x-radius of the ellipses defining the corners
+  \param yRadius y-radius of the ellipses defining the corners
+  \param palette QPalette::WindowText is used for plain borders
+                 QPalette::Dark and QPalette::Light for raised
+                 or sunken borders
+  \param lineWidth Line width
+  \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
+*/
+
+void QwtPainter::drawRoundedFrame( QPainter *painter, 
+    const QRectF &rect, double xRadius, double yRadius, 
+    const QPalette &palette, int lineWidth, int frameStyle )
+{
+    painter->save();
+    painter->setRenderHint( QPainter::Antialiasing, true );
+    painter->setBrush( Qt::NoBrush );
+
+    double lw2 = lineWidth * 0.5;
+    QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
+
+    QPainterPath path;
+    path.addRoundedRect( r, xRadius, yRadius );
+
+    enum Style
+    {
+        Plain,
+        Sunken,
+        Raised
+    };
+
+    Style style = Plain;
+    if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
+        style = Sunken;
+    else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
+        style = Raised;
+
+    if ( style != Plain && path.elementCount() == 17 )
+    {
+        // move + 4 * ( cubicTo + lineTo )
+        QPainterPath pathList[8];
+        
+        for ( int i = 0; i < 4; i++ )
+        {
+            const int j = i * 4 + 1;
+            
+            pathList[ 2 * i ].moveTo(
+                path.elementAt(j - 1).x, path.elementAt( j - 1 ).y
+            );  
+            
+            pathList[ 2 * i ].cubicTo(
+                path.elementAt(j + 0).x, path.elementAt(j + 0).y,
+                path.elementAt(j + 1).x, path.elementAt(j + 1).y,
+                path.elementAt(j + 2).x, path.elementAt(j + 2).y );
+                
+            pathList[ 2 * i + 1 ].moveTo(
+                path.elementAt(j + 2).x, path.elementAt(j + 2).y
+            );  
+            pathList[ 2 * i + 1 ].lineTo(
+                path.elementAt(j + 3).x, path.elementAt(j + 3).y
+            );  
+        }   
+
+        QColor c1( palette.color( QPalette::Dark ) );
+        QColor c2( palette.color( QPalette::Light ) );
+
+        if ( style == Raised )
+            qSwap( c1, c2 );
+
+        for ( int i = 0; i < 4; i++ )
+        {
+            QRectF r = pathList[2 * i].controlPointRect();
+
+            QPen arcPen;
+            arcPen.setCapStyle( Qt::FlatCap );
+            arcPen.setWidth( lineWidth );
+
+            QPen linePen;
+            linePen.setCapStyle( Qt::FlatCap );
+            linePen.setWidth( lineWidth );
+
+            switch( i )
+            {
+                case 0:
+                {
+                    arcPen.setColor( c1 );
+                    linePen.setColor( c1 );
+                    break;
+                }
+                case 1:
+                {
+                    QLinearGradient gradient;
+                    gradient.setStart( r.topLeft() );
+                    gradient.setFinalStop( r.bottomRight() );
+                    gradient.setColorAt( 0.0, c1 );
+                    gradient.setColorAt( 1.0, c2 );
+
+                    arcPen.setBrush( gradient );
+                    linePen.setColor( c2 );
+                    break;
+                }
+                case 2:
+                {
+                    arcPen.setColor( c2 );
+                    linePen.setColor( c2 );
+                    break;
+                }
+                case 3:
+                {
+                    QLinearGradient gradient;
+
+                    gradient.setStart( r.bottomRight() );
+                    gradient.setFinalStop( r.topLeft() );
+                    gradient.setColorAt( 0.0, c2 );
+                    gradient.setColorAt( 1.0, c1 );
+
+                    arcPen.setBrush( gradient );
+                    linePen.setColor( c1 );
+                    break;
+                }
+            }
+
+
+            painter->setPen( arcPen );
+            painter->drawPath( pathList[ 2 * i] );
+
+            painter->setPen( linePen );
+            painter->drawPath( pathList[ 2 * i + 1] );
+        }
+    }
+    else
+    {
+        QPen pen( palette.color( QPalette::WindowText ), lineWidth );
+        painter->setPen( pen );
+        painter->drawPath( path );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw a color bar into a rectangle
+
+  \param painter Painter
+  \param colorMap Color map
+  \param interval Value range
+  \param scaleMap Scale map
+  \param orientation Orientation
+  \param rect Traget rectangle
+*/
+void QwtPainter::drawColorBar( QPainter *painter,
+        const QwtColorMap &colorMap, const QwtInterval &interval,
+        const QwtScaleMap &scaleMap, Qt::Orientation orientation,
+        const QRectF &rect )
+{
+    QVector<QRgb> colorTable;
+    if ( colorMap.format() == QwtColorMap::Indexed )
+        colorTable = colorMap.colorTable( interval );
+
+    QColor c;
+
+    const QRect devRect = rect.toAlignedRect();
+
+    /*
+      We paint to a pixmap first to have something scalable for printing
+      ( f.e. in a Pdf document )
+     */
+
+    QPixmap pixmap( devRect.size() );
+    pixmap.fill( Qt::transparent );
+
+    QPainter pmPainter( &pixmap );
+    pmPainter.translate( -devRect.x(), -devRect.y() );
+
+    if ( orientation == Qt::Horizontal )
+    {
+        QwtScaleMap sMap = scaleMap;
+        sMap.setPaintInterval( rect.left(), rect.right() );
+
+        for ( int x = devRect.left(); x <= devRect.right(); x++ )
+        {
+            const double value = sMap.invTransform( x );
+
+            if ( colorMap.format() == QwtColorMap::RGB )
+                c.setRgba( colorMap.rgb( interval, value ) );
+            else
+                c = colorTable[colorMap.colorIndex( interval, value )];
+
+            pmPainter.setPen( c );
+            pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
+        }
+    }
+    else // Vertical
+    {
+        QwtScaleMap sMap = scaleMap;
+        sMap.setPaintInterval( rect.bottom(), rect.top() );
+
+        for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
+        {
+            const double value = sMap.invTransform( y );
+
+            if ( colorMap.format() == QwtColorMap::RGB )
+                c.setRgba( colorMap.rgb( interval, value ) );
+            else
+                c = colorTable[colorMap.colorIndex( interval, value )];
+
+            pmPainter.setPen( c );
+            pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
+        }
+    }
+    pmPainter.end();
+
+    drawPixmap( painter, rect, pixmap );
+}
+
+static inline void qwtFillRect( const QWidget *widget, QPainter *painter, 
+    const QRect &rect, const QBrush &brush)
+{
+    if ( brush.style() == Qt::TexturePattern ) 
+    {
+        painter->save();
+
+        painter->setClipRect( rect );
+        painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
+
+        painter->restore();
+    } 
+    else if ( brush.gradient() )
+    {
+        painter->save();
+
+        painter->setClipRect( rect );
+        painter->fillRect(0, 0, widget->width(), 
+            widget->height(), brush);
+
+        painter->restore();
+    } 
+    else 
+    {
+        painter->fillRect(rect, brush);
+    }
+}
+
+/*!
+  Fill a pixmap with the content of a widget
+
+  In Qt >= 5.0 QPixmap::fill() is a nop, in Qt 4.x it is buggy
+  for backgrounds with gradients. Thus fillPixmap() offers 
+  an alternative implementation.
+
+  \param widget Widget
+  \param pixmap Pixmap to be filled
+  \param offset Offset 
+
+  \sa QPixmap::fill()
+ */
+void QwtPainter::fillPixmap( const QWidget *widget, 
+    QPixmap &pixmap, const QPoint &offset )
+{
+    const QRect rect( offset, pixmap.size() );
+
+    QPainter painter( &pixmap );
+    painter.translate( -offset );
+
+    const QBrush autoFillBrush = 
+        widget->palette().brush( widget->backgroundRole() );
+
+    if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) ) 
+    {
+        const QBrush bg = widget->palette().brush( QPalette::Window );
+        qwtFillRect( widget, &painter, rect, bg);
+    }
+
+    if ( widget->autoFillBackground() )
+        qwtFillRect( widget, &painter, rect, autoFillBrush);
+
+    if ( widget->testAttribute(Qt::WA_StyledBackground) ) 
+    {
+        painter.setClipRegion( rect );
+
+        QStyleOption opt;
+        opt.initFrom( widget );
+        widget->style()->drawPrimitive( QStyle::PE_Widget, 
+            &opt, &painter, widget );
+    }
+}
+
+/*!
+  Fill rect with the background of a widget
+
+  \param painter Painter
+  \param rect Rectangle to be filled
+  \param widget Widget
+
+  \sa QStyle::PE_Widget, QWidget::backgroundRole()
+ */
+void QwtPainter::drawBackgound( QPainter *painter,
+    const QRectF &rect, const QWidget *widget )
+{
+    if ( widget->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        QStyleOption opt;
+        opt.initFrom( widget );
+        opt.rect = rect.toAlignedRect();
+
+        widget->style()->drawPrimitive(
+            QStyle::PE_Widget, &opt, painter, widget);
+    }
+    else
+    {
+        const QBrush brush =
+            widget->palette().brush( widget->backgroundRole() );
+
+        painter->fillRect( rect, brush );
+    }
+}
+
+/*!
+  \return A pixmap that can be used as backing store
+
+  \param widget Widget, for which the backinstore is intended
+  \param size Size of the pixmap
+ */
+QPixmap QwtPainter::backingStore( QWidget *widget, const QSize &size )
+{
+    QPixmap pm;
+
+#define QWT_HIGH_DPI 1
+
+#if QT_VERSION >= 0x050000 && QWT_HIGH_DPI
+    qreal pixelRatio = 1.0;
+
+    if ( widget && widget->windowHandle() )
+    {
+#if QT_VERSION < 0x050100
+        pixelRatio = widget->windowHandle()->devicePixelRatio();
+#else
+        pixelRatio = widget->devicePixelRatio();
+#endif
+    }
+    else
+    {
+        if ( qApp )
+            pixelRatio = qApp->devicePixelRatio();
+    }
+
+    pm = QPixmap( size * pixelRatio );
+    pm.setDevicePixelRatio( pixelRatio );
+#else
+    Q_UNUSED( widget )
+    pm = QPixmap( size );
+#endif
+
+#if QT_VERSION < 0x050000 
+#ifdef Q_WS_X11
+    if ( widget && isX11GraphicsSystem() )
+    {
+        if ( pm.x11Info().screen() != widget->x11Info().screen() )
+            pm.x11SetScreen( widget->x11Info().screen() );
+    }
+#endif
+#endif
+
+    return pm;
+}
+
diff --git a/source/third_party/qwt/qwt_painter_command.cpp b/source/third_party/qwt/qwt_painter_command.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..951f4681868968eca75f75cda663fa424f0916d7
--- /dev/null
+++ b/source/third_party/qwt/qwt_painter_command.cpp
@@ -0,0 +1,237 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_painter_command.h"
+
+//! Construct an invalid command
+QwtPainterCommand::QwtPainterCommand():
+    d_type( Invalid )
+{
+}
+
+//! Copy constructor
+QwtPainterCommand::QwtPainterCommand( const QPainterPath &path ):
+    d_type( Path )
+{
+    d_path = new QPainterPath( path );
+}
+
+/*!
+  Constructor for Pixmap paint operation
+
+  \param rect Target rectangle
+  \param pixmap Pixmap
+  \param subRect Rectangle inside the pixmap
+
+  \sa QPainter::drawPixmap()
+ */
+QwtPainterCommand::QwtPainterCommand( const QRectF &rect,
+        const QPixmap &pixmap, const QRectF& subRect ):
+    d_type( Pixmap )
+{
+    d_pixmapData = new PixmapData();
+    d_pixmapData->rect = rect;
+    d_pixmapData->pixmap = pixmap;
+    d_pixmapData->subRect = subRect;
+}
+
+/*!
+  Constructor for Image paint operation
+
+  \param rect Target rectangle
+  \param image Image
+  \param subRect Rectangle inside the image
+  \param flags Conversion flags
+
+  \sa QPainter::drawImage()
+ */
+QwtPainterCommand::QwtPainterCommand( const QRectF &rect,
+        const QImage &image, const QRectF& subRect,
+        Qt::ImageConversionFlags flags ):
+    d_type( Image )
+{
+    d_imageData = new ImageData();
+    d_imageData->rect = rect;
+    d_imageData->image = image;
+    d_imageData->subRect = subRect;
+    d_imageData->flags = flags;
+}
+
+/*! 
+  Constructor for State paint operation
+  \param state Paint engine state
+ */  
+QwtPainterCommand::QwtPainterCommand( const QPaintEngineState &state ):
+    d_type( State )
+{
+    d_stateData = new StateData();
+
+    d_stateData->flags = state.state();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyPen ) 
+        d_stateData->pen = state.pen();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyBrush ) 
+        d_stateData->brush = state.brush();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyBrushOrigin ) 
+        d_stateData->brushOrigin = state.brushOrigin();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyFont ) 
+        d_stateData->font = state.font();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyBackground ) 
+    {
+        d_stateData->backgroundMode = state.backgroundMode();
+        d_stateData->backgroundBrush = state.backgroundBrush();
+    }
+
+    if ( d_stateData->flags & QPaintEngine::DirtyTransform ) 
+        d_stateData->transform = state.transform();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyClipEnabled ) 
+        d_stateData->isClipEnabled = state.isClipEnabled();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyClipRegion ) 
+    {
+        d_stateData->clipRegion = state.clipRegion();
+        d_stateData->clipOperation = state.clipOperation();
+    }
+
+    if ( d_stateData->flags & QPaintEngine::DirtyClipPath ) 
+    {
+        d_stateData->clipPath = state.clipPath();
+        d_stateData->clipOperation = state.clipOperation();
+    }
+
+    if ( d_stateData->flags & QPaintEngine::DirtyHints ) 
+        d_stateData->renderHints = state.renderHints();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyCompositionMode ) 
+        d_stateData->compositionMode = state.compositionMode();
+
+    if ( d_stateData->flags & QPaintEngine::DirtyOpacity ) 
+        d_stateData->opacity = state.opacity();
+}
+
+/*!
+  Copy constructor
+  \param other Command to be copied
+  
+ */
+QwtPainterCommand::QwtPainterCommand(const QwtPainterCommand &other)
+{
+    copy( other );
+}
+
+//! Destructor
+QwtPainterCommand::~QwtPainterCommand()
+{
+    reset();
+}
+
+/*!
+  Assignment operator
+
+  \param other Command to be copied
+  \return Modified command
+ */
+QwtPainterCommand &QwtPainterCommand::operator=(const QwtPainterCommand &other)
+{
+    reset();
+    copy( other );
+
+    return *this;
+}
+
+void QwtPainterCommand::copy( const QwtPainterCommand &other )
+{
+    d_type = other.d_type;
+
+    switch( other.d_type )
+    {
+        case Path:
+        {
+            d_path = new QPainterPath( *other.d_path );
+            break;
+        }
+        case Pixmap:
+        {
+            d_pixmapData = new PixmapData( *other.d_pixmapData );
+            break;
+        }
+        case Image:
+        {
+            d_imageData = new ImageData( *other.d_imageData );
+            break;
+        }
+        case State:
+        {
+            d_stateData = new StateData( *other.d_stateData );
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+void QwtPainterCommand::reset()
+{
+    switch( d_type )
+    {
+        case Path:
+        {
+            delete d_path;
+            break;
+        }
+        case Pixmap:
+        {
+            delete d_pixmapData;
+            break;
+        }
+        case Image:
+        {
+            delete d_imageData;
+            break;
+        }
+        case State:
+        {
+            delete d_stateData;
+            break;
+        }
+        default:
+            break;
+    }
+
+    d_type = Invalid;
+}
+
+//! \return Painter path to be painted
+QPainterPath *QwtPainterCommand::path() 
+{
+    return d_path;
+}
+
+//! \return Attributes how to paint a QPixmap
+QwtPainterCommand::PixmapData* QwtPainterCommand::pixmapData() 
+{
+    return d_pixmapData;
+}
+
+//! \return Attributes how to paint a QImage
+QwtPainterCommand::ImageData* QwtPainterCommand::imageData() 
+{
+    return d_imageData;
+}
+
+//! \return Attributes of a state change
+QwtPainterCommand::StateData* QwtPainterCommand::stateData() 
+{
+    return d_stateData;
+}
diff --git a/source/third_party/qwt/qwt_panner.cpp b/source/third_party/qwt/qwt_panner.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..61205f16c481c35649e01d7fdac189a740dc7ecb
--- /dev/null
+++ b/source/third_party/qwt/qwt_panner.cpp
@@ -0,0 +1,538 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_panner.h"
+#include "qwt/qwt_picker.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <qcursor.h>
+#include <qbitmap.h>
+
+static QVector<QwtPicker *> qwtActivePickers( QWidget *w )
+{
+    QVector<QwtPicker *> pickers;
+
+    QObjectList children = w->children();
+    for ( int i = 0; i < children.size(); i++ )
+    {
+        QwtPicker *picker = qobject_cast<QwtPicker *>( children[i] );
+        if ( picker && picker->isEnabled() )
+            pickers += picker;
+    }
+
+    return pickers;
+}
+
+class QwtPanner::PrivateData
+{
+public:
+    PrivateData():
+        button( Qt::LeftButton ),
+        buttonModifiers( Qt::NoModifier ),
+        abortKey( Qt::Key_Escape ),
+        abortKeyModifiers( Qt::NoModifier ),
+#ifndef QT_NO_CURSOR
+        cursor( NULL ),
+        restoreCursor( NULL ),
+        hasCursor( false ),
+#endif
+        isEnabled( false )
+    {
+        orientations = Qt::Vertical | Qt::Horizontal;
+    }
+
+    ~PrivateData()
+    {
+#ifndef QT_NO_CURSOR
+        delete cursor;
+        delete restoreCursor;
+#endif
+    }
+
+    Qt::MouseButton button;
+    Qt::KeyboardModifiers  buttonModifiers;
+
+    int abortKey;
+    Qt::KeyboardModifiers abortKeyModifiers;
+
+    QPoint initialPos;
+    QPoint pos;
+
+    QPixmap pixmap;
+    QBitmap contentsMask;
+
+#ifndef QT_NO_CURSOR
+    QCursor *cursor;
+    QCursor *restoreCursor;
+    bool hasCursor;
+#endif
+    bool isEnabled;
+    Qt::Orientations orientations;
+};
+
+/*!
+  Creates an panner that is enabled for the left mouse button.
+
+  \param parent Parent widget to be panned
+*/
+QwtPanner::QwtPanner( QWidget *parent ):
+    QWidget( parent )
+{
+    d_data = new PrivateData();
+
+    setAttribute( Qt::WA_TransparentForMouseEvents );
+    setAttribute( Qt::WA_NoSystemBackground );
+    setFocusPolicy( Qt::NoFocus );
+    hide();
+
+    setEnabled( true );
+}
+
+//! Destructor
+QwtPanner::~QwtPanner()
+{
+    delete d_data;
+}
+
+/*!
+   Change the mouse button and modifiers used for panning
+   The defaults are Qt::LeftButton and Qt::NoModifier
+*/
+void QwtPanner::setMouseButton( Qt::MouseButton button,
+    Qt::KeyboardModifiers modifiers )
+{
+    d_data->button = button;
+    d_data->buttonModifiers = modifiers;
+}
+
+//! Get mouse button and modifiers used for panning
+void QwtPanner::getMouseButton( Qt::MouseButton &button,
+    Qt::KeyboardModifiers &modifiers ) const
+{
+    button = d_data->button;
+    modifiers = d_data->buttonModifiers;
+}
+
+/*!
+   Change the abort key
+   The defaults are Qt::Key_Escape and Qt::NoModifiers
+
+   \param key Key ( See Qt::Keycode )
+   \param modifiers Keyboard modifiers
+*/
+void QwtPanner::setAbortKey( int key, 
+    Qt::KeyboardModifiers modifiers )
+{
+    d_data->abortKey = key;
+    d_data->abortKeyModifiers = modifiers;
+}
+
+//! Get the abort key and modifiers
+void QwtPanner::getAbortKey( int &key, 
+    Qt::KeyboardModifiers &modifiers ) const
+{
+    key = d_data->abortKey;
+    modifiers = d_data->abortKeyModifiers;
+}
+
+/*!
+   Change the cursor, that is active while panning
+   The default is the cursor of the parent widget.
+
+   \param cursor New cursor
+
+   \sa setCursor()
+*/
+#ifndef QT_NO_CURSOR
+void QwtPanner::setCursor( const QCursor &cursor )
+{
+    d_data->cursor = new QCursor( cursor );
+}
+#endif
+
+/*!
+   \return Cursor that is active while panning
+   \sa setCursor()
+*/
+#ifndef QT_NO_CURSOR
+const QCursor QwtPanner::cursor() const
+{
+    if ( d_data->cursor )
+        return *d_data->cursor;
+
+    if ( parentWidget() )
+        return parentWidget()->cursor();
+
+    return QCursor();
+}
+#endif
+
+/*!
+  \brief En/disable the panner
+
+  When enabled is true an event filter is installed for
+  the observed widget, otherwise the event filter is removed.
+
+  \param on true or false
+  \sa isEnabled(), eventFilter()
+*/
+void QwtPanner::setEnabled( bool on )
+{
+    if ( d_data->isEnabled != on )
+    {
+        d_data->isEnabled = on;
+
+        QWidget *w = parentWidget();
+        if ( w )
+        {
+            if ( d_data->isEnabled )
+            {
+                w->installEventFilter( this );
+            }
+            else
+            {
+                w->removeEventFilter( this );
+                hide();
+            }
+        }
+    }
+}
+
+/*!
+   Set the orientations, where panning is enabled
+   The default value is in both directions: Qt::Horizontal | Qt::Vertical
+
+   /param o Orientation
+*/
+void QwtPanner::setOrientations( Qt::Orientations o )
+{
+    d_data->orientations = o;
+}
+
+//! Return the orientation, where paning is enabled
+Qt::Orientations QwtPanner::orientations() const
+{
+    return d_data->orientations;
+}
+
+/*!
+   \return True if an orientation is enabled
+   \sa orientations(), setOrientations()
+*/
+bool QwtPanner::isOrientationEnabled( Qt::Orientation o ) const
+{
+    return d_data->orientations & o;
+}
+
+/*!
+  \return true when enabled, false otherwise
+  \sa setEnabled, eventFilter()
+*/
+bool QwtPanner::isEnabled() const
+{
+    return d_data->isEnabled;
+}
+
+/*!
+   \brief Paint event
+
+   Repaint the grabbed pixmap on its current position and
+   fill the empty spaces by the background of the parent widget.
+
+   \param pe Paint event
+*/
+void QwtPanner::paintEvent( QPaintEvent *pe )
+{
+    int dx = d_data->pos.x() - d_data->initialPos.x();
+    int dy = d_data->pos.y() - d_data->initialPos.y();
+
+    QRect r( 0, 0, d_data->pixmap.width(), d_data->pixmap.height() );
+    r.moveCenter( QPoint( r.center().x() + dx, r.center().y() + dy ) );
+
+    QPixmap pm( size() );
+    QwtPainter::fillPixmap( parentWidget(), pm );
+
+    QPainter painter( &pm );
+
+    if ( !d_data->contentsMask.isNull() )
+    {
+        QPixmap masked = d_data->pixmap;
+        masked.setMask( d_data->contentsMask );
+        painter.drawPixmap( r, masked );
+    }
+    else
+    {
+        painter.drawPixmap( r, d_data->pixmap );
+    }
+
+    painter.end();
+
+    if ( !d_data->contentsMask.isNull() )
+        pm.setMask( d_data->contentsMask );
+
+    painter.begin( this );
+    painter.setClipRegion( pe->region() );
+    painter.drawPixmap( 0, 0, pm );
+}
+
+/*!
+  \brief Calculate a mask for the contents of the panned widget
+
+  Sometimes only parts of the contents of a widget should be
+  panned. F.e. for a widget with a styled background with rounded borders
+  only the area inside of the border should be panned.
+
+  \return An empty bitmap, indicating no mask
+*/
+QBitmap QwtPanner::contentsMask() const
+{
+    return QBitmap();
+}
+
+/*!
+  Grab the widget into a pixmap.
+  \return Grabbed pixmap
+*/
+QPixmap QwtPanner::grab() const
+{
+#if QT_VERSION >= 0x050000
+    return parentWidget()->grab( parentWidget()->rect() );
+#else
+    return QPixmap::grabWidget( parentWidget() );
+#endif
+}
+
+/*!
+  \brief Event filter
+
+  When isEnabled() is true mouse events of the
+  observed widget are filtered.
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return Always false, beside for paint events for the
+          parent widget.
+
+  \sa widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseMoveEvent()
+*/
+bool QwtPanner::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object == NULL || object != parentWidget() )
+        return false;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
+            break;
+        }
+        case QEvent::MouseMove:
+        {
+            widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
+            break;
+        }
+        case QEvent::KeyRelease:
+        {
+            widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
+            break;
+        }
+        case QEvent::Paint:
+        {
+            if ( isVisible() )
+                return true;
+            break;
+        }
+        default:;
+    }
+
+    return false;
+}
+
+/*!
+  Handle a mouse press event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMouseReleaseEvent(),
+      widgetMouseMoveEvent(),
+*/
+void QwtPanner::widgetMousePressEvent( QMouseEvent *mouseEvent )
+{
+    if ( ( mouseEvent->button() != d_data->button )
+        || ( mouseEvent->modifiers() != d_data->buttonModifiers ) )
+    {
+        return;
+    }
+
+    QWidget *w = parentWidget();
+    if ( w == NULL )
+        return;
+
+#ifndef QT_NO_CURSOR
+    showCursor( true );
+#endif
+
+    d_data->initialPos = d_data->pos = mouseEvent->pos();
+
+    setGeometry( parentWidget()->rect() );
+
+    // We don't want to grab the picker !
+    QVector<QwtPicker *> pickers = qwtActivePickers( parentWidget() );
+    for ( int i = 0; i < pickers.size(); i++ )
+        pickers[i]->setEnabled( false );
+
+    d_data->pixmap = grab();
+    d_data->contentsMask = contentsMask();
+
+    for ( int i = 0; i < pickers.size(); i++ )
+        pickers[i]->setEnabled( true );
+
+    show();
+}
+
+/*!
+  Handle a mouse move event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent()
+*/
+void QwtPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
+{
+    if ( !isVisible() )
+        return;
+
+    QPoint pos = mouseEvent->pos();
+    if ( !isOrientationEnabled( Qt::Horizontal ) )
+        pos.setX( d_data->initialPos.x() );
+    if ( !isOrientationEnabled( Qt::Vertical ) )
+        pos.setY( d_data->initialPos.y() );
+
+    if ( pos != d_data->pos && rect().contains( pos ) )
+    {
+        d_data->pos = pos;
+        update();
+
+        Q_EMIT moved( d_data->pos.x() - d_data->initialPos.x(),
+            d_data->pos.y() - d_data->initialPos.y() );
+    }
+}
+
+/*!
+  Handle a mouse release event for the observed widget.
+
+  \param mouseEvent Mouse event
+  \sa eventFilter(), widgetMousePressEvent(),
+      widgetMouseMoveEvent(),
+*/
+void QwtPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
+{
+    if ( isVisible() )
+    {
+        hide();
+#ifndef QT_NO_CURSOR
+        showCursor( false );
+#endif
+
+        QPoint pos = mouseEvent->pos();
+        if ( !isOrientationEnabled( Qt::Horizontal ) )
+            pos.setX( d_data->initialPos.x() );
+        if ( !isOrientationEnabled( Qt::Vertical ) )
+            pos.setY( d_data->initialPos.y() );
+
+        d_data->pixmap = QPixmap();
+        d_data->contentsMask = QBitmap();
+        d_data->pos = pos;
+
+        if ( d_data->pos != d_data->initialPos )
+        {
+            Q_EMIT panned( d_data->pos.x() - d_data->initialPos.x(),
+                d_data->pos.y() - d_data->initialPos.y() );
+        }
+    }
+}
+
+/*!
+  Handle a key press event for the observed widget.
+
+  \param keyEvent Key event
+  \sa eventFilter(), widgetKeyReleaseEvent()
+*/
+void QwtPanner::widgetKeyPressEvent( QKeyEvent *keyEvent )
+{
+    if ( ( keyEvent->key() == d_data->abortKey )
+        && ( keyEvent->modifiers() == d_data->abortKeyModifiers ) )
+    {
+        hide();
+
+#ifndef QT_NO_CURSOR
+        showCursor( false );
+#endif
+        d_data->pixmap = QPixmap();
+    }
+}
+
+/*!
+  Handle a key release event for the observed widget.
+
+  \param keyEvent Key event
+  \sa eventFilter(), widgetKeyReleaseEvent()
+*/
+void QwtPanner::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
+{
+    Q_UNUSED( keyEvent );
+}
+
+#ifndef QT_NO_CURSOR
+void QwtPanner::showCursor( bool on )
+{
+    if ( on == d_data->hasCursor )
+        return;
+
+    QWidget *w = parentWidget();
+    if ( w == NULL || d_data->cursor == NULL )
+        return;
+
+    d_data->hasCursor = on;
+
+    if ( on )
+    {
+        if ( w->testAttribute( Qt::WA_SetCursor ) )
+        {
+            delete d_data->restoreCursor;
+            d_data->restoreCursor = new QCursor( w->cursor() );
+        }
+        w->setCursor( *d_data->cursor );
+    }
+    else
+    {
+        if ( d_data->restoreCursor )
+        {
+            w->setCursor( *d_data->restoreCursor );
+            delete d_data->restoreCursor;
+            d_data->restoreCursor = NULL;
+        }
+        else
+            w->unsetCursor();
+    }
+}
+#endif
diff --git a/source/third_party/qwt/qwt_picker.cpp b/source/third_party/qwt/qwt_picker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ae5ef13578ee96966c53dd6de11eefe396f3bc2
--- /dev/null
+++ b/source/third_party/qwt/qwt_picker.cpp
@@ -0,0 +1,1593 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_picker.h"
+#include "qwt/qwt_picker_machine.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_widget_overlay.h"
+#include <qapplication.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qframe.h>
+#include <qcursor.h>
+#include <qbitmap.h>
+#include <qpointer.h>
+#include <qpaintengine.h>
+#include <qmath.h>
+
+static inline QRegion qwtMaskRegion( const QRect &r, int penWidth )
+{
+    const int pw = qMax( penWidth, 1 );
+    const int pw2 = penWidth / 2;
+
+    int x1 = r.left() - pw2;
+    int x2 = r.right() + 1 + pw2 + ( pw % 2 );
+
+    int y1 = r.top() - pw2;
+    int y2 = r.bottom() + 1 + pw2 + ( pw % 2 );
+
+    QRegion region;
+
+    region += QRect( x1, y1, x2 - x1, pw );
+    region += QRect( x1, y1, pw, y2 - y1 );
+    region += QRect( x1, y2 - pw, x2 - x1, pw );
+    region += QRect( x2 - pw, y1, pw, y2 - y1 );
+
+    return region;
+}
+
+static inline QRegion qwtMaskRegion( const QLine &l, int penWidth )
+{
+    const int pw = qMax( penWidth, 1 );
+    const int pw2 = penWidth / 2;
+
+    QRegion region;
+
+    if ( l.x1() == l.x2() )
+    {
+        region += QRect( l.x1() - pw2, l.y1(), 
+            pw, l.y2() ).normalized();
+    }
+    else if ( l.y1() == l.y2() )
+    {
+        region += QRect( l.x1(), l.y1() - pw2, 
+            l.x2(), pw ).normalized();
+    }
+
+    return region;
+}
+
+class QwtPickerRubberband: public QwtWidgetOverlay
+{
+public:
+    QwtPickerRubberband( QwtPicker *, QWidget * );
+
+protected:
+    virtual void drawOverlay( QPainter * ) const;
+    virtual QRegion maskHint() const;
+
+    QwtPicker *d_picker;
+};
+
+class QwtPickerTracker: public QwtWidgetOverlay
+{                                  
+public:
+    QwtPickerTracker( QwtPicker *, QWidget * );
+    
+protected:
+    virtual void drawOverlay( QPainter * ) const;
+    virtual QRegion maskHint() const;
+    
+    QwtPicker *d_picker;
+};  
+
+
+class QwtPicker::PrivateData
+{
+public:
+    PrivateData():
+        enabled( false ),
+        stateMachine( NULL ),
+        resizeMode( QwtPicker::Stretch ),
+        rubberBand( QwtPicker::NoRubberBand ),
+        trackerMode( QwtPicker::AlwaysOff ),
+        isActive( false ),
+        trackerPosition( -1, -1 ),
+        mouseTracking( false ),
+        openGL( false )
+    {
+    }
+        
+    bool enabled;
+
+    QwtPickerMachine *stateMachine;
+
+    QwtPicker::ResizeMode resizeMode;
+
+    QwtPicker::RubberBand rubberBand;
+    QPen rubberBandPen;
+
+    QwtPicker::DisplayMode trackerMode;
+    QPen trackerPen;
+    QFont trackerFont;
+
+    QPolygon pickedPoints;
+    bool isActive;
+    QPoint trackerPosition;
+
+    bool mouseTracking; // used to save previous value
+
+    QPointer< QwtPickerRubberband > rubberBandOverlay;
+    QPointer< QwtPickerTracker> trackerOverlay;
+
+    bool openGL;
+};
+
+QwtPickerRubberband::QwtPickerRubberband(
+        QwtPicker *picker, QWidget *parent ):
+    QwtWidgetOverlay( parent ),
+    d_picker( picker )
+{
+    setMaskMode( QwtWidgetOverlay::MaskHint );
+}
+
+QRegion QwtPickerRubberband::maskHint() const
+{
+    return d_picker->rubberBandMask();
+}
+
+void QwtPickerRubberband::drawOverlay( QPainter *painter ) const
+{
+    painter->setPen( d_picker->rubberBandPen() );
+    d_picker->drawRubberBand( painter );
+}
+
+QwtPickerTracker::QwtPickerTracker(
+        QwtPicker *picker, QWidget *parent ):
+    QwtWidgetOverlay( parent ),
+    d_picker( picker )
+{
+    setMaskMode( QwtWidgetOverlay::MaskHint );
+}
+
+QRegion QwtPickerTracker::maskHint() const
+{
+    return d_picker->trackerRect( font() );
+}
+
+void QwtPickerTracker::drawOverlay( QPainter *painter ) const
+{
+    painter->setPen( d_picker->trackerPen() );
+    d_picker->drawTracker( painter );
+}
+
+/*!
+  Constructor
+
+  Creates an picker that is enabled, but without a state machine.
+  rubber band and tracker are disabled.
+
+  \param parent Parent widget, that will be observed
+ */
+
+QwtPicker::QwtPicker( QWidget *parent ):
+    QObject( parent )
+{
+    init( parent, NoRubberBand, AlwaysOff );
+}
+
+/*!
+  Constructor
+
+  \param rubberBand Rubber band style
+  \param trackerMode Tracker mode
+  \param parent Parent widget, that will be observed
+ */
+QwtPicker::QwtPicker( RubberBand rubberBand,
+        DisplayMode trackerMode, QWidget *parent ):
+    QObject( parent )
+{
+    init( parent, rubberBand, trackerMode );
+}
+
+//! Destructor
+QwtPicker::~QwtPicker()
+{
+    setMouseTracking( false );
+
+    delete d_data->stateMachine;
+    delete d_data->rubberBandOverlay;
+    delete d_data->trackerOverlay;
+
+    delete d_data;
+}
+
+//! Initialize the picker - used by the constructors
+void QwtPicker::init( QWidget *parent,
+    RubberBand rubberBand, DisplayMode trackerMode )
+{
+    d_data = new PrivateData;
+
+    d_data->rubberBand = rubberBand;
+
+    if ( parent )
+    {
+        if ( parent->focusPolicy() == Qt::NoFocus )
+            parent->setFocusPolicy( Qt::WheelFocus );
+
+        d_data->openGL = parent->inherits( "QGLWidget" );
+        d_data->trackerFont = parent->font();
+        d_data->mouseTracking = parent->hasMouseTracking();
+
+        setEnabled( true );
+    }
+
+    setTrackerMode( trackerMode );
+}
+
+/*!
+  Set a state machine and delete the previous one
+
+  \param stateMachine State machine
+  \sa stateMachine()
+*/
+void QwtPicker::setStateMachine( QwtPickerMachine *stateMachine )
+{
+    if ( d_data->stateMachine != stateMachine )
+    {
+        reset();
+
+        delete d_data->stateMachine;
+        d_data->stateMachine = stateMachine;
+
+        if ( d_data->stateMachine )
+            d_data->stateMachine->reset();
+    }
+}
+
+/*!
+  \return Assigned state machine
+  \sa setStateMachine()
+*/
+QwtPickerMachine *QwtPicker::stateMachine()
+{
+    return d_data->stateMachine;
+}
+
+/*!
+  \return Assigned state machine
+  \sa setStateMachine()
+*/
+const QwtPickerMachine *QwtPicker::stateMachine() const
+{
+    return d_data->stateMachine;
+}
+
+//! Return the parent widget, where the selection happens
+QWidget *QwtPicker::parentWidget()
+{
+    QObject *obj = parent();
+    if ( obj && obj->isWidgetType() )
+        return static_cast<QWidget *>( obj );
+
+    return NULL;
+}
+
+//! Return the parent widget, where the selection happens
+const QWidget *QwtPicker::parentWidget() const
+{
+    QObject *obj = parent();
+    if ( obj && obj->isWidgetType() )
+        return static_cast< const QWidget *>( obj );
+
+    return NULL;
+}
+
+/*!
+  Set the rubber band style
+
+  \param rubberBand Rubber band style
+         The default value is NoRubberBand.
+
+  \sa rubberBand(), RubberBand, setRubberBandPen()
+*/
+void QwtPicker::setRubberBand( RubberBand rubberBand )
+{
+    d_data->rubberBand = rubberBand;
+}
+
+/*!
+  \return Rubber band style
+  \sa setRubberBand(), RubberBand, rubberBandPen()
+*/
+QwtPicker::RubberBand QwtPicker::rubberBand() const
+{
+    return d_data->rubberBand;
+}
+
+/*!
+  \brief Set the display mode of the tracker.
+
+  A tracker displays information about current position of
+  the cursor as a string. The display mode controls
+  if the tracker has to be displayed whenever the observed
+  widget has focus and cursor (AlwaysOn), never (AlwaysOff), or
+  only when the selection is active (ActiveOnly).
+
+  \param mode Tracker display mode
+
+  \warning In case of AlwaysOn, mouseTracking will be enabled
+           for the observed widget.
+  \sa trackerMode(), DisplayMode
+*/
+
+void QwtPicker::setTrackerMode( DisplayMode mode )
+{
+    if ( d_data->trackerMode != mode )
+    {
+        d_data->trackerMode = mode;
+        setMouseTracking( d_data->trackerMode == AlwaysOn );
+    }
+}
+
+/*!
+  \return Tracker display mode
+  \sa setTrackerMode(), DisplayMode
+*/
+QwtPicker::DisplayMode QwtPicker::trackerMode() const
+{
+    return d_data->trackerMode;
+}
+
+/*!
+  \brief Set the resize mode.
+
+  The resize mode controls what to do with the selected points of an active
+  selection when the observed widget is resized.
+
+  Stretch means the points are scaled according to the new
+  size, KeepSize means the points remain unchanged.
+
+  The default mode is Stretch.
+
+  \param mode Resize mode
+  \sa resizeMode(), ResizeMode
+*/
+void QwtPicker::setResizeMode( ResizeMode mode )
+{
+    d_data->resizeMode = mode;
+}
+
+/*!
+  \return Resize mode
+  \sa setResizeMode(), ResizeMode
+*/
+
+QwtPicker::ResizeMode QwtPicker::resizeMode() const
+{
+    return d_data->resizeMode;
+}
+
+/*!
+  \brief En/disable the picker
+
+  When enabled is true an event filter is installed for
+  the observed widget, otherwise the event filter is removed.
+
+  \param enabled true or false
+  \sa isEnabled(), eventFilter()
+*/
+void QwtPicker::setEnabled( bool enabled )
+{
+    if ( d_data->enabled != enabled )
+    {
+        d_data->enabled = enabled;
+
+        QWidget *w = parentWidget();
+        if ( w )
+        {
+            if ( enabled )
+                w->installEventFilter( this );
+            else
+                w->removeEventFilter( this );
+        }
+
+        updateDisplay();
+    }
+}
+
+/*!
+  \return true when enabled, false otherwise
+  \sa setEnabled(), eventFilter()
+*/
+
+bool QwtPicker::isEnabled() const
+{
+    return d_data->enabled;
+}
+
+/*!
+  Set the font for the tracker
+
+  \param font Tracker font
+  \sa trackerFont(), setTrackerMode(), setTrackerPen()
+*/
+void QwtPicker::setTrackerFont( const QFont &font )
+{
+    if ( font != d_data->trackerFont )
+    {
+        d_data->trackerFont = font;
+        updateDisplay();
+    }
+}
+
+/*!
+  \return Tracker font
+  \sa setTrackerFont(), trackerMode(), trackerPen()
+*/
+
+QFont QwtPicker::trackerFont() const
+{
+    return d_data->trackerFont;
+}
+
+/*!
+  Set the pen for the tracker
+
+  \param pen Tracker pen
+  \sa trackerPen(), setTrackerMode(), setTrackerFont()
+*/
+void QwtPicker::setTrackerPen( const QPen &pen )
+{
+    if ( pen != d_data->trackerPen )
+    {
+        d_data->trackerPen = pen;
+        updateDisplay();
+    }
+}
+
+/*!
+  \return Tracker pen
+  \sa setTrackerPen(), trackerMode(), trackerFont()
+*/
+QPen QwtPicker::trackerPen() const
+{
+    return d_data->trackerPen;
+}
+
+/*!
+  Set the pen for the rubberband
+
+  \param pen Rubber band pen
+  \sa rubberBandPen(), setRubberBand()
+*/
+void QwtPicker::setRubberBandPen( const QPen &pen )
+{
+    if ( pen != d_data->rubberBandPen )
+    {
+        d_data->rubberBandPen = pen;
+        updateDisplay();
+    }
+}
+
+/*!
+  \return Rubber band pen
+  \sa setRubberBandPen(), rubberBand()
+*/
+QPen QwtPicker::rubberBandPen() const
+{
+    return d_data->rubberBandPen;
+}
+
+/*!
+   \brief Return the label for a position
+
+   In case of HLineRubberBand the label is the value of the
+   y position, in case of VLineRubberBand the value of the x position.
+   Otherwise the label contains x and y position separated by a ',' .
+
+   The format for the string conversion is "%d".
+
+   \param pos Position
+   \return Converted position as string
+*/
+
+QwtText QwtPicker::trackerText( const QPoint &pos ) const
+{
+    QString label;
+
+    switch ( rubberBand() )
+    {
+        case HLineRubberBand:
+            label.sprintf( "%d", pos.y() );
+            break;
+        case VLineRubberBand:
+            label.sprintf( "%d", pos.x() );
+            break;
+        default:
+            label.sprintf( "%d, %d", pos.x(), pos.y() );
+    }
+    return label;
+}
+
+/*!
+  Calculate the mask for the rubber band overlay
+
+  \return Region for the mask
+  \sa QWidget::setMask()
+ */
+QRegion QwtPicker::rubberBandMask() const
+{
+    QRegion mask;
+
+    if ( !isActive() || rubberBand() == NoRubberBand ||
+        rubberBandPen().style() == Qt::NoPen )
+    {
+        return mask;
+    }
+
+    const QPolygon pa = adjustedPoints( d_data->pickedPoints );
+
+    QwtPickerMachine::SelectionType selectionType =
+        QwtPickerMachine::NoSelection;
+
+    if ( d_data->stateMachine )
+        selectionType = d_data->stateMachine->selectionType();
+
+    switch ( selectionType )
+    {
+        case QwtPickerMachine::NoSelection:
+        case QwtPickerMachine::PointSelection:
+        {
+            if ( pa.count() < 1 )
+                return mask;
+
+            const QPoint pos = pa[0];
+            const int pw = rubberBandPen().width();
+
+            const QRect pRect = pickArea().boundingRect().toRect();
+            switch ( rubberBand() )
+            {
+                case VLineRubberBand:
+                {
+                    mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), 
+                        pos.x(), pRect.bottom() ), pw );
+                    break;
+                }
+                case HLineRubberBand:
+                {
+                    mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), 
+                        pRect.right(), pos.y() ), pw );
+                    break;
+                }
+                case CrossRubberBand:
+                {
+                    mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), 
+                        pos.x(), pRect.bottom() ), pw );
+                    mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), 
+                        pRect.right(), pos.y() ), pw );
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        case QwtPickerMachine::RectSelection:
+        {
+            if ( pa.count() < 2 )
+                return mask;
+
+            const int pw = rubberBandPen().width();
+
+            switch ( rubberBand() )
+            {
+                case RectRubberBand:
+                {
+                    const QRect r = QRect( pa.first(), pa.last() );
+                    mask = qwtMaskRegion( r.normalized(), pw );
+                    break;
+                }
+                case EllipseRubberBand:
+                {
+                    const QRect r = QRect( pa.first(), pa.last() );
+                    mask += r.adjusted( -pw, -pw, pw, pw );
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        case QwtPickerMachine::PolygonSelection:
+        {
+            const int pw = rubberBandPen().width();
+            if ( pw <= 1 )
+            {
+                // because of the join style we better
+                // return a mask for a pen width <= 1 only
+
+                const int off = 2 * pw;
+                const QRect r = pa.boundingRect();
+                mask += r.adjusted( -off, -off, off, off );
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return mask;
+}
+
+/*!
+   Draw a rubber band, depending on rubberBand()
+
+   \param painter Painter, initialized with a clip region
+
+   \sa rubberBand(), RubberBand
+*/
+
+void QwtPicker::drawRubberBand( QPainter *painter ) const
+{
+    if ( !isActive() || rubberBand() == NoRubberBand ||
+        rubberBandPen().style() == Qt::NoPen )
+    {
+        return;
+    }
+
+    const QPolygon pa = adjustedPoints( d_data->pickedPoints );
+
+    QwtPickerMachine::SelectionType selectionType =
+        QwtPickerMachine::NoSelection;
+
+    if ( d_data->stateMachine )
+        selectionType = d_data->stateMachine->selectionType();
+
+    switch ( selectionType )
+    {
+        case QwtPickerMachine::NoSelection:
+        case QwtPickerMachine::PointSelection:
+        {
+            if ( pa.count() < 1 )
+                return;
+
+            const QPoint pos = pa[0];
+
+            const QRect pRect = pickArea().boundingRect().toRect();
+            switch ( rubberBand() )
+            {
+                case VLineRubberBand:
+                {
+                    QwtPainter::drawLine( painter, pos.x(),
+                        pRect.top(), pos.x(), pRect.bottom() );
+                    break;
+                }
+                case HLineRubberBand:
+                {
+                    QwtPainter::drawLine( painter, pRect.left(),
+                        pos.y(), pRect.right(), pos.y() );
+                    break;
+                }
+                case CrossRubberBand:
+                {
+                    QwtPainter::drawLine( painter, pos.x(),
+                        pRect.top(), pos.x(), pRect.bottom() );
+                    QwtPainter::drawLine( painter, pRect.left(),
+                        pos.y(), pRect.right(), pos.y() );
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        case QwtPickerMachine::RectSelection:
+        {
+            if ( pa.count() < 2 )
+                return;
+
+            const QRect rect = QRect( pa.first(), pa.last() ).normalized();
+            switch ( rubberBand() )
+            {
+                case EllipseRubberBand:
+                {
+                    QwtPainter::drawEllipse( painter, rect );
+                    break;
+                }
+                case RectRubberBand:
+                {
+                    QwtPainter::drawRect( painter, rect );
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        case QwtPickerMachine::PolygonSelection:
+        {
+            if ( rubberBand() == PolygonRubberBand )
+                painter->drawPolyline( pa );
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*!
+   Draw the tracker
+
+   \param painter Painter
+   \sa trackerRect(), trackerText()
+*/
+
+void QwtPicker::drawTracker( QPainter *painter ) const
+{
+    const QRect textRect = trackerRect( painter->font() );
+    if ( !textRect.isEmpty() )
+    {
+        const QwtText label = trackerText( d_data->trackerPosition );
+        if ( !label.isEmpty() )
+            label.draw( painter, textRect );
+    }
+}
+
+/*!
+   \brief Map the pickedPoints() into a selection()
+
+   adjustedPoints() maps the points, that have been collected on
+   the parentWidget() into a selection(). The default implementation
+   simply returns the points unmodified.
+
+   The reason, why a selection() differs from the picked points
+   depends on the application requirements. F.e. :
+
+   - A rectangular selection might need to have a specific aspect ratio only.\n
+   - A selection could accept non intersecting polygons only.\n
+   - ...\n
+
+   The example below is for a rectangular selection, where the first
+   point is the center of the selected rectangle.
+  \par Example
+  \verbatim QPolygon MyPicker::adjustedPoints(const QPolygon &points) const
+{
+    QPolygon adjusted;
+    if ( points.size() == 2 )
+    {
+        const int width = qAbs(points[1].x() - points[0].x());
+        const int height = qAbs(points[1].y() - points[0].y());
+
+        QRect rect(0, 0, 2 * width, 2 * height);
+        rect.moveCenter(points[0]);
+
+        adjusted += rect.topLeft();
+        adjusted += rect.bottomRight();
+    }
+    return adjusted;
+}\endverbatim\n
+
+  \param points Selected points
+  \return Selected points unmodified
+*/
+QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const
+{
+    return points;
+}
+
+/*!
+  \return Selected points
+  \sa pickedPoints(), adjustedPoints()
+*/
+QPolygon QwtPicker::selection() const
+{
+    return adjustedPoints( d_data->pickedPoints );
+}
+
+//! \return Current position of the tracker
+QPoint QwtPicker::trackerPosition() const
+{
+    return d_data->trackerPosition;
+}
+
+/*!
+   Calculate the bounding rectangle for the tracker text
+   from the current position of the tracker
+
+   \param font Font of the tracker text
+   \return Bounding rectangle of the tracker text
+
+   \sa trackerPosition()
+*/
+QRect QwtPicker::trackerRect( const QFont &font ) const
+{
+    if ( trackerMode() == AlwaysOff ||
+        ( trackerMode() == ActiveOnly && !isActive() ) )
+    {
+        return QRect();
+    }
+
+    if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
+        return QRect();
+
+    QwtText text = trackerText( d_data->trackerPosition );
+    if ( text.isEmpty() )
+        return QRect();
+
+    const QSizeF textSize = text.textSize( font );
+    QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) );
+
+    const QPoint &pos = d_data->trackerPosition;
+
+    int alignment = 0;
+    if ( isActive() && d_data->pickedPoints.count() > 1
+        && rubberBand() != NoRubberBand )
+    {
+        const QPoint last =
+            d_data->pickedPoints[int( d_data->pickedPoints.count() ) - 2];
+
+        alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft;
+        alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop;
+    }
+    else
+        alignment = Qt::AlignTop | Qt::AlignRight;
+
+    const int margin = 5;
+
+    int x = pos.x();
+    if ( alignment & Qt::AlignLeft )
+        x -= textRect.width() + margin;
+    else if ( alignment & Qt::AlignRight )
+        x += margin;
+
+    int y = pos.y();
+    if ( alignment & Qt::AlignBottom )
+        y += margin;
+    else if ( alignment & Qt::AlignTop )
+        y -= textRect.height() + margin;
+
+    textRect.moveTopLeft( QPoint( x, y ) );
+
+    const QRect pickRect = pickArea().boundingRect().toRect();
+
+    int right = qMin( textRect.right(), pickRect.right() - margin );
+    int bottom = qMin( textRect.bottom(), pickRect.bottom() - margin );
+    textRect.moveBottomRight( QPoint( right, bottom ) );
+
+    int left = qMax( textRect.left(), pickRect.left() + margin );
+    int top = qMax( textRect.top(), pickRect.top() + margin );
+    textRect.moveTopLeft( QPoint( left, top ) );
+
+    return textRect;
+}
+
+/*!
+  \brief Event filter
+
+  When isEnabled() is true all events of the observed widget are filtered.
+  Mouse and keyboard events are translated into widgetMouse- and widgetKey-
+  and widgetWheel-events. Paint and Resize events are handled to keep
+  rubber band and tracker up to date.
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return Always false.
+
+  \sa widgetEnterEvent(), widgetLeaveEvent(),
+      widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(),
+      QObject::installEventFilter(), QObject::event()
+*/
+bool QwtPicker::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object && object == parentWidget() )
+    {
+        switch ( event->type() )
+        {
+            case QEvent::Resize:
+            {
+                const QResizeEvent *re = static_cast<QResizeEvent *>( event );
+
+                /*
+                   Adding/deleting additional event filters inside of an event filter
+                   is not safe dues to the implementation in Qt ( changing alist while iterating ).
+                   So we create the overlays in a way, that they don't install en event filter
+                   ( parent set to NULL ) and do the resizing here.
+                 */
+                if ( d_data->trackerOverlay )
+                    d_data->trackerOverlay->resize( re->size() );
+
+                if ( d_data->rubberBandOverlay )
+                    d_data->rubberBandOverlay->resize( re->size() );
+
+                if ( d_data->resizeMode == Stretch )
+                    stretchSelection( re->oldSize(), re->size() );
+
+                updateDisplay();
+                break;
+            }
+            case QEvent::Enter:
+            {
+                widgetEnterEvent( event );
+                break;
+            }
+            case QEvent::Leave:
+            {
+                widgetLeaveEvent( event );
+                break;
+            }
+            case QEvent::MouseButtonPress:
+            {
+                widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseButtonRelease:
+            {
+                widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseButtonDblClick:
+            {
+                widgetMouseDoubleClickEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::MouseMove:
+            {
+                widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
+                break;
+            }
+            case QEvent::KeyPress:
+            {
+                widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
+                break;
+            }
+            case QEvent::KeyRelease:
+            {
+                widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
+                break;
+            }
+            case QEvent::Wheel:
+            {
+                widgetWheelEvent( static_cast<QWheelEvent *>( event ) );
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return false;
+}
+
+/*!
+  Handle a mouse press event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent )
+{
+    transition( mouseEvent );
+}
+
+/*!
+  Handle a mouse move event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
+{
+    if ( pickArea().contains( mouseEvent->pos() ) )
+        d_data->trackerPosition = mouseEvent->pos();
+    else
+        d_data->trackerPosition = QPoint( -1, -1 );
+
+    if ( !isActive() )
+        updateDisplay();
+
+    transition( mouseEvent );
+}
+
+/*!
+  Handle a enter event for the observed widget.
+
+  \param event Qt event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetEnterEvent( QEvent *event )
+{
+    transition( event );
+}
+
+/*!
+  Handle a leave event for the observed widget.
+
+  \param event Qt event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetLeaveEvent( QEvent *event )
+{
+    transition( event );
+
+    d_data->trackerPosition = QPoint( -1, -1 );
+    if ( !isActive() )
+        updateDisplay();
+}
+
+/*!
+  Handle a mouse release event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMousePressEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
+{
+    transition( mouseEvent );
+}
+
+/*!
+  Handle mouse double click event for the observed widget.
+
+  \param mouseEvent Mouse event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetMouseDoubleClickEvent( QMouseEvent *mouseEvent )
+{
+    transition( mouseEvent );
+}
+
+
+/*!
+  Handle a wheel event for the observed widget.
+
+  Move the last point of the selection in case of isActive() == true
+
+  \param wheelEvent Wheel event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetKeyPressEvent(), widgetKeyReleaseEvent()
+*/
+void QwtPicker::widgetWheelEvent( QWheelEvent *wheelEvent )
+{
+    if ( pickArea().contains( wheelEvent->pos() ) )
+        d_data->trackerPosition = wheelEvent->pos();
+    else
+        d_data->trackerPosition = QPoint( -1, -1 );
+
+    updateDisplay();
+
+    transition( wheelEvent );
+}
+
+/*!
+  Handle a key press event for the observed widget.
+
+  Selections can be completely done by the keyboard. The arrow keys
+  move the cursor, the abort key aborts a selection. All other keys
+  are handled by the current state machine.
+
+  \param keyEvent Key event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyReleaseEvent(), stateMachine(),
+      QwtEventPattern::KeyPatternCode
+*/
+void QwtPicker::widgetKeyPressEvent( QKeyEvent *keyEvent )
+{
+    int dx = 0;
+    int dy = 0;
+
+    int offset = 1;
+    if ( keyEvent->isAutoRepeat() )
+        offset = 5;
+
+    if ( keyMatch( KeyLeft, keyEvent ) )
+        dx = -offset;
+    else if ( keyMatch( KeyRight, keyEvent ) )
+        dx = offset;
+    else if ( keyMatch( KeyUp, keyEvent ) )
+        dy = -offset;
+    else if ( keyMatch( KeyDown, keyEvent ) )
+        dy = offset;
+    else if ( keyMatch( KeyAbort, keyEvent ) )
+    {
+        reset();
+    }
+    else
+        transition( keyEvent );
+
+    if ( dx != 0 || dy != 0 )
+    {
+        const QRect rect = pickArea().boundingRect().toRect();
+        const QPoint pos = parentWidget()->mapFromGlobal( QCursor::pos() );
+
+        int x = pos.x() + dx;
+        x = qMax( rect.left(), x );
+        x = qMin( rect.right(), x );
+
+        int y = pos.y() + dy;
+        y = qMax( rect.top(), y );
+        y = qMin( rect.bottom(), y );
+
+        QCursor::setPos( parentWidget()->mapToGlobal( QPoint( x, y ) ) );
+    }
+}
+
+/*!
+  Handle a key release event for the observed widget.
+
+  Passes the event to the state machine.
+
+  \param keyEvent Key event
+
+  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
+      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
+      widgetWheelEvent(), widgetKeyPressEvent(), stateMachine()
+*/
+void QwtPicker::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
+{
+    transition( keyEvent );
+}
+
+/*!
+  Passes an event to the state machine and executes the resulting
+  commands. Append and Move commands use the current position
+  of the cursor ( QCursor::pos() ).
+
+  \param event Event
+*/
+void QwtPicker::transition( const QEvent *event )
+{
+    if ( !d_data->stateMachine )
+        return;
+
+    const QList<QwtPickerMachine::Command> commandList =
+        d_data->stateMachine->transition( *this, event );
+
+    QPoint pos;
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonDblClick:
+        case QEvent::MouseButtonPress:
+        case QEvent::MouseButtonRelease:
+        case QEvent::MouseMove:
+        {
+            const QMouseEvent *me = 
+                static_cast< const QMouseEvent * >( event );
+            pos = me->pos();
+            break;
+        }
+        default:
+            pos = parentWidget()->mapFromGlobal( QCursor::pos() );
+    }
+
+    for ( int i = 0; i < commandList.count(); i++ )
+    {
+        switch ( commandList[i] )
+        {
+            case QwtPickerMachine::Begin:
+            {
+                begin();
+                break;
+            }
+            case QwtPickerMachine::Append:
+            {
+                append( pos );
+                break;
+            }
+            case QwtPickerMachine::Move:
+            {
+                move( pos );
+                break;
+            }
+            case QwtPickerMachine::Remove:
+            {
+                remove();
+                break;
+            }
+            case QwtPickerMachine::End:
+            {
+                end();
+                break;
+            }
+        }
+    }
+}
+
+/*!
+  Open a selection setting the state to active
+
+  \sa isActive(), end(), append(), move()
+*/
+void QwtPicker::begin()
+{
+    if ( d_data->isActive )
+        return;
+
+    d_data->pickedPoints.resize( 0 );
+    d_data->isActive = true;
+    Q_EMIT activated( true );
+
+    if ( trackerMode() != AlwaysOff )
+    {
+        if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
+        {
+            QWidget *w = parentWidget();
+            if ( w )
+                d_data->trackerPosition = w->mapFromGlobal( QCursor::pos() );
+        }
+    }
+
+    updateDisplay();
+    setMouseTracking( true );
+}
+
+/*!
+  \brief Close a selection setting the state to inactive.
+
+  The selection is validated and maybe fixed by accept().
+
+  \param ok If true, complete the selection and emit a selected signal
+            otherwise discard the selection.
+  \return true if the selection is accepted, false otherwise
+  \sa isActive(), begin(), append(), move(), selected(), accept()
+*/
+bool QwtPicker::end( bool ok )
+{
+    if ( d_data->isActive )
+    {
+        setMouseTracking( false );
+
+        d_data->isActive = false;
+        Q_EMIT activated( false );
+
+        if ( trackerMode() == ActiveOnly )
+            d_data->trackerPosition = QPoint( -1, -1 );
+
+        if ( ok )
+            ok = accept( d_data->pickedPoints );
+
+        if ( ok )
+            Q_EMIT selected( d_data->pickedPoints );
+        else
+            d_data->pickedPoints.resize( 0 );
+
+        updateDisplay();
+    }
+    else
+        ok = false;
+
+    return ok;
+}
+
+/*!
+   Reset the state machine and terminate ( end(false) ) the selection
+*/
+void QwtPicker::reset()
+{
+    if ( d_data->stateMachine )
+        d_data->stateMachine->reset();
+
+    if ( isActive() )
+        end( false );
+}
+
+/*!
+  Append a point to the selection and update rubber band and tracker.
+  The appended() signal is emitted.
+
+  \param pos Additional point
+
+  \sa isActive(), begin(), end(), move(), appended()
+*/
+void QwtPicker::append( const QPoint &pos )
+{
+    if ( d_data->isActive )
+    {
+        const int idx = d_data->pickedPoints.count();
+        d_data->pickedPoints.resize( idx + 1 );
+        d_data->pickedPoints[idx] = pos;
+
+        updateDisplay();
+        Q_EMIT appended( pos );
+    }
+}
+
+/*!
+  Move the last point of the selection
+  The moved() signal is emitted.
+
+  \param pos New position
+  \sa isActive(), begin(), end(), append()
+*/
+void QwtPicker::move( const QPoint &pos )
+{
+    if ( d_data->isActive )
+    {
+        const int idx = d_data->pickedPoints.count() - 1;
+        if ( idx >= 0 )
+        {
+            if ( d_data->pickedPoints[idx] != pos )
+            {
+                d_data->pickedPoints[idx] = pos;
+
+                updateDisplay();
+                Q_EMIT moved( pos );
+            }
+        }
+    }
+}
+
+/*!
+  Remove the last point of the selection
+  The removed() signal is emitted.
+
+  \sa isActive(), begin(), end(), append(), move()
+*/
+void QwtPicker::remove()
+{
+    if ( d_data->isActive )
+    {
+        const int idx = d_data->pickedPoints.count() - 1;
+        if ( idx > 0 )
+        {
+            const int idx = d_data->pickedPoints.count();
+
+            const QPoint pos = d_data->pickedPoints[idx - 1];
+            d_data->pickedPoints.resize( idx - 1 );
+
+            updateDisplay();
+            Q_EMIT removed( pos );
+        }
+    }
+}
+
+/*!
+  \brief Validate and fix up the selection
+
+  Accepts all selections unmodified
+
+  \param selection Selection to validate and fix up
+  \return true, when accepted, false otherwise
+*/
+bool QwtPicker::accept( QPolygon &selection ) const
+{
+    Q_UNUSED( selection );
+    return true;
+}
+
+/*!
+  A picker is active between begin() and end().
+  \return true if the selection is active.
+*/
+bool QwtPicker::isActive() const
+{
+    return d_data->isActive;
+}
+
+/*!
+  Return the points, that have been collected so far. The selection()
+  is calculated from the pickedPoints() in adjustedPoints().
+  \return Picked points
+*/
+const QPolygon &QwtPicker::pickedPoints() const
+{
+    return d_data->pickedPoints;
+}
+
+/*!
+  Scale the selection by the ratios of oldSize and newSize
+  The changed() signal is emitted.
+
+  \param oldSize Previous size
+  \param newSize Current size
+
+  \sa ResizeMode, setResizeMode(), resizeMode()
+*/
+void QwtPicker::stretchSelection( const QSize &oldSize, const QSize &newSize )
+{
+    if ( oldSize.isEmpty() )
+    {
+        // avoid division by zero. But scaling for small sizes also
+        // doesn't make much sense, because of rounding losses. TODO ...
+        return;
+    }
+
+    const double xRatio =
+        double( newSize.width() ) / double( oldSize.width() );
+    const double yRatio =
+        double( newSize.height() ) / double( oldSize.height() );
+
+    for ( int i = 0; i < int( d_data->pickedPoints.count() ); i++ )
+    {
+        QPoint &p = d_data->pickedPoints[i];
+        p.setX( qRound( p.x() * xRatio ) );
+        p.setY( qRound( p.y() * yRatio ) );
+
+        Q_EMIT changed( d_data->pickedPoints );
+    }
+}
+
+/*!
+  Set mouse tracking for the observed widget.
+
+  In case of enable is true, the previous value
+  is saved, that is restored when enable is false.
+
+  \warning Even when enable is false, mouse tracking might be restored
+           to true. When mouseTracking for the observed widget
+           has been changed directly by QWidget::setMouseTracking
+           while mouse tracking has been set to true, this value can't
+           be restored.
+*/
+
+void QwtPicker::setMouseTracking( bool enable )
+{
+    QWidget *widget = parentWidget();
+    if ( !widget )
+        return;
+
+    if ( enable )
+    {
+        d_data->mouseTracking = widget->hasMouseTracking();
+        widget->setMouseTracking( true );
+    }
+    else
+    {
+        widget->setMouseTracking( d_data->mouseTracking );
+    }
+}
+
+/*!
+  Find the area of the observed widget, where selection might happen.
+
+  \return parentWidget()->contentsRect() 
+*/
+QPainterPath QwtPicker::pickArea() const
+{
+    QPainterPath path;
+
+    const QWidget *widget = parentWidget();
+    if ( widget )
+        path.addRect( widget->contentsRect() );
+
+    return path;
+}
+
+//! Update the state of rubber band and tracker label
+void QwtPicker::updateDisplay()
+{
+    QWidget *w = parentWidget();
+
+    bool showRubberband = false;
+    bool showTracker = false;
+
+    if ( w && w->isVisible() && d_data->enabled )
+    {
+        if ( rubberBand() != NoRubberBand && isActive() &&
+            rubberBandPen().style() != Qt::NoPen )
+        {
+            showRubberband = true;
+        }
+
+        if ( trackerMode() == AlwaysOn ||
+            ( trackerMode() == ActiveOnly && isActive() ) )
+        {
+            if ( trackerPen() != Qt::NoPen 
+                && !trackerRect( QFont() ).isEmpty() )
+            {
+                showTracker = true;
+            }
+        }
+    }
+
+    QPointer< QwtPickerRubberband > &rw = d_data->rubberBandOverlay;
+    if ( showRubberband )
+    {
+        if ( rw.isNull() )
+        {
+            rw = new QwtPickerRubberband( this, NULL ); // NULL -> no extra event filter
+            rw->setObjectName( "PickerRubberBand" );
+            rw->setParent( w );
+            rw->resize( w->size() );
+        }
+
+        if ( d_data->rubberBand <= RectRubberBand )
+            rw->setMaskMode( QwtWidgetOverlay::MaskHint );
+        else
+            rw->setMaskMode( QwtWidgetOverlay::AlphaMask );
+
+        rw->updateOverlay();
+    }
+    else
+    {
+        if ( d_data->openGL )
+        {
+            // Qt 4.8 crashes for a delete
+            if ( !rw.isNull() )
+            {
+                rw->hide();
+                rw->deleteLater();
+                rw = NULL;
+            }
+        }
+        else
+        {
+            delete rw;
+        }
+    }
+
+    QPointer< QwtPickerTracker > &tw = d_data->trackerOverlay;
+    if ( showTracker )
+    {
+        if ( tw.isNull() )
+        {
+            tw = new QwtPickerTracker( this, NULL ); // NULL -> no extra event filter
+            tw->setObjectName( "PickerTracker" );
+            tw->setParent( w );
+            tw->resize( w->size() );
+        }
+        tw->setFont( d_data->trackerFont );
+        tw->updateOverlay();
+    }
+    else
+    {
+        if ( d_data->openGL )
+        {
+            // Qt 4.8 crashes for a delete
+            if ( !tw.isNull() )
+            {
+                tw->hide();
+                tw->deleteLater();
+                tw = NULL;
+            }
+        }
+        else
+        {
+            delete tw;
+        }
+    }
+}
+
+//! \return Overlay displaying the rubber band
+const QwtWidgetOverlay *QwtPicker::rubberBandOverlay() const
+{
+    return d_data->rubberBandOverlay;
+}
+
+//! \return Overlay displaying the tracker text
+const QwtWidgetOverlay *QwtPicker::trackerOverlay() const
+{
+    return d_data->trackerOverlay;
+}
+
diff --git a/source/third_party/qwt/qwt_picker_machine.cpp b/source/third_party/qwt/qwt_picker_machine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d023485fed7b88a7b570664a3cb7edce1c5f4df6
--- /dev/null
+++ b/source/third_party/qwt/qwt_picker_machine.cpp
@@ -0,0 +1,541 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_picker_machine.h"
+#include "qwt/qwt_event_pattern.h"
+#include <qevent.h>
+
+//! Constructor
+QwtPickerMachine::QwtPickerMachine( SelectionType type ):
+    d_selectionType( type ),
+    d_state( 0 )
+{
+}
+
+//! Destructor
+QwtPickerMachine::~QwtPickerMachine()
+{
+}
+
+//! Return the selection type
+QwtPickerMachine::SelectionType QwtPickerMachine::selectionType() const
+{
+    return d_selectionType;
+}
+
+//! Return the current state
+int QwtPickerMachine::state() const
+{
+    return d_state;
+}
+
+//! Change the current state
+void QwtPickerMachine::setState( int state )
+{
+    d_state = state;
+}
+
+//! Set the current state to 0.
+void QwtPickerMachine::reset()
+{
+    setState( 0 );
+}
+
+//! Constructor
+QwtPickerTrackerMachine::QwtPickerTrackerMachine():
+    QwtPickerMachine( NoSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerTrackerMachine::transition(
+    const QwtEventPattern &, const QEvent *e )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( e->type() )
+    {
+        case QEvent::Enter:
+        case QEvent::MouseMove:
+        {
+            if ( state() == 0 )
+            {
+                cmdList += Begin;
+                cmdList += Append;
+                setState( 1 );
+            }
+            else
+            {
+                cmdList += Move;
+            }
+            break;
+        }
+        case QEvent::Leave:
+        {
+            cmdList += Remove;
+            cmdList += End;
+            setState( 0 );
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerClickPointMachine::QwtPickerClickPointMachine():
+    QwtPickerMachine( PointSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerClickPointMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                cmdList += Begin;
+                cmdList += Append;
+                cmdList += End;
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            const QKeyEvent *keyEvent = static_cast<const QKeyEvent *> ( event );
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) )
+            {
+                if ( !keyEvent->isAutoRepeat() )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += End;
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerDragPointMachine::QwtPickerDragPointMachine():
+    QwtPickerMachine( PointSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerDragPointMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            if ( state() != 0 )
+            {
+                cmdList += End;
+                setState( 0 );
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            const QKeyEvent *keyEvent = static_cast<const QKeyEvent *> ( event );
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) )
+            {
+                if ( !keyEvent->isAutoRepeat() )
+                {
+                    if ( state() == 0 )
+                    {
+                        cmdList += Begin;
+                        cmdList += Append;
+                        setState( 1 );
+                    }
+                    else
+                    {
+                        cmdList += End;
+                        setState( 0 );
+                    }
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerClickRectMachine::QwtPickerClickRectMachine():
+    QwtPickerMachine( RectSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerClickRectMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                switch ( state() )
+                {
+                    case 0:
+                    {
+                        cmdList += Begin;
+                        cmdList += Append;
+                        setState( 1 );
+                        break;
+                    }
+                    case 1:
+                    {
+                        // Uh, strange we missed the MouseButtonRelease
+                        break;
+                    }
+                    default:
+                    {
+                        cmdList += End;
+                        setState( 0 );
+                    }
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 1 )
+                {
+                    cmdList += Append;
+                    setState( 2 );
+                }
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            const QKeyEvent *keyEvent = static_cast<const QKeyEvent *> ( event );
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) )
+            {
+                if ( !keyEvent->isAutoRepeat() )
+                {
+                    if ( state() == 0 )
+                    {
+                        cmdList += Begin;
+                        cmdList += Append;
+                        setState( 1 );
+                    }
+                    else
+                    {
+                        if ( state() == 1 )
+                        {
+                            cmdList += Append;
+                            setState( 2 );
+                        }
+                        else if ( state() == 2 )
+                        {
+                            cmdList += End;
+                            setState( 0 );
+                        }
+                    }
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerDragRectMachine::QwtPickerDragRectMachine():
+    QwtPickerMachine( RectSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerDragRectMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 2 );
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            if ( state() == 2 )
+            {
+                cmdList += End;
+                setState( 0 );
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, 
+                static_cast<const QKeyEvent *> ( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 2 );
+                }
+                else
+                {
+                    cmdList += End;
+                    setState( 0 );
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerPolygonMachine::QwtPickerPolygonMachine():
+    QwtPickerMachine( PolygonSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerPolygonMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch ( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+                else
+                {
+                    cmdList += Append;
+                }
+            }
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect2, 
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 1 )
+                {
+                    cmdList += End;
+                    setState( 0 );
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            const QKeyEvent *keyEvent = static_cast<const QKeyEvent *> ( event );
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) )
+            {
+                if ( !keyEvent->isAutoRepeat() )
+                {
+                    if ( state() == 0 )
+                    {
+                        cmdList += Begin;
+                        cmdList += Append;
+                        cmdList += Append;
+                        setState( 1 );
+                    }
+                    else
+                    {
+                        cmdList += Append;
+                    }
+                }
+            }
+            else if ( eventPattern.keyMatch( QwtEventPattern::KeySelect2, keyEvent ) )
+            {
+                if ( !keyEvent->isAutoRepeat() )
+                {
+                    if ( state() == 1 )
+                    {
+                        cmdList += End;
+                        setState( 0 );
+                    }
+                }
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
+
+//! Constructor
+QwtPickerDragLineMachine::QwtPickerDragLineMachine():
+    QwtPickerMachine( PolygonSelection )
+{
+}
+
+//! Transition
+QList<QwtPickerMachine::Command> QwtPickerDragLineMachine::transition(
+    const QwtEventPattern &eventPattern, const QEvent *event )
+{
+    QList<QwtPickerMachine::Command> cmdList;
+
+    switch( event->type() )
+    {
+        case QEvent::MouseButtonPress:
+        {
+            if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1,
+                static_cast<const QMouseEvent *>( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+            }
+            break;
+        }
+        case QEvent::KeyPress:
+        {
+            if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1,
+                static_cast<const QKeyEvent *> ( event ) ) )
+            {
+                if ( state() == 0 )
+                {
+                    cmdList += Begin;
+                    cmdList += Append;
+                    cmdList += Append;
+                    setState( 1 );
+                }
+                else
+                {
+                    cmdList += End;
+                    setState( 0 );
+                }
+            }
+            break;
+        }
+        case QEvent::MouseMove:
+        case QEvent::Wheel:
+        {
+            if ( state() != 0 )
+                cmdList += Move;
+
+            break;
+        }
+        case QEvent::MouseButtonRelease:
+        {
+            if ( state() != 0 )
+            {
+                cmdList += End;
+                setState( 0 );
+            }
+        }
+        default:
+            break;
+    }
+
+    return cmdList;
+}
diff --git a/source/third_party/qwt/qwt_pixel_matrix.cpp b/source/third_party/qwt/qwt_pixel_matrix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a6191c5a44db23daf48cc16ba12cde64ca8909df
--- /dev/null
+++ b/source/third_party/qwt/qwt_pixel_matrix.cpp
@@ -0,0 +1,51 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_pixel_matrix.h"
+
+/*!
+  \brief Constructor
+
+  \param rect Bounding rectangle for the matrix
+*/
+QwtPixelMatrix::QwtPixelMatrix( const QRect& rect ):
+    QBitArray( qMax( rect.width() * rect.height(), 0 ) ),
+    d_rect( rect )
+{
+}
+
+//! Destructor
+QwtPixelMatrix::~QwtPixelMatrix()
+{
+}
+
+/*!
+    Set the bounding rectangle of the matrix
+
+    \param rect Bounding rectangle
+
+    \note All bits are cleared
+ */
+void QwtPixelMatrix::setRect( const QRect& rect )
+{
+    if ( rect != d_rect )
+    {
+        d_rect = rect;
+        const int sz = qMax( rect.width() * rect.height(), 0 );
+        resize( sz );
+    }
+
+    fill( false );
+}
+
+//! \return Bounding rectangle
+QRect QwtPixelMatrix::rect() const
+{
+    return d_rect;
+}
diff --git a/source/third_party/qwt/qwt_plot.cpp b/source/third_party/qwt/qwt_plot.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6349ee88a06424f2afd4de4a33174e30cb1b13d7
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot.cpp
@@ -0,0 +1,1176 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_plot_dict.h"
+#include "qwt/qwt_plot_layout.h"
+#include "qwt/qwt_scale_widget.h"
+#include "qwt/qwt_scale_engine.h"
+#include "qwt/qwt_text_label.h"
+#include "qwt/qwt_legend.h"
+#include "qwt/qwt_legend_data.h"
+#include "qwt/qwt_plot_canvas.h"
+#include <qmath.h>
+#include <qpainter.h>
+#include <qpointer.h>
+#include <qpaintengine.h>
+#include <qapplication.h>
+#include <qevent.h>
+
+static inline void qwtEnableLegendItems( QwtPlot *plot, bool on )
+{
+    if ( on )
+    {
+        QObject::connect( 
+            plot, SIGNAL( legendDataChanged(
+                const QVariant &, const QList<QwtLegendData> & ) ),
+            plot, SLOT( updateLegendItems( 
+                const QVariant &, const QList<QwtLegendData> & ) ) );
+    }
+    else
+    {
+        QObject::disconnect( 
+            plot, SIGNAL( legendDataChanged(
+                const QVariant &, const QList<QwtLegendData> & ) ),
+            plot, SLOT( updateLegendItems( 
+                const QVariant &, const QList<QwtLegendData> & ) ) );
+    }
+}
+
+static void qwtSetTabOrder( 
+    QWidget *first, QWidget *second, bool withChildren )
+{
+    QList<QWidget *> tabChain;
+    tabChain += first;
+    tabChain += second;
+
+    if ( withChildren )
+    {
+        QList<QWidget *> children = second->findChildren<QWidget *>();
+
+        QWidget *w = second->nextInFocusChain();
+        while ( children.contains( w ) )
+        {
+            children.removeAll( w );
+
+            tabChain += w;
+            w = w->nextInFocusChain();
+        }
+    }
+
+    for ( int i = 0; i < tabChain.size() - 1; i++ )
+    {
+        QWidget *from = tabChain[i];
+        QWidget *to = tabChain[i+1];
+
+        const Qt::FocusPolicy policy1 = from->focusPolicy();
+        const Qt::FocusPolicy policy2 = to->focusPolicy();
+
+        QWidget *proxy1 = from->focusProxy();
+        QWidget *proxy2 = to->focusProxy();
+
+        from->setFocusPolicy( Qt::TabFocus );
+        from->setFocusProxy( NULL);
+
+        to->setFocusPolicy( Qt::TabFocus );
+        to->setFocusProxy( NULL);
+
+        QWidget::setTabOrder( from, to );
+
+        from->setFocusPolicy( policy1 );
+        from->setFocusProxy( proxy1);
+
+        to->setFocusPolicy( policy2 );
+        to->setFocusProxy( proxy2 );
+    }
+}
+
+class QwtPlot::PrivateData
+{
+public:
+    QPointer<QwtTextLabel> titleLabel;
+    QPointer<QwtTextLabel> footerLabel;
+    QPointer<QWidget> canvas;
+    QPointer<QwtAbstractLegend> legend;
+    QwtPlotLayout *layout;
+
+    bool autoReplot;
+};
+
+/*!
+  \brief Constructor
+  \param parent Parent widget
+ */
+QwtPlot::QwtPlot( QWidget *parent ):
+    QFrame( parent )
+{
+    initPlot( QwtText() );
+}
+
+/*!
+  \brief Constructor
+  \param title Title text
+  \param parent Parent widget
+ */
+QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ):
+    QFrame( parent )
+{
+    initPlot( title );
+}
+
+//! Destructor
+QwtPlot::~QwtPlot()
+{
+    setAutoReplot( false );
+    detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() );
+
+    delete d_data->layout;
+    deleteAxesData();
+    delete d_data;
+}
+
+/*!
+  \brief Initializes a QwtPlot instance
+  \param title Title text
+ */
+void QwtPlot::initPlot( const QwtText &title )
+{
+    d_data = new PrivateData;
+
+    d_data->layout = new QwtPlotLayout;
+    d_data->autoReplot = false;
+
+    // title
+    d_data->titleLabel = new QwtTextLabel( this );
+    d_data->titleLabel->setObjectName( "QwtPlotTitle" );
+    d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) );
+
+    QwtText text( title );
+    text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
+    d_data->titleLabel->setText( text );
+
+    // footer
+    d_data->footerLabel = new QwtTextLabel( this );
+    d_data->footerLabel->setObjectName( "QwtPlotFooter" );
+
+    QwtText footer;
+    footer.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
+    d_data->footerLabel->setText( footer );
+
+    // legend
+    d_data->legend = NULL;
+
+    // axis
+    initAxesData();
+
+    // canvas
+    d_data->canvas = new QwtPlotCanvas( this );
+    d_data->canvas->setObjectName( "QwtPlotCanvas" );
+    d_data->canvas->installEventFilter( this );
+
+    setSizePolicy( QSizePolicy::MinimumExpanding,
+        QSizePolicy::MinimumExpanding );
+
+    resize( 200, 200 );
+
+    QList<QWidget *> focusChain;
+    focusChain << this << d_data->titleLabel << axisWidget( xTop )
+        << axisWidget( yLeft ) << d_data->canvas << axisWidget( yRight )
+        << axisWidget( xBottom ) << d_data->footerLabel;
+
+    for ( int i = 0; i < focusChain.size() - 1; i++ )
+        qwtSetTabOrder( focusChain[i], focusChain[i+1], false );
+
+    qwtEnableLegendItems( this, true );
+}
+
+/*!
+  \brief Set the drawing canvas of the plot widget
+
+  QwtPlot invokes methods of the canvas as meta methods ( see QMetaObject ).
+  In opposite to using conventional C++ techniques like virtual methods
+  they allow to use canvas implementations that are derived from 
+  QWidget or QGLWidget.
+
+  The following meta methods could be implemented:
+
+  - replot()
+    When the canvas doesn't offer a replot method, QwtPlot calls
+    update() instead.
+
+  - borderPath()
+    The border path is necessary to clip the content of the canvas
+    When the canvas doesn't have any special border ( f.e rounded corners )
+    it is o.k. not to implement this method.
+
+  The default canvas is a QwtPlotCanvas 
+
+  \param canvas Canvas Widget
+  \sa canvas()
+ */
+void QwtPlot::setCanvas( QWidget *canvas )
+{
+    if ( canvas == d_data->canvas )
+        return;
+
+    delete d_data->canvas;
+    d_data->canvas = canvas;
+
+    if ( canvas )
+    {
+        canvas->setParent( this );
+        canvas->installEventFilter( this );
+
+        if ( isVisible() )
+            canvas->show();
+    }
+}
+
+/*!
+  \brief Adds handling of layout requests
+  \param event Event
+
+  \return See QFrame::event()
+*/
+bool QwtPlot::event( QEvent *event )
+{
+    bool ok = QFrame::event( event );
+    switch ( event->type() )
+    {
+        case QEvent::LayoutRequest:
+            updateLayout();
+            break;
+        case QEvent::PolishRequest:
+            replot();
+            break;
+        default:;
+    }
+    return ok;
+}
+
+/*!
+  \brief Event filter
+
+  The plot handles the following events for the canvas:
+
+  - QEvent::Resize
+    The canvas margins might depend on its size
+
+  - QEvent::ContentsRectChange
+    The layout needs to be recalculated
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return See QFrame::eventFilter()
+
+  \sa updateCanvasMargins(), updateLayout()
+*/
+bool QwtPlot::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object == d_data->canvas )
+    {
+        if ( event->type() == QEvent::Resize )
+        {
+            updateCanvasMargins();
+        }
+        else if ( event->type() == QEvent::ContentsRectChange )
+        {
+            updateLayout();
+        }
+    }
+
+    return QFrame::eventFilter( object, event );
+}
+
+//! Replots the plot if autoReplot() is \c true.
+void QwtPlot::autoRefresh()
+{
+    if ( d_data->autoReplot )
+        replot();
+}
+
+/*!
+  \brief Set or reset the autoReplot option
+
+  If the autoReplot option is set, the plot will be
+  updated implicitly by manipulating member functions.
+  Since this may be time-consuming, it is recommended
+  to leave this option switched off and call replot()
+  explicitly if necessary.
+
+  The autoReplot option is set to false by default, which
+  means that the user has to call replot() in order to make
+  changes visible.
+  \param tf \c true or \c false. Defaults to \c true.
+  \sa replot()
+*/
+void QwtPlot::setAutoReplot( bool tf )
+{
+    d_data->autoReplot = tf;
+}
+
+/*! 
+  \return true if the autoReplot option is set.
+  \sa setAutoReplot()
+*/
+bool QwtPlot::autoReplot() const
+{
+    return d_data->autoReplot;
+}
+
+/*!
+  Change the plot's title
+  \param title New title
+*/
+void QwtPlot::setTitle( const QString &title )
+{
+    if ( title != d_data->titleLabel->text().text() )
+    {
+        d_data->titleLabel->setText( title );
+        updateLayout();
+    }
+}
+
+/*!
+  Change the plot's title
+  \param title New title
+*/
+void QwtPlot::setTitle( const QwtText &title )
+{
+    if ( title != d_data->titleLabel->text() )
+    {
+        d_data->titleLabel->setText( title );
+        updateLayout();
+    }
+}
+
+//! \return Title of the plot
+QwtText QwtPlot::title() const
+{
+    return d_data->titleLabel->text();
+}
+
+//! \return Title label widget.
+QwtTextLabel *QwtPlot::titleLabel()
+{
+    return d_data->titleLabel;
+}
+
+//! \return Title label widget.
+const QwtTextLabel *QwtPlot::titleLabel() const
+{
+    return d_data->titleLabel;
+}
+
+/*!
+  Change the text the footer 
+  \param text New text of the footer
+*/
+void QwtPlot::setFooter( const QString &text )
+{
+    if ( text != d_data->footerLabel->text().text() )
+    {
+        d_data->footerLabel->setText( text );
+        updateLayout();
+    }
+}
+
+/*!
+  Change the text the footer 
+  \param text New text of the footer
+*/
+void QwtPlot::setFooter( const QwtText &text )
+{
+    if ( text != d_data->footerLabel->text() )
+    {
+        d_data->footerLabel->setText( text );
+        updateLayout();
+    }
+}
+
+//! \return Text of the footer
+QwtText QwtPlot::footer() const
+{
+    return d_data->footerLabel->text();
+}
+
+//! \return Footer label widget.
+QwtTextLabel *QwtPlot::footerLabel()
+{
+    return d_data->footerLabel;
+}
+
+//! \return Footer label widget.
+const QwtTextLabel *QwtPlot::footerLabel() const
+{
+    return d_data->footerLabel;
+}
+
+/*!
+   \brief Assign a new plot layout
+
+   \param layout Layout()
+   \sa plotLayout()
+ */
+void QwtPlot::setPlotLayout( QwtPlotLayout *layout )
+{
+    if ( layout != d_data->layout )
+    {
+        delete d_data->layout;
+        d_data->layout = layout;
+
+        updateLayout();
+    }
+}
+
+//! \return the plot's layout
+QwtPlotLayout *QwtPlot::plotLayout()
+{
+    return d_data->layout;
+}
+
+//! \return the plot's layout
+const QwtPlotLayout *QwtPlot::plotLayout() const
+{
+    return d_data->layout;
+}
+
+/*!
+  \return the plot's legend
+  \sa insertLegend()
+*/
+QwtAbstractLegend *QwtPlot::legend()
+{
+    return d_data->legend;
+}
+
+/*!
+  \return the plot's legend
+  \sa insertLegend()
+*/
+const QwtAbstractLegend *QwtPlot::legend() const
+{
+    return d_data->legend;
+}
+
+
+/*!
+  \return the plot's canvas
+*/
+QWidget *QwtPlot::canvas()
+{
+    return d_data->canvas;
+}
+
+/*!
+  \return the plot's canvas
+*/
+const QWidget *QwtPlot::canvas() const
+{
+    return d_data->canvas;
+}
+
+/*!
+  \return Size hint for the plot widget
+  \sa minimumSizeHint()
+*/
+QSize QwtPlot::sizeHint() const
+{
+    int dw = 0;
+    int dh = 0;
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        if ( axisEnabled( axisId ) )
+        {
+            const int niceDist = 40;
+            const QwtScaleWidget *scaleWidget = axisWidget( axisId );
+            const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv();
+            const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count();
+
+            if ( axisId == yLeft || axisId == yRight )
+            {
+                int hDiff = ( majCnt - 1 ) * niceDist
+                    - scaleWidget->minimumSizeHint().height();
+                if ( hDiff > dh )
+                    dh = hDiff;
+            }
+            else
+            {
+                int wDiff = ( majCnt - 1 ) * niceDist
+                    - scaleWidget->minimumSizeHint().width();
+                if ( wDiff > dw )
+                    dw = wDiff;
+            }
+        }
+    }
+    return minimumSizeHint() + QSize( dw, dh );
+}
+
+/*!
+  \brief Return a minimum size hint
+*/
+QSize QwtPlot::minimumSizeHint() const
+{
+    QSize hint = d_data->layout->minimumSizeHint( this );
+    hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
+
+    return hint;
+}
+
+/*!
+  Resize and update internal layout
+  \param e Resize event
+*/
+void QwtPlot::resizeEvent( QResizeEvent *e )
+{
+    QFrame::resizeEvent( e );
+    updateLayout();
+}
+
+/*!
+  \brief Redraw the plot
+
+  If the autoReplot option is not set (which is the default)
+  or if any curves are attached to raw data, the plot has to
+  be refreshed explicitly in order to make changes visible.
+
+  \sa updateAxes(), setAutoReplot()
+*/
+void QwtPlot::replot()
+{
+    bool doAutoReplot = autoReplot();
+    setAutoReplot( false );
+
+    updateAxes();
+
+    /*
+      Maybe the layout needs to be updated, because of changed
+      axes labels. We need to process them here before painting
+      to avoid that scales and canvas get out of sync.
+     */
+    QApplication::sendPostedEvents( this, QEvent::LayoutRequest );
+
+    if ( d_data->canvas )
+    {
+        const bool ok = QMetaObject::invokeMethod( 
+            d_data->canvas, "replot", Qt::DirectConnection );
+        if ( !ok )
+        {
+            // fallback, when canvas has no a replot method
+            d_data->canvas->update( d_data->canvas->contentsRect() );
+        }
+    }
+
+    setAutoReplot( doAutoReplot );
+}
+
+/*!
+  \brief Adjust plot content to its current size.
+  \sa resizeEvent()
+*/
+void QwtPlot::updateLayout()
+{
+    d_data->layout->activate( this, contentsRect() );
+
+    QRect titleRect = d_data->layout->titleRect().toRect();
+    QRect footerRect = d_data->layout->footerRect().toRect();
+    QRect scaleRect[QwtPlot::axisCnt];
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+        scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect();
+    QRect legendRect = d_data->layout->legendRect().toRect();
+    QRect canvasRect = d_data->layout->canvasRect().toRect();
+
+    // resize and show the visible widgets
+
+    if ( !d_data->titleLabel->text().isEmpty() )
+    {
+        d_data->titleLabel->setGeometry( titleRect );
+        if ( !d_data->titleLabel->isVisibleTo( this ) )
+            d_data->titleLabel->show();
+    }
+    else
+        d_data->titleLabel->hide();
+
+    if ( !d_data->footerLabel->text().isEmpty() )
+    {
+        d_data->footerLabel->setGeometry( footerRect );
+        if ( !d_data->footerLabel->isVisibleTo( this ) )
+            d_data->footerLabel->show();
+    }
+    else
+        d_data->footerLabel->hide();
+
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        if ( axisEnabled( axisId ) )
+        {
+            axisWidget( axisId )->setGeometry( scaleRect[axisId] );
+
+#if 1
+            if ( axisId == xBottom || axisId == xTop )
+            {
+                // do we need this code any longer ???
+
+                QRegion r( scaleRect[axisId] );
+                if ( axisEnabled( yLeft ) )
+                    r = r.subtracted( QRegion( scaleRect[yLeft] ) );
+                if ( axisEnabled( yRight ) )
+                    r = r.subtracted( QRegion( scaleRect[yRight] ) );
+                r.translate( -scaleRect[ axisId ].x(),
+                    -scaleRect[axisId].y() );
+
+                axisWidget( axisId )->setMask( r );
+            }
+#endif
+            if ( !axisWidget( axisId )->isVisibleTo( this ) )
+                axisWidget( axisId )->show();
+        }
+        else
+            axisWidget( axisId )->hide();
+    }
+
+    if ( d_data->legend )
+    {
+        if ( d_data->legend->isEmpty() )
+        {
+            d_data->legend->hide();
+        }
+        else
+        {
+            d_data->legend->setGeometry( legendRect );
+            d_data->legend->show();
+        }
+    }
+
+    d_data->canvas->setGeometry( canvasRect );
+}
+
+/*!
+  \brief Calculate the canvas margins
+
+  \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
+  \param canvasRect Bounding rectangle where to paint
+  \param left Return parameter for the left margin
+  \param top Return parameter for the top margin
+  \param right Return parameter for the right margin
+  \param bottom Return parameter for the bottom margin
+
+  Plot items might indicate, that they need some extra space
+  at the borders of the canvas by the QwtPlotItem::Margins flag.
+
+  updateCanvasMargins(), QwtPlotItem::getCanvasMarginHint()
+ */
+void QwtPlot::getCanvasMarginsHint(
+    const QwtScaleMap maps[], const QRectF &canvasRect,
+    double &left, double &top, double &right, double &bottom) const
+{
+    left = top = right = bottom = -1.0;
+
+    const QwtPlotItemList& itmList = itemList();
+    for ( QwtPlotItemIterator it = itmList.begin();
+        it != itmList.end(); ++it )
+    {
+        const QwtPlotItem *item = *it;
+        if ( item->testItemAttribute( QwtPlotItem::Margins ) )
+        {
+            double m[ QwtPlot::axisCnt ];
+            item->getCanvasMarginHint(
+                maps[ item->xAxis() ], maps[ item->yAxis() ],
+                canvasRect, m[yLeft], m[xTop], m[yRight], m[xBottom] );
+
+            left = qMax( left, m[yLeft] );
+            top = qMax( top, m[xTop] );
+            right = qMax( right, m[yRight] );
+            bottom = qMax( bottom, m[xBottom] );
+        }
+    }
+}
+
+/*!
+  \brief Update the canvas margins
+
+  Plot items might indicate, that they need some extra space
+  at the borders of the canvas by the QwtPlotItem::Margins flag.
+
+  getCanvasMarginsHint(), QwtPlotItem::getCanvasMarginHint()
+ */
+void QwtPlot::updateCanvasMargins()
+{
+    QwtScaleMap maps[axisCnt];
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+        maps[axisId] = canvasMap( axisId );
+
+    double margins[axisCnt];
+    getCanvasMarginsHint( maps, canvas()->contentsRect(),
+        margins[yLeft], margins[xTop], margins[yRight], margins[xBottom] );
+    
+    bool doUpdate = false;
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        if ( margins[axisId] >= 0.0 )
+        {
+            const int m = qCeil( margins[axisId] );
+            plotLayout()->setCanvasMargin( m, axisId);
+            doUpdate = true;
+        }
+    }
+
+    if ( doUpdate )
+        updateLayout();
+}
+
+/*!
+  Redraw the canvas.
+  \param painter Painter used for drawing
+
+  \warning drawCanvas calls drawItems what is also used
+           for printing. Applications that like to add individual
+           plot items better overload drawItems()
+  \sa drawItems()
+*/
+void QwtPlot::drawCanvas( QPainter *painter )
+{
+    QwtScaleMap maps[axisCnt];
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+        maps[axisId] = canvasMap( axisId );
+
+    drawItems( painter, d_data->canvas->contentsRect(), maps );
+}
+
+/*!
+  Redraw the canvas items.
+
+  \param painter Painter used for drawing
+  \param canvasRect Bounding rectangle where to paint
+  \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
+
+  \note Usually canvasRect is contentsRect() of the plot canvas.
+        Due to a bug in Qt this rectangle might be wrong for certain 
+        frame styles ( f.e QFrame::Box ) and it might be necessary to 
+        fix the margins manually using QWidget::setContentsMargins()
+*/
+
+void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect,
+        const QwtScaleMap maps[axisCnt] ) const
+{
+    const QwtPlotItemList& itmList = itemList();
+    for ( QwtPlotItemIterator it = itmList.begin();
+        it != itmList.end(); ++it )
+    {
+        QwtPlotItem *item = *it;
+        if ( item && item->isVisible() )
+        {
+            painter->save();
+
+            painter->setRenderHint( QPainter::Antialiasing,
+                item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
+            painter->setRenderHint( QPainter::HighQualityAntialiasing,
+                item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+            item->draw( painter,
+                maps[item->xAxis()], maps[item->yAxis()],
+                canvasRect );
+
+            painter->restore();
+        }
+    }
+}
+
+/*!
+  \param axisId Axis
+  \return Map for the axis on the canvas. With this map pixel coordinates can
+          translated to plot coordinates and vice versa.
+  \sa QwtScaleMap, transform(), invTransform()
+
+*/
+QwtScaleMap QwtPlot::canvasMap( int axisId ) const
+{
+    QwtScaleMap map;
+    if ( !d_data->canvas )
+        return map;
+
+    map.setTransformation( axisScaleEngine( axisId )->transformation() );
+
+    const QwtScaleDiv &sd = axisScaleDiv( axisId );
+    map.setScaleInterval( sd.lowerBound(), sd.upperBound() );
+
+    if ( axisEnabled( axisId ) )
+    {
+        const QwtScaleWidget *s = axisWidget( axisId );
+        if ( axisId == yLeft || axisId == yRight )
+        {
+            double y = s->y() + s->startBorderDist() - d_data->canvas->y();
+            double h = s->height() - s->startBorderDist() - s->endBorderDist();
+            map.setPaintInterval( y + h, y );
+        }
+        else
+        {
+            double x = s->x() + s->startBorderDist() - d_data->canvas->x();
+            double w = s->width() - s->startBorderDist() - s->endBorderDist();
+            map.setPaintInterval( x, x + w );
+        }
+    }
+    else
+    {
+        const QRect &canvasRect = d_data->canvas->contentsRect();
+        if ( axisId == yLeft || axisId == yRight )
+        {
+            int top = 0;
+            if ( !plotLayout()->alignCanvasToScale( xTop ) )
+                top = plotLayout()->canvasMargin( xTop );
+
+            int bottom = 0;
+            if ( !plotLayout()->alignCanvasToScale( xBottom ) )
+                bottom = plotLayout()->canvasMargin( xBottom );
+
+            map.setPaintInterval( canvasRect.bottom() - bottom,
+                canvasRect.top() + top );
+        }
+        else
+        {
+            int left = 0;
+            if ( !plotLayout()->alignCanvasToScale( yLeft ) )
+                left = plotLayout()->canvasMargin( yLeft );
+
+            int right = 0;
+            if ( !plotLayout()->alignCanvasToScale( yRight ) )
+                right = plotLayout()->canvasMargin( yRight );
+
+            map.setPaintInterval( canvasRect.left() + left,
+                canvasRect.right() - right );
+        }
+    }
+
+    return map;
+}
+
+/*!
+  \brief Change the background of the plotting area
+
+  Sets brush to QPalette::Window of all color groups of
+  the palette of the canvas. Using canvas()->setPalette()
+  is a more powerful way to set these colors.
+
+  \param brush New background brush
+  \sa canvasBackground()
+*/
+void QwtPlot::setCanvasBackground( const QBrush &brush )
+{
+    QPalette pal = d_data->canvas->palette();
+    pal.setBrush( QPalette::Window, brush );
+
+    canvas()->setPalette( pal );
+}
+
+/*!
+  Nothing else than: canvas()->palette().brush(
+        QPalette::Normal, QPalette::Window);
+
+  \return Background brush of the plotting area.
+  \sa setCanvasBackground()
+*/
+QBrush QwtPlot::canvasBackground() const
+{
+    return canvas()->palette().brush(
+        QPalette::Normal, QPalette::Window );
+}
+
+/*!
+  \return \c true if the specified axis exists, otherwise \c false
+  \param axisId axis index
+ */
+bool QwtPlot::axisValid( int axisId )
+{
+    return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) );
+}
+
+/*!
+  \brief Insert a legend
+
+  If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend
+  the legend will be organized in one column from top to down.
+  Otherwise the legend items will be placed in a table
+  with a best fit number of columns from left to right.
+
+  insertLegend() will set the plot widget as parent for the legend.
+  The legend will be deleted in the destructor of the plot or when 
+  another legend is inserted.
+
+  Legends, that are not inserted into the layout of the plot widget
+  need to connect to the legendDataChanged() signal. Calling updateLegend()
+  initiates this signal for an initial update. When the application code
+  wants to implement its own layout this also needs to be done for
+  rendering plots to a document ( see QwtPlotRenderer ).
+
+  \param legend Legend
+  \param pos The legend's position. For top/left position the number
+             of columns will be limited to 1, otherwise it will be set to
+             unlimited.
+
+  \param ratio Ratio between legend and the bounding rectangle
+               of title, canvas and axes. The legend will be shrunk
+               if it would need more space than the given ratio.
+               The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
+               it will be reset to the default ratio.
+               The default vertical/horizontal ratio is 0.33/0.5.
+
+  \sa legend(), QwtPlotLayout::legendPosition(),
+      QwtPlotLayout::setLegendPosition()
+*/
+void QwtPlot::insertLegend( QwtAbstractLegend *legend,
+    QwtPlot::LegendPosition pos, double ratio )
+{
+    d_data->layout->setLegendPosition( pos, ratio );
+
+    if ( legend != d_data->legend )
+    {
+        if ( d_data->legend && d_data->legend->parent() == this )
+            delete d_data->legend;
+
+        d_data->legend = legend;
+
+        if ( d_data->legend )
+        {
+            connect( this, 
+                SIGNAL( legendDataChanged( 
+                    const QVariant &, const QList<QwtLegendData> & ) ),
+                d_data->legend, 
+                SLOT( updateLegend( 
+                    const QVariant &, const QList<QwtLegendData> & ) ) 
+            );
+
+            if ( d_data->legend->parent() != this )
+                d_data->legend->setParent( this );
+
+            qwtEnableLegendItems( this, false );
+            updateLegend();
+            qwtEnableLegendItems( this, true );
+
+            QwtLegend *lgd = qobject_cast<QwtLegend *>( legend );
+            if ( lgd )
+            {
+                switch ( d_data->layout->legendPosition() )
+                {
+                    case LeftLegend:
+                    case RightLegend:
+                    {
+                        if ( lgd->maxColumns() == 0     )
+                            lgd->setMaxColumns( 1 ); // 1 column: align vertical
+                        break;
+                    }
+                    case TopLegend:
+                    case BottomLegend:
+                    {
+                        lgd->setMaxColumns( 0 ); // unlimited
+                        break;
+                    }
+                    default:
+                        break;
+                }
+            }
+
+            QWidget *previousInChain = NULL;
+            switch ( d_data->layout->legendPosition() )
+            {
+                case LeftLegend:
+                {
+                    previousInChain = axisWidget( QwtPlot::xTop );
+                    break;
+                }
+                case TopLegend:
+                {
+                    previousInChain = this;
+                    break;
+                }
+                case RightLegend:
+                {
+                    previousInChain = axisWidget( QwtPlot::yRight );
+                    break;
+                }
+                case BottomLegend:
+                {
+                    previousInChain = footerLabel();
+                    break;
+                }
+            }
+
+            if ( previousInChain )
+                qwtSetTabOrder( previousInChain, legend, true );
+        }
+    }
+
+    updateLayout();
+}
+
+/*!
+  Emit legendDataChanged() for all plot item
+
+  \sa QwtPlotItem::legendData(), legendDataChanged()
+ */
+void QwtPlot::updateLegend()
+{
+    const QwtPlotItemList& itmList = itemList();
+    for ( QwtPlotItemIterator it = itmList.begin();
+        it != itmList.end(); ++it )
+    {
+        updateLegend( *it );
+    }
+}
+
+/*!
+  Emit legendDataChanged() for a plot item
+
+  \param plotItem Plot item
+  \sa QwtPlotItem::legendData(), legendDataChanged()
+ */
+void QwtPlot::updateLegend( const QwtPlotItem *plotItem )
+{
+    if ( plotItem == NULL )
+        return;
+
+    QList<QwtLegendData> legendData;
+
+    if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) )
+        legendData = plotItem->legendData();
+
+    const QVariant itemInfo = itemToInfo( const_cast< QwtPlotItem *>( plotItem) );
+    Q_EMIT legendDataChanged( itemInfo, legendData );
+}
+
+/*!
+  \brief Update all plot items interested in legend attributes
+
+  Call QwtPlotItem::updateLegend(), when the QwtPlotItem::LegendInterest
+  flag is set.
+
+  \param itemInfo Info about the plot item
+  \param legendData Entries to be displayed for the plot item ( usually 1 )
+
+  \sa QwtPlotItem::LegendInterest,
+      QwtPlotLegendItem, QwtPlotItem::updateLegend()
+ */
+void QwtPlot::updateLegendItems( const QVariant &itemInfo,
+    const QList<QwtLegendData> &legendData )
+{
+    QwtPlotItem *plotItem = infoToItem( itemInfo );
+    if ( plotItem )
+    {
+        const QwtPlotItemList& itmList = itemList();
+        for ( QwtPlotItemIterator it = itmList.begin();
+            it != itmList.end(); ++it )
+        {
+            QwtPlotItem *item = *it;
+            if ( item->testItemInterest( QwtPlotItem::LegendInterest ) )
+                item->updateLegend( plotItem, legendData );
+        }
+    }
+}
+
+/*!
+  \brief Attach/Detach a plot item 
+
+  \param plotItem Plot item
+  \param on When true attach the item, otherwise detach it
+ */
+void QwtPlot::attachItem( QwtPlotItem *plotItem, bool on )
+{
+    if ( plotItem->testItemInterest( QwtPlotItem::LegendInterest ) )
+    {
+        // plotItem is some sort of legend
+
+        const QwtPlotItemList& itmList = itemList();
+        for ( QwtPlotItemIterator it = itmList.begin();
+            it != itmList.end(); ++it )
+        {
+            QwtPlotItem *item = *it;
+
+            QList<QwtLegendData> legendData;
+            if ( on && item->testItemAttribute( QwtPlotItem::Legend ) )
+            {
+                legendData = item->legendData();
+                plotItem->updateLegend( item, legendData );
+            }
+        }
+    }
+
+    if ( on )
+        insertItem( plotItem );
+    else 
+        removeItem( plotItem );
+
+    Q_EMIT itemAttached( plotItem, on );
+
+    if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) )
+    {
+        // the item wants to be represented on the legend
+
+        if ( on )
+        {
+            updateLegend( plotItem );
+        }
+        else
+        {
+            const QVariant itemInfo = itemToInfo( plotItem );
+            Q_EMIT legendDataChanged( itemInfo, QList<QwtLegendData>() );
+        }
+    }
+
+    autoRefresh();
+}
+
+/*!
+  \brief Build an information, that can be used to identify
+         a plot item on the legend.
+
+  The default implementation simply wraps the plot item
+  into a QVariant object. When overloading itemToInfo()
+  usually infoToItem() needs to reimplemeted too.
+
+\code
+    QVariant itemInfo;
+    qVariantSetValue( itemInfo, plotItem );
+\endcode
+
+  \param plotItem Plot item
+  \return Plot item embedded in a QVariant
+  \sa infoToItem()
+ */
+QVariant QwtPlot::itemToInfo( QwtPlotItem *plotItem ) const
+{
+    QVariant itemInfo;
+    qVariantSetValue( itemInfo, plotItem );
+
+    return itemInfo;
+}
+
+/*!
+  \brief Identify the plot item according to an item info object,
+         that has bee generated from itemToInfo().
+
+  The default implementation simply tries to unwrap a QwtPlotItem 
+  pointer:
+
+\code
+    if ( itemInfo.canConvert<QwtPlotItem *>() )
+        return qvariant_cast<QwtPlotItem *>( itemInfo );
+\endcode
+  \param itemInfo Plot item
+  \return A plot item, when successful, otherwise a NULL pointer.
+  \sa itemToInfo()
+*/
+QwtPlotItem *QwtPlot::infoToItem( const QVariant &itemInfo ) const
+{
+    if ( itemInfo.canConvert<QwtPlotItem *>() )
+        return qvariant_cast<QwtPlotItem *>( itemInfo );
+
+    return NULL;
+}
+
+
diff --git a/source/third_party/qwt/qwt_plot_abstract_barchart.cpp b/source/third_party/qwt/qwt_plot_abstract_barchart.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4fe2634aed5db91711a5625e5af7258eb9e39a86
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_abstract_barchart.cpp
@@ -0,0 +1,368 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_abstract_barchart.h"
+#include "qwt/qwt_scale_map.h"
+
+static inline double qwtTransformWidth(
+    const QwtScaleMap &map, double value, double width )
+{
+    const double w2 = 0.5 * width;
+
+    const double v1 = map.transform( value - w2 );
+    const double v2 = map.transform( value + w2 );
+
+    return qAbs( v2 - v1 );
+}
+
+class QwtPlotAbstractBarChart::PrivateData
+{
+public:
+    PrivateData():
+        layoutPolicy( QwtPlotAbstractBarChart::AutoAdjustSamples ),
+        layoutHint( 0.5 ),
+        spacing( 10 ),
+        margin( 5 ),
+        baseline( 0.0 )
+    {
+    }
+
+    QwtPlotAbstractBarChart::LayoutPolicy layoutPolicy;
+    double layoutHint;
+    int spacing;
+    int margin;
+    double baseline;
+};
+
+/*!
+  Constructor
+  \param title Title of the chart
+*/
+QwtPlotAbstractBarChart::QwtPlotAbstractBarChart( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    d_data = new PrivateData;
+
+    setItemAttribute( QwtPlotItem::Legend, true );
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Margins, true );
+    setZ( 19.0 );
+}
+
+//! Destructor
+QwtPlotAbstractBarChart::~QwtPlotAbstractBarChart()
+{
+    delete d_data;
+}
+
+/*!
+  The combination of layoutPolicy() and layoutHint() define how the width
+  of the bars is calculated
+
+  \param policy Layout policy
+
+  \sa layoutPolicy(), layoutHint()
+ */
+void QwtPlotAbstractBarChart::setLayoutPolicy( LayoutPolicy policy )
+{
+    if ( policy != d_data->layoutPolicy )
+    {
+        d_data->layoutPolicy = policy;
+        itemChanged();
+    }
+}
+
+/*!
+  The combination of layoutPolicy() and layoutHint() define how the width
+  of the bars is calculated
+
+  \return Layout policy of the chart item
+  \sa setLayoutPolicy(), layoutHint()
+ */
+QwtPlotAbstractBarChart::LayoutPolicy QwtPlotAbstractBarChart::layoutPolicy() const
+{
+    return d_data->layoutPolicy;
+}
+
+/*!
+  The combination of layoutPolicy() and layoutHint() define how the width
+  of the bars is calculated
+
+  \param hint Layout hint
+
+  \sa LayoutPolicy, layoutPolicy(), layoutHint()
+ */
+void QwtPlotAbstractBarChart::setLayoutHint( double hint )
+{
+    hint = qMax( 0.0, hint );
+    if ( hint != d_data->layoutHint )
+    {
+        d_data->layoutHint = hint;
+        itemChanged();
+    }
+}
+
+/*!
+  The combination of layoutPolicy() and layoutHint() define how the width
+  of the bars is calculated
+
+  \return Layout policy of the chart item
+  \sa LayoutPolicy, setLayoutHint(), layoutPolicy()
+*/
+double QwtPlotAbstractBarChart::layoutHint() const
+{
+    return d_data->layoutHint;
+}
+
+/*!
+  \brief Set the spacing
+
+  The spacing is the distance between 2 samples ( bars for QwtPlotBarChart or
+  a group of bars for QwtPlotMultiBarChart ) in paint device coordinates.
+
+  \sa spacing()
+ */
+void QwtPlotAbstractBarChart::setSpacing( int spacing )
+{
+    spacing = qMax( spacing, 0 );
+    if ( spacing != d_data->spacing )
+    {
+        d_data->spacing = spacing;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Spacing between 2 samples ( bars or groups of bars )
+  \sa setSpacing(), margin()
+ */
+int QwtPlotAbstractBarChart::spacing() const
+{
+    return d_data->spacing;
+}
+/*!
+  \brief Set the margin
+
+  The margin is the distance between the outmost bars and the contentsRect()
+  of the canvas. The default setting is 5 pixels.
+
+  \param margin Margin
+
+  \sa spacing(), margin()
+ */
+void QwtPlotAbstractBarChart::setMargin( int margin )
+{
+    margin = qMax( margin, 0 );
+    if ( margin != d_data->margin )
+    {
+        d_data->margin = margin;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin between the outmost bars and the contentsRect()
+  of the canvas.
+
+  \sa setMargin(), spacing()
+ */
+int QwtPlotAbstractBarChart::margin() const
+{
+    return d_data->margin;
+}
+
+/*!
+   \brief Set the baseline
+
+   The baseline is the origin for the chart. Each bar is 
+   painted from the baseline in the direction of the sample 
+   value. In case of a horizontal orientation() the baseline
+   is interpreted as x - otherwise as y - value.
+
+   The default value for the baseline is 0.
+
+   \param value Value for the baseline
+
+   \sa baseline(), QwtPlotSeriesItem::orientation()
+*/
+void QwtPlotAbstractBarChart::setBaseline( double value )
+{
+    if ( value != d_data->baseline )
+    {
+        d_data->baseline = value;
+        itemChanged();
+    }
+}
+
+/*! 
+   \return Value for the origin of the bar chart
+   \sa setBaseline(), QwtPlotSeriesItem::orientation()
+ */
+double QwtPlotAbstractBarChart::baseline() const
+{
+    return d_data->baseline;
+}
+
+/*!
+   Calculate the width for a sample in paint device coordinates
+
+   \param map Scale map for the corresponding scale
+   \param canvasSize Size of the canvas in paint device coordinates
+   \param boundingSize Bounding size of the chart in plot coordinates
+                       ( used in AutoAdjustSamples mode )
+   \param value Value of the sample
+
+   \return Sample width
+   \sa layoutPolicy(), layoutHint()
+*/
+double QwtPlotAbstractBarChart::sampleWidth( const QwtScaleMap &map,
+    double canvasSize, double boundingSize, double value ) const
+{
+    double width;
+
+    switch( d_data->layoutPolicy )
+    {
+        case ScaleSamplesToAxes:
+        {
+            width = qwtTransformWidth( map, value, d_data->layoutHint );
+            break;
+        }
+        case ScaleSampleToCanvas:
+        {
+            width = canvasSize * d_data->layoutHint;
+            break;
+        }
+        case FixedSampleSize:
+        {
+            width = d_data->layoutHint;
+            break;
+        }
+        case AutoAdjustSamples:
+        default:
+        {
+            const size_t numSamples = dataSize();
+
+            double w = 1.0;
+            if ( numSamples > 1 )
+            {
+                w = qAbs( boundingSize / ( numSamples - 1 ) );
+            }
+
+            width = qwtTransformWidth( map, value, w );
+            width -= d_data->spacing;
+            width = qMax( width, d_data->layoutHint );
+        }
+    }
+
+    return width;
+}
+
+/*!
+   \brief Calculate a hint for the canvas margin
+
+   Bar charts need to reserve some space for displaying the bars
+   for the first and the last sample.  The hint is calculated
+   from the layoutHint() depending on the layoutPolicy().
+
+   The margins are in target device coordinates ( pixels on screen )
+
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+   \param canvasRect Contents rectangle of the canvas in painter coordinates
+   \param left Returns the left margin
+   \param top Returns the top margin
+   \param right Returns the right margin
+   \param bottom Returns the bottom margin
+
+   \return Margin
+
+   \sa layoutPolicy(), layoutHint(), QwtPlotItem::Margins
+       QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins()
+ */
+void QwtPlotAbstractBarChart::getCanvasMarginHint( const QwtScaleMap &xMap, 
+    const QwtScaleMap &yMap, const QRectF &canvasRect,
+    double &left, double &top, double &right, double &bottom ) const
+{
+    double hint = -1.0;
+
+    switch( layoutPolicy() )
+    {
+        case ScaleSampleToCanvas:
+        {
+            if ( orientation() == Qt::Vertical )
+                hint = 0.5 * canvasRect.width() * d_data->layoutHint;
+            else
+                hint = 0.5 * canvasRect.height() * d_data->layoutHint;
+
+            break;
+        }
+        case FixedSampleSize:
+        {
+            hint = 0.5 * d_data->layoutHint;
+            break;
+        }
+        case AutoAdjustSamples:
+        case ScaleSamplesToAxes:
+        default:
+        {
+            const size_t numSamples = dataSize();
+            if ( numSamples <= 0 )
+                break;
+
+            // doesn't work for nonlinear scales
+
+            const QRectF br = dataRect();
+            double spacing = 0.0;
+            double sampleWidthS = 1.0;
+
+            if ( layoutPolicy() == ScaleSamplesToAxes )
+            {
+                sampleWidthS = qMax( d_data->layoutHint, 0.0 );
+            }
+            else
+            {
+                spacing = d_data->spacing;
+
+                if ( numSamples > 1 )
+                {
+                    sampleWidthS = qAbs( br.width() / ( numSamples - 1 ) );
+                }
+            }
+
+            double ds, w;
+            if ( orientation() == Qt::Vertical )
+            {
+                ds = qAbs( xMap.sDist() );
+                w = canvasRect.width();
+            }
+            else
+            {
+                ds = qAbs( yMap.sDist() );
+                w = canvasRect.height();
+            }
+
+            const double sampleWidthP = ( w - spacing * ( numSamples - 1 ) )
+                * sampleWidthS / ( ds + sampleWidthS );
+
+            hint = 0.5 * sampleWidthP;
+            hint += qMax( d_data->margin, 0 );
+        }
+    }
+
+    if ( orientation() == Qt::Vertical )
+    {
+        left = right = hint;
+        top = bottom = -1.0; // no hint
+    }
+    else
+    {
+        left = right = -1.0; // no hint
+        top = bottom = hint;
+    }
+}
diff --git a/source/third_party/qwt/qwt_plot_axis.cpp b/source/third_party/qwt/qwt_plot_axis.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..53d116c44d8c5f29b574878dde35af2b7bcda128
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_axis.cpp
@@ -0,0 +1,719 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_scale_widget.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_scale_engine.h"
+
+class QwtPlot::AxisData
+{
+public:
+    bool isEnabled;
+    bool doAutoScale;
+
+    double minValue;
+    double maxValue;
+    double stepSize;
+
+    int maxMajor;
+    int maxMinor;
+
+    bool isValid;
+
+    QwtScaleDiv scaleDiv;
+    QwtScaleEngine *scaleEngine;
+    QwtScaleWidget *scaleWidget;
+};
+
+//! Initialize axes
+void QwtPlot::initAxesData()
+{
+    int axisId;
+
+    for ( axisId = 0; axisId < axisCnt; axisId++ )
+        d_axisData[axisId] = new AxisData;
+
+    d_axisData[yLeft]->scaleWidget =
+        new QwtScaleWidget( QwtScaleDraw::LeftScale, this );
+    d_axisData[yRight]->scaleWidget =
+        new QwtScaleWidget( QwtScaleDraw::RightScale, this );
+    d_axisData[xTop]->scaleWidget =
+        new QwtScaleWidget( QwtScaleDraw::TopScale, this );
+    d_axisData[xBottom]->scaleWidget =
+        new QwtScaleWidget( QwtScaleDraw::BottomScale, this );
+
+    d_axisData[yLeft]->scaleWidget->setObjectName( "QwtPlotAxisYLeft" );
+    d_axisData[yRight]->scaleWidget->setObjectName( "QwtPlotAxisYRight" );
+    d_axisData[xTop]->scaleWidget->setObjectName( "QwtPlotAxisXTop" );
+    d_axisData[xBottom]->scaleWidget->setObjectName( "QwtPlotAxisXBottom" );
+
+#if 1
+    // better find the font sizes from the application font
+    QFont fscl( fontInfo().family(), 10 );
+    QFont fttl( fontInfo().family(), 12, QFont::Bold );
+#endif
+
+    for ( axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        d.scaleEngine = new QwtLinearScaleEngine;
+
+        d.scaleWidget->setTransformation( 
+            d.scaleEngine->transformation() );
+
+        d.scaleWidget->setFont( fscl );
+        d.scaleWidget->setMargin( 2 );
+
+        QwtText text = d.scaleWidget->title();
+        text.setFont( fttl );
+        d.scaleWidget->setTitle( text );
+
+        d.doAutoScale = true;
+
+        d.minValue = 0.0;
+        d.maxValue = 1000.0;
+        d.stepSize = 0.0;
+
+        d.maxMinor = 5;
+        d.maxMajor = 8;
+
+
+        d.isValid = false;
+    }
+
+    d_axisData[yLeft]->isEnabled = true;
+    d_axisData[yRight]->isEnabled = false;
+    d_axisData[xBottom]->isEnabled = true;
+    d_axisData[xTop]->isEnabled = false;
+}
+
+void QwtPlot::deleteAxesData()
+{
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        delete d_axisData[axisId]->scaleEngine;
+        delete d_axisData[axisId];
+        d_axisData[axisId] = NULL;
+    }
+}
+
+/*!
+  \return Scale widget of the specified axis, or NULL if axisId is invalid.
+  \param axisId Axis index
+*/
+const QwtScaleWidget *QwtPlot::axisWidget( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->scaleWidget;
+
+    return NULL;
+}
+
+/*!
+  \return Scale widget of the specified axis, or NULL if axisId is invalid.
+  \param axisId Axis index
+*/
+QwtScaleWidget *QwtPlot::axisWidget( int axisId )
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->scaleWidget;
+
+    return NULL;
+}
+
+/*!
+  Change the scale engine for an axis
+
+  \param axisId Axis index
+  \param scaleEngine Scale engine
+
+  \sa axisScaleEngine()
+*/
+void QwtPlot::setAxisScaleEngine( int axisId, QwtScaleEngine *scaleEngine )
+{
+    if ( axisValid( axisId ) && scaleEngine != NULL )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        delete d.scaleEngine;
+        d.scaleEngine = scaleEngine;
+
+        d_axisData[axisId]->scaleWidget->setTransformation( 
+            scaleEngine->transformation() );
+
+        d.isValid = false;
+
+        autoRefresh();
+    }
+}
+
+/*!
+  \param axisId Axis index
+  \return Scale engine for a specific axis
+*/
+QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId )
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->scaleEngine;
+    else
+        return NULL;
+}
+
+/*!
+  \param axisId Axis index
+  \return Scale engine for a specific axis
+*/
+const QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->scaleEngine;
+    else
+        return NULL;
+}
+/*!
+  \return \c True, if autoscaling is enabled
+  \param axisId Axis index
+*/
+bool QwtPlot::axisAutoScale( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->doAutoScale;
+    else
+        return false;
+
+}
+
+/*!
+  \return \c True, if a specified axis is enabled
+  \param axisId Axis index
+*/
+bool QwtPlot::axisEnabled( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->isEnabled;
+    else
+        return false;
+}
+
+/*!
+  \return The font of the scale labels for a specified axis
+  \param axisId Axis index
+*/
+QFont QwtPlot::axisFont( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return axisWidget( axisId )->font();
+    else
+        return QFont();
+
+}
+
+/*!
+  \return The maximum number of major ticks for a specified axis
+  \param axisId Axis index
+  \sa setAxisMaxMajor(), QwtScaleEngine::divideScale()
+*/
+int QwtPlot::axisMaxMajor( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->maxMajor;
+    else
+        return 0;
+}
+
+/*!
+  \return the maximum number of minor ticks for a specified axis
+  \param axisId Axis index
+  \sa setAxisMaxMinor(), QwtScaleEngine::divideScale()
+*/
+int QwtPlot::axisMaxMinor( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return d_axisData[axisId]->maxMinor;
+    else
+        return 0;
+}
+
+/*!
+  \brief Return the scale division of a specified axis
+
+  axisScaleDiv(axisId).lowerBound(), axisScaleDiv(axisId).upperBound()
+  are the current limits of the axis scale.
+
+  \param axisId Axis index
+  \return Scale division
+
+  \sa QwtScaleDiv, setAxisScaleDiv(), QwtScaleEngine::divideScale()
+*/
+const QwtScaleDiv &QwtPlot::axisScaleDiv( int axisId ) const
+{
+    return d_axisData[axisId]->scaleDiv;
+}
+
+/*!
+  \brief Return the scale draw of a specified axis
+
+  \param axisId Axis index
+  \return Specified scaleDraw for axis, or NULL if axis is invalid.
+*/
+const QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) const
+{
+    if ( !axisValid( axisId ) )
+        return NULL;
+
+    return axisWidget( axisId )->scaleDraw();
+}
+
+/*!
+  \brief Return the scale draw of a specified axis
+
+  \param axisId Axis index
+  \return Specified scaleDraw for axis, or NULL if axis is invalid.
+*/
+QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId )
+{
+    if ( !axisValid( axisId ) )
+        return NULL;
+
+    return axisWidget( axisId )->scaleDraw();
+}
+
+/*!
+  \brief Return the step size parameter that has been set in setAxisScale. 
+
+  This doesn't need to be the step size of the current scale.
+
+  \param axisId Axis index
+  \return step size parameter value
+
+   \sa setAxisScale(), QwtScaleEngine::divideScale()
+*/
+double QwtPlot::axisStepSize( int axisId ) const
+{
+    if ( !axisValid( axisId ) )
+        return 0;
+
+    return d_axisData[axisId]->stepSize;
+}
+
+/*!
+  \brief Return the current interval of the specified axis
+
+  This is only a convenience function for axisScaleDiv( axisId )->interval();
+  
+  \param axisId Axis index
+  \return Scale interval
+
+  \sa QwtScaleDiv, axisScaleDiv()
+*/
+QwtInterval QwtPlot::axisInterval( int axisId ) const
+{
+    if ( !axisValid( axisId ) )
+        return QwtInterval();
+
+    return d_axisData[axisId]->scaleDiv.interval();
+}
+
+/*!
+  \return Title of a specified axis
+  \param axisId Axis index
+*/
+QwtText QwtPlot::axisTitle( int axisId ) const
+{
+    if ( axisValid( axisId ) )
+        return axisWidget( axisId )->title();
+    else
+        return QwtText();
+}
+
+/*!
+  \brief Enable or disable a specified axis
+
+  When an axis is disabled, this only means that it is not
+  visible on the screen. Curves, markers and can be attached
+  to disabled axes, and transformation of screen coordinates
+  into values works as normal.
+
+  Only xBottom and yLeft are enabled by default.
+
+  \param axisId Axis index
+  \param tf \c true (enabled) or \c false (disabled)
+*/
+void QwtPlot::enableAxis( int axisId, bool tf )
+{
+    if ( axisValid( axisId ) && tf != d_axisData[axisId]->isEnabled )
+    {
+        d_axisData[axisId]->isEnabled = tf;
+        updateLayout();
+    }
+}
+
+/*!
+  Transform the x or y coordinate of a position in the
+  drawing region into a value.
+
+  \param axisId Axis index
+  \param pos position
+
+  \return Position as axis coordinate
+
+  \warning The position can be an x or a y coordinate,
+           depending on the specified axis.
+*/
+double QwtPlot::invTransform( int axisId, int pos ) const
+{
+    if ( axisValid( axisId ) )
+        return( canvasMap( axisId ).invTransform( pos ) );
+    else
+        return 0.0;
+}
+
+
+/*!
+  \brief Transform a value into a coordinate in the plotting region
+
+  \param axisId Axis index
+  \param value value
+  \return X or Y coordinate in the plotting region corresponding
+          to the value.
+*/
+double QwtPlot::transform( int axisId, double value ) const
+{
+    if ( axisValid( axisId ) )
+        return( canvasMap( axisId ).transform( value ) );
+    else
+        return 0.0;
+}
+
+/*!
+  \brief Change the font of an axis
+
+  \param axisId Axis index
+  \param font Font
+  \warning This function changes the font of the tick labels,
+           not of the axis title.
+*/
+void QwtPlot::setAxisFont( int axisId, const QFont &font )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setFont( font );
+}
+
+/*!
+  \brief Enable autoscaling for a specified axis
+
+  This member function is used to switch back to autoscaling mode
+  after a fixed scale has been set. Autoscaling is enabled by default.
+
+  \param axisId Axis index
+  \param on On/Off
+  \sa setAxisScale(), setAxisScaleDiv(), updateAxes()
+
+  \note The autoscaling flag has no effect until updateAxes() is executed
+        ( called by replot() ).
+*/
+void QwtPlot::setAxisAutoScale( int axisId, bool on )
+{
+    if ( axisValid( axisId ) && ( d_axisData[axisId]->doAutoScale != on ) )
+    {
+        d_axisData[axisId]->doAutoScale = on;
+        autoRefresh();
+    }
+}
+
+/*!
+  \brief Disable autoscaling and specify a fixed scale for a selected axis.
+
+  In updateAxes() the scale engine calculates a scale division from the 
+  specified parameters, that will be assigned to the scale widget. So 
+  updates of the scale widget usually happen delayed with the next replot.
+
+  \param axisId Axis index
+  \param min Minimum of the scale
+  \param max Maximum of the scale
+  \param stepSize Major step size. If <code>step == 0</code>, the step size is
+                  calculated automatically using the maxMajor setting.
+
+  \sa setAxisMaxMajor(), setAxisAutoScale(), axisStepSize(), QwtScaleEngine::divideScale()
+*/
+void QwtPlot::setAxisScale( int axisId, double min, double max, double stepSize )
+{
+    if ( axisValid( axisId ) )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        d.doAutoScale = false;
+        d.isValid = false;
+
+        d.minValue = min;
+        d.maxValue = max;
+        d.stepSize = stepSize;
+
+        autoRefresh();
+    }
+}
+
+/*!
+  \brief Disable autoscaling and specify a fixed scale for a selected axis.
+
+  The scale division will be stored locally only until the next call
+  of updateAxes(). So updates of the scale widget usually happen delayed with 
+  the next replot.
+
+  \param axisId Axis index
+  \param scaleDiv Scale division
+
+  \sa setAxisScale(), setAxisAutoScale()
+*/
+void QwtPlot::setAxisScaleDiv( int axisId, const QwtScaleDiv &scaleDiv )
+{
+    if ( axisValid( axisId ) )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        d.doAutoScale = false;
+        d.scaleDiv = scaleDiv;
+        d.isValid = true;
+
+        autoRefresh();
+    }
+}
+
+/*!
+  \brief Set a scale draw
+
+  \param axisId Axis index
+  \param scaleDraw Object responsible for drawing scales.
+
+  By passing scaleDraw it is possible to extend QwtScaleDraw
+  functionality and let it take place in QwtPlot. Please note
+  that scaleDraw has to be created with new and will be deleted
+  by the corresponding QwtScale member ( like a child object ).
+
+  \sa QwtScaleDraw, QwtScaleWidget
+  \warning The attributes of scaleDraw will be overwritten by those of the
+           previous QwtScaleDraw.
+*/
+
+void QwtPlot::setAxisScaleDraw( int axisId, QwtScaleDraw *scaleDraw )
+{
+    if ( axisValid( axisId ) )
+    {
+        axisWidget( axisId )->setScaleDraw( scaleDraw );
+        autoRefresh();
+    }
+}
+
+/*!
+  Change the alignment of the tick labels
+
+  \param axisId Axis index
+  \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h>
+
+  \sa QwtScaleDraw::setLabelAlignment()
+*/
+void QwtPlot::setAxisLabelAlignment( int axisId, Qt::Alignment alignment )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setLabelAlignment( alignment );
+}
+
+/*!
+  Rotate all tick labels
+
+  \param axisId Axis index
+  \param rotation Angle in degrees. When changing the label rotation,
+                  the label alignment might be adjusted too.
+
+  \sa QwtScaleDraw::setLabelRotation(), setAxisLabelAlignment()
+*/
+void QwtPlot::setAxisLabelRotation( int axisId, double rotation )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setLabelRotation( rotation );
+}
+
+/*!
+  Set the maximum number of minor scale intervals for a specified axis
+
+  \param axisId Axis index
+  \param maxMinor Maximum number of minor steps
+
+  \sa axisMaxMinor()
+*/
+void QwtPlot::setAxisMaxMinor( int axisId, int maxMinor )
+{
+    if ( axisValid( axisId ) )
+    {
+        maxMinor = qBound( 0, maxMinor, 100 );
+
+        AxisData &d = *d_axisData[axisId];
+        if ( maxMinor != d.maxMinor )
+        {
+            d.maxMinor = maxMinor;
+            d.isValid = false;
+            autoRefresh();
+        }
+    }
+}
+
+/*!
+  Set the maximum number of major scale intervals for a specified axis
+
+  \param axisId Axis index
+  \param maxMajor Maximum number of major steps
+
+  \sa axisMaxMajor()
+*/
+void QwtPlot::setAxisMaxMajor( int axisId, int maxMajor )
+{
+    if ( axisValid( axisId ) )
+    {
+        maxMajor = qBound( 1, maxMajor, 10000 );
+
+        AxisData &d = *d_axisData[axisId];
+        if ( maxMajor != d.maxMajor )
+        {
+            d.maxMajor = maxMajor;
+            d.isValid = false;
+            autoRefresh();
+        }
+    }
+}
+
+/*!
+  \brief Change the title of a specified axis
+
+  \param axisId Axis index
+  \param title axis title
+*/
+void QwtPlot::setAxisTitle( int axisId, const QString &title )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setTitle( title );
+}
+
+/*!
+  \brief Change the title of a specified axis
+
+  \param axisId Axis index
+  \param title Axis title
+*/
+void QwtPlot::setAxisTitle( int axisId, const QwtText &title )
+{
+    if ( axisValid( axisId ) )
+        axisWidget( axisId )->setTitle( title );
+}
+
+/*! 
+  \brief Rebuild the axes scales
+
+  In case of autoscaling the boundaries of a scale are calculated 
+  from the bounding rectangles of all plot items, having the 
+  QwtPlotItem::AutoScale flag enabled ( QwtScaleEngine::autoScale() ). 
+  Then a scale division is calculated ( QwtScaleEngine::didvideScale() ) 
+  and assigned to scale widget.
+
+  When the scale boundaries have been assigned with setAxisScale() a 
+  scale division is calculated ( QwtScaleEngine::didvideScale() )
+  for this interval and assigned to the scale widget.
+
+  When the scale has been set explicitly by setAxisScaleDiv() the 
+  locally stored scale division gets assigned to the scale widget.
+
+  The scale widget indicates modifications by emitting a 
+  QwtScaleWidget::scaleDivChanged() signal.
+
+  updateAxes() is usually called by replot(). 
+
+  \sa setAxisAutoScale(), setAxisScale(), setAxisScaleDiv(), replot()
+      QwtPlotItem::boundingRect()
+ */
+void QwtPlot::updateAxes()
+{
+    // Find bounding interval of the item data
+    // for all axes, where autoscaling is enabled
+
+    QwtInterval intv[axisCnt];
+
+    const QwtPlotItemList& itmList = itemList();
+
+    QwtPlotItemIterator it;
+    for ( it = itmList.begin(); it != itmList.end(); ++it )
+    {
+        const QwtPlotItem *item = *it;
+
+        if ( !item->testItemAttribute( QwtPlotItem::AutoScale ) )
+            continue;
+
+        if ( !item->isVisible() )
+            continue;
+
+        if ( axisAutoScale( item->xAxis() ) || axisAutoScale( item->yAxis() ) )
+        {
+            const QRectF rect = item->boundingRect();
+
+            if ( rect.width() >= 0.0 )
+                intv[item->xAxis()] |= QwtInterval( rect.left(), rect.right() );
+
+            if ( rect.height() >= 0.0 )
+                intv[item->yAxis()] |= QwtInterval( rect.top(), rect.bottom() );
+        }
+    }
+
+    // Adjust scales
+
+    for ( int axisId = 0; axisId < axisCnt; axisId++ )
+    {
+        AxisData &d = *d_axisData[axisId];
+
+        double minValue = d.minValue;
+        double maxValue = d.maxValue;
+        double stepSize = d.stepSize;
+
+        if ( d.doAutoScale && intv[axisId].isValid() )
+        {
+            d.isValid = false;
+
+            minValue = intv[axisId].minValue();
+            maxValue = intv[axisId].maxValue();
+
+            d.scaleEngine->autoScale( d.maxMajor,
+                minValue, maxValue, stepSize );
+        }
+        if ( !d.isValid )
+        {
+            d.scaleDiv = d.scaleEngine->divideScale(
+                minValue, maxValue,
+                d.maxMajor, d.maxMinor, stepSize );
+            d.isValid = true;
+        }
+
+        QwtScaleWidget *scaleWidget = axisWidget( axisId );
+        scaleWidget->setScaleDiv( d.scaleDiv );
+
+        int startDist, endDist;
+        scaleWidget->getBorderDistHint( startDist, endDist );
+        scaleWidget->setBorderDist( startDist, endDist );
+    }
+
+    for ( it = itmList.begin(); it != itmList.end(); ++it )
+    {
+        QwtPlotItem *item = *it;
+        if ( item->testItemInterest( QwtPlotItem::ScaleInterest ) )
+        {
+            item->updateScaleDiv( axisScaleDiv( item->xAxis() ),
+                axisScaleDiv( item->yAxis() ) );
+        }
+    }
+}
+
diff --git a/source/third_party/qwt/qwt_plot_barchart.cpp b/source/third_party/qwt/qwt_plot_barchart.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..729140f657d1d901d9e568f4018a6a0290d6cf99
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_barchart.cpp
@@ -0,0 +1,459 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_barchart.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_column_symbol.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+
+class QwtPlotBarChart::PrivateData
+{
+public:
+    PrivateData():
+        symbol( NULL ),
+        legendMode( QwtPlotBarChart::LegendChartTitle )
+    {
+    }
+ 
+    ~PrivateData()
+    {
+        delete symbol;
+    }
+
+    QwtColumnSymbol *symbol;
+    QwtPlotBarChart::LegendMode legendMode;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotBarChart::QwtPlotBarChart( const QwtText &title ):
+    QwtPlotAbstractBarChart( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotBarChart::QwtPlotBarChart( const QString &title ):
+    QwtPlotAbstractBarChart( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotBarChart::~QwtPlotBarChart()
+{
+    delete d_data;
+}
+
+void QwtPlotBarChart::init()
+{
+    d_data = new PrivateData;
+    setData( new QwtPointSeriesData() );
+}
+
+//! \return QwtPlotItem::Rtti_PlotBarChart
+int QwtPlotBarChart::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotBarChart;
+}
+
+/*!
+  Initialize data with an array of points
+
+  \param samples Vector of points
+  \note QVector is implicitly shared
+  \note QPolygonF is derived from QVector<QPointF>
+*/
+void QwtPlotBarChart::setSamples(
+    const QVector<QPointF> &samples )
+{
+    setData( new QwtPointSeriesData( samples ) );
+}
+
+/*!
+  Initialize data with an array of doubles
+
+  The indices in the array are taken as x coordinate,
+  while the doubles are interpreted as y values.
+
+  \param samples Vector of y coordinates
+  \note QVector is implicitly shared
+*/
+void QwtPlotBarChart::setSamples(
+    const QVector<double> &samples )
+{
+    QVector<QPointF> points;
+    for ( int i = 0; i < samples.size(); i++ )
+        points += QPointF( i, samples[ i ] );
+
+    setData( new QwtPointSeriesData( points ) );
+}
+
+/*!
+  Assign a series of samples
+
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/
+void QwtPlotBarChart::setSamples( QwtSeriesData<QPointF> *data )
+{
+    setData( data );
+}
+
+/*!
+  \brief Assign a symbol
+
+  The bar chart will take the ownership of the symbol, hence the previously
+  set symbol will be delete by setting a new one. If \p symbol is 
+  \c NULL no symbol will be drawn.
+
+  \param symbol Symbol
+  \sa symbol()
+*/
+void QwtPlotBarChart::setSymbol( QwtColumnSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Current symbol or NULL, when no symbol has been assigned
+  \sa setSymbol()
+*/
+const QwtColumnSymbol *QwtPlotBarChart::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  Set the mode that decides what to display on the legend
+
+  In case of LegendBarTitles barTitle() needs to be overloaded
+  to return individual titles for each bar.
+
+  \param mode New mode
+  \sa legendMode(), legendData(), barTitle(), QwtPlotItem::ItemAttribute
+ */
+void QwtPlotBarChart::setLegendMode( LegendMode mode )
+{
+    if ( mode != d_data->legendMode )
+    {
+        d_data->legendMode = mode;
+        legendChanged();
+    }
+}
+
+/*!
+  \return Legend mode
+  \sa setLegendMode()
+ */
+QwtPlotBarChart::LegendMode QwtPlotBarChart::legendMode() const
+{
+    return d_data->legendMode;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotBarChart::boundingRect() const
+{
+    const size_t numSamples = dataSize();
+    if ( numSamples == 0 )
+        return QwtPlotSeriesItem::boundingRect();
+
+    QRectF rect = QwtPlotSeriesItem::boundingRect();
+    if ( rect.height() >= 0 )
+    {
+        const double baseLine = baseline();
+
+        if ( rect.bottom() < baseLine )
+            rect.setBottom( baseLine );
+
+        if ( rect.top() > baseLine )
+            rect.setTop( baseLine );
+    }
+
+    if ( orientation() == Qt::Horizontal )
+        rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
+
+    return rect;
+}
+
+/*!
+  Draw an interval of the bar chart
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rect of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         curve will be painted to its last point.
+
+  \sa drawSymbols()
+*/
+void QwtPlotBarChart::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+
+    const QRectF br = data()->boundingRect();
+    const QwtInterval interval( br.left(), br.right() );
+
+    painter->save();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        drawSample( painter, xMap, yMap,
+                    canvasRect, interval, i, sample( i ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw a sample
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rect of the canvas
+  \param boundingInterval Bounding interval of sample values
+  \param index Index of the sample
+  \param sample Value of the sample
+
+  \sa drawSeries()
+*/
+void QwtPlotBarChart::drawSample( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, const QwtInterval &boundingInterval,
+    int index, const QPointF &sample ) const
+{
+    QwtColumnRect barRect;
+
+    if ( orientation() == Qt::Horizontal )
+    {
+        const double barHeight = sampleWidth( yMap, canvasRect.height(),
+            boundingInterval.width(), sample.y() );
+
+        const double x1 = xMap.transform( baseline() );
+        const double x2 = xMap.transform( sample.y() );
+
+        const double y = yMap.transform( sample.x() );
+        const double y1 = y - 0.5 * barHeight;
+        const double y2 = y + 0.5 * barHeight;
+
+        barRect.direction = ( x1 < x2 ) ?
+            QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
+
+        barRect.hInterval = QwtInterval( x1, x2 ).normalized();
+        barRect.vInterval = QwtInterval( y1, y2 );
+    }
+    else
+    {
+        const double barWidth = sampleWidth( xMap, canvasRect.width(),
+            boundingInterval.width(), sample.y() );
+
+        const double x = xMap.transform( sample.x() );
+        const double x1 = x - 0.5 * barWidth;
+        const double x2 = x + 0.5 * barWidth;
+
+        const double y1 = yMap.transform( baseline() );
+        const double y2 = yMap.transform( sample.y() );
+
+        barRect.direction = ( y1 < y2 ) ?
+            QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
+
+        barRect.hInterval = QwtInterval( x1, x2 );
+        barRect.vInterval = QwtInterval( y1, y2 ).normalized();
+    }
+
+    drawBar( painter, index, sample, barRect );
+}
+
+/*!
+  Draw a bar 
+
+  \param painter Painter
+  \param sampleIndex Index of the sample represented by the bar
+  \param sample Value of the sample
+  \param rect Bounding rectangle of the bar
+ */
+void QwtPlotBarChart::drawBar( QPainter *painter,
+    int sampleIndex, const QPointF &sample, 
+    const QwtColumnRect &rect ) const
+{
+    const QwtColumnSymbol *specialSym = 
+        specialSymbol( sampleIndex, sample );
+
+    const QwtColumnSymbol *sym = specialSym;
+    if ( sym == NULL )
+        sym = d_data->symbol;
+
+    if ( sym )
+    {
+        sym->draw( painter, rect );
+    }
+    else
+    {
+        // we build a temporary default symbol
+        QwtColumnSymbol sym( QwtColumnSymbol::Box );
+        sym.setLineWidth( 1 );
+        sym.setFrameStyle( QwtColumnSymbol::Plain );
+        sym.draw( painter, rect );
+    }
+
+    delete specialSym;
+}
+
+/*!
+  Needs to be overloaded to return a 
+  non default symbol for a specific sample
+
+  \param sampleIndex Index of the sample represented by the bar
+  \param sample Value of the sample
+
+  \return NULL, indicating to use the default symbol
+ */
+QwtColumnSymbol *QwtPlotBarChart::specialSymbol( 
+    int sampleIndex, const QPointF &sample ) const
+{
+    Q_UNUSED( sampleIndex );
+    Q_UNUSED( sample );
+
+    return NULL;
+}
+
+/*!
+  \brief Return the title of a bar
+
+  In LegendBarTitles mode the title is displayed on
+  the legend entry corresponding to a bar.
+
+  The default implementation is a dummy, that is intended
+  to be overloaded.
+
+  \param sampleIndex Index of the bar
+  \return An empty text
+  \sa LegendBarTitles
+ */
+QwtText QwtPlotBarChart::barTitle( int sampleIndex ) const
+{
+    Q_UNUSED( sampleIndex );
+    return QwtText();
+}
+
+/*!
+   \brief Return all information, that is needed to represent
+          the item on the legend
+
+   In case of LegendBarTitles an entry for each bar is returned,
+   otherwise the chart is represented like any other plot item
+   from its title() and the legendIcon().
+
+   \return Information, that is needed to represent the item on the legend
+   \sa title(), setLegendMode(), barTitle(), QwtLegend, QwtPlotLegendItem
+ */
+QList<QwtLegendData> QwtPlotBarChart::legendData() const
+{
+    QList<QwtLegendData> list;
+
+    if ( d_data->legendMode == LegendBarTitles )
+    {
+        const size_t numSamples = dataSize();
+        for ( size_t i = 0; i < numSamples; i++ )
+        {
+            QwtLegendData data;
+
+            QVariant titleValue;
+            qVariantSetValue( titleValue, barTitle( i ) );
+            data.setValue( QwtLegendData::TitleRole, titleValue );
+
+            if ( !legendIconSize().isEmpty() )
+            {
+                QVariant iconValue;
+                qVariantSetValue( iconValue,
+                    legendIcon( i, legendIconSize() ) );
+
+                data.setValue( QwtLegendData::IconRole, iconValue );
+            }
+
+            list += data;
+        }
+    }
+    else
+    {
+        return QwtPlotAbstractBarChart::legendData();
+    }
+
+    return list;
+}
+
+/*!
+   \return Icon representing a bar or the chart on the legend
+
+   When the legendMode() is LegendBarTitles the icon shows
+   the bar corresponding to index - otherwise the bar
+   displays the default symbol.
+
+   \param index Index of the legend entry 
+   \param size Icon size
+
+   \sa setLegendMode(), drawBar(), 
+       QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
+ */
+QwtGraphic QwtPlotBarChart::legendIcon( 
+    int index, const QSizeF &size ) const
+{
+    QwtColumnRect column;
+    column.hInterval = QwtInterval( 0.0, size.width() - 1.0 );
+    column.vInterval = QwtInterval( 0.0, size.height() - 1.0 );
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+    icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &icon );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    int barIndex = -1;
+    if ( d_data->legendMode == QwtPlotBarChart::LegendBarTitles )
+        barIndex = index;
+        
+    drawBar( &painter, barIndex, QPointF(), column );
+
+    return icon;
+}
diff --git a/source/third_party/qwt/qwt_plot_canvas.cpp b/source/third_party/qwt/qwt_plot_canvas.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..31aa1c883c4f8b1dba96bca81f35aac0798768bb
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_canvas.cpp
@@ -0,0 +1,1101 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_canvas.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_null_paintdevice.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_plot.h"
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qpaintengine.h>
+#include <qevent.h>
+
+class QwtStyleSheetRecorder: public QwtNullPaintDevice
+{
+public:
+    QwtStyleSheetRecorder( const QSize &size ):
+        d_size( size )
+    {
+    }
+
+    virtual void updateState( const QPaintEngineState &state )
+    {
+        if ( state.state() & QPaintEngine::DirtyPen )
+        {
+            d_pen = state.pen();
+        }
+        if ( state.state() & QPaintEngine::DirtyBrush )
+        {
+            d_brush = state.brush();
+        }
+        if ( state.state() & QPaintEngine::DirtyBrushOrigin )
+        {
+            d_origin = state.brushOrigin();
+        }
+    }
+
+    virtual void drawRects(const QRectF *rects, int count )
+    {
+        for ( int i = 0; i < count; i++ )
+            border.rectList += rects[i];
+    }
+
+    virtual void drawPath( const QPainterPath &path )
+    {
+        const QRectF rect( QPointF( 0.0, 0.0 ), d_size );
+        if ( path.controlPointRect().contains( rect.center() ) )
+        {
+            setCornerRects( path );
+            alignCornerRects( rect );
+
+            background.path = path;
+            background.brush = d_brush;
+            background.origin = d_origin;
+        }
+        else
+        {
+            border.pathList += path;
+        }
+    }
+
+    void setCornerRects( const QPainterPath &path )
+    {
+        QPointF pos( 0.0, 0.0 );
+
+        for ( int i = 0; i < path.elementCount(); i++ )
+        {
+            QPainterPath::Element el = path.elementAt(i); 
+            switch( el.type )
+            {
+                case QPainterPath::MoveToElement:
+                case QPainterPath::LineToElement:
+                {
+                    pos.setX( el.x );
+                    pos.setY( el.y );
+                    break;
+                }
+                case QPainterPath::CurveToElement:
+                {
+                    QRectF r( pos, QPointF( el.x, el.y ) );
+                    clipRects += r.normalized();
+
+                    pos.setX( el.x );
+                    pos.setY( el.y );
+
+                    break;
+                }
+                case QPainterPath::CurveToDataElement:
+                {
+                    if ( clipRects.size() > 0 )
+                    {
+                        QRectF r = clipRects.last();
+                        r.setCoords( 
+                            qMin( r.left(), el.x ),
+                            qMin( r.top(), el.y ),
+                            qMax( r.right(), el.x ),
+                            qMax( r.bottom(), el.y )
+                        );
+                        clipRects.last() = r.normalized();
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+protected:
+    virtual QSize sizeMetrics() const
+    {
+        return d_size;
+    }
+
+private:
+    void alignCornerRects( const QRectF &rect )
+    {
+        for ( int i = 0; i < clipRects.size(); i++ )
+        {
+            QRectF &r = clipRects[i];
+            if ( r.center().x() < rect.center().x() )
+                r.setLeft( rect.left() );
+            else
+                r.setRight( rect.right() );
+
+            if ( r.center().y() < rect.center().y() )
+                r.setTop( rect.top() );
+            else
+                r.setBottom( rect.bottom() );
+        }
+    }
+
+
+public:
+    QVector<QRectF> clipRects;
+
+    struct Border
+    {
+        QList<QPainterPath> pathList;
+        QList<QRectF> rectList;
+        QRegion clipRegion;
+    } border;
+
+    struct Background
+    {
+        QPainterPath path;
+        QBrush brush;
+        QPointF origin;
+    } background;
+
+private:
+    const QSize d_size;
+
+    QPen d_pen;
+    QBrush d_brush;
+    QPointF d_origin;
+};
+
+static void qwtDrawBackground( QPainter *painter, QwtPlotCanvas *canvas )
+{
+    painter->save();
+
+    const QPainterPath borderClip = canvas->borderPath( canvas->rect() );
+    if ( !borderClip.isEmpty() )
+        painter->setClipPath( borderClip, Qt::IntersectClip );
+
+    const QBrush &brush = 
+        canvas->palette().brush( canvas->backgroundRole() );
+
+    if ( brush.style() == Qt::TexturePattern )
+    {
+        QPixmap pm( canvas->size() );
+        QwtPainter::fillPixmap( canvas, pm );
+        painter->drawPixmap( 0, 0, pm );
+    }
+    else if ( brush.gradient() )
+    {
+        QVector<QRect> rects;
+
+        if ( brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode )
+        {
+            rects += canvas->rect();
+        } 
+        else 
+        {
+            rects = painter->clipRegion().rects();
+        }
+
+#if 1
+        bool useRaster = false;
+
+        if ( painter->paintEngine()->type() == QPaintEngine::X11 )
+        {
+            // Qt 4.7.1: gradients on X11 are broken ( subrects + 
+            // QGradient::StretchToDeviceMode ) and horrible slow.
+            // As workaround we have to use the raster paintengine.
+            // Even if the QImage -> QPixmap translation is slow
+            // it is three times faster, than using X11 directly
+
+            useRaster = true;
+        }
+#endif
+        if ( useRaster )
+        {
+            QImage::Format format = QImage::Format_RGB32;
+
+            const QGradientStops stops = brush.gradient()->stops();
+            for ( int i = 0; i < stops.size(); i++ )
+            {
+                if ( stops[i].second.alpha() != 255 )
+                {
+                    // don't use Format_ARGB32_Premultiplied. It's
+                    // recommended by the Qt docs, but QPainter::drawImage()
+                    // is horrible slow on X11.
+
+                    format = QImage::Format_ARGB32;
+                    break;
+                }
+            }
+            
+            QImage image( canvas->size(), format );
+
+            QPainter p( &image );
+            p.setPen( Qt::NoPen );
+            p.setBrush( brush );
+
+            p.drawRects( rects );
+
+            p.end();
+
+            painter->drawImage( 0, 0, image );
+        }
+        else
+        {
+            painter->setPen( Qt::NoPen );
+            painter->setBrush( brush );
+
+            painter->drawRects( rects );
+        }
+    }
+    else
+    {
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( brush );
+
+        painter->drawRects( painter->clipRegion().rects() );
+
+    }
+
+    painter->restore();
+}
+
+static inline void qwtRevertPath( QPainterPath &path )
+{
+    if ( path.elementCount() == 4 )
+    {
+        QPainterPath::Element el0 = path.elementAt(0);
+        QPainterPath::Element el3 = path.elementAt(3);
+
+        path.setElementPositionAt( 0, el3.x, el3.y );
+        path.setElementPositionAt( 3, el0.x, el0.y );
+    }
+}
+
+static QPainterPath qwtCombinePathList( const QRectF &rect, 
+    const QList<QPainterPath> &pathList )
+{
+    if ( pathList.isEmpty() )
+        return QPainterPath();
+
+    QPainterPath ordered[8]; // starting top left
+
+    for ( int i = 0; i < pathList.size(); i++ )
+    {
+        int index = -1;
+        QPainterPath subPath = pathList[i];
+
+        const QRectF br = pathList[i].controlPointRect();
+        if ( br.center().x() < rect.center().x() )
+        {
+            if ( br.center().y() < rect.center().y() )
+            {
+                if ( qAbs( br.top() - rect.top() ) < 
+                    qAbs( br.left() - rect.left() ) )
+                {
+                    index = 1;
+                }
+                else
+                {
+                    index = 0;
+                }
+            }
+            else
+            {
+                if ( qAbs( br.bottom() - rect.bottom() ) < 
+                    qAbs( br.left() - rect.left() ) )
+                {
+                    index = 6;
+                }
+                else
+                {
+                    index = 7;
+                }
+            }
+
+            if ( subPath.currentPosition().y() > br.center().y() )
+                qwtRevertPath( subPath );
+        }
+        else
+        {
+            if ( br.center().y() < rect.center().y() )
+            {
+                if ( qAbs( br.top() - rect.top() ) < 
+                    qAbs( br.right() - rect.right() ) )
+                {
+                    index = 2;
+                }
+                else
+                {
+                    index = 3;
+                }
+            }
+            else
+            {
+                if ( qAbs( br.bottom() - rect.bottom() ) < 
+                    qAbs( br.right() - rect.right() ) )
+                {
+                    index = 5;
+                }
+                else
+                {
+                    index = 4;
+                }
+            }
+            if ( subPath.currentPosition().y() < br.center().y() )
+                qwtRevertPath( subPath );
+        }   
+        ordered[index] = subPath;
+    }
+
+    for ( int i = 0; i < 4; i++ )
+    {
+        if ( ordered[ 2 * i].isEmpty() != ordered[2 * i + 1].isEmpty() )
+        {
+            // we don't accept incomplete rounded borders
+            return QPainterPath();
+        }
+    }
+
+
+    const QPolygonF corners( rect );
+
+    QPainterPath path;
+    //path.moveTo( rect.topLeft() );
+
+    for ( int i = 0; i < 4; i++ )
+    {
+        if ( ordered[2 * i].isEmpty() )
+        {
+            path.lineTo( corners[i] );
+        }
+        else
+        {
+            path.connectPath( ordered[2 * i] );
+            path.connectPath( ordered[2 * i + 1] );
+        }
+    }
+
+    path.closeSubpath();
+
+#if 0
+    return path.simplified();
+#else
+    return path;
+#endif
+}
+
+static inline void qwtDrawStyledBackground( 
+    QWidget *w, QPainter *painter )
+{
+    QStyleOption opt;
+    opt.initFrom(w);
+    w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w);
+}
+
+static QWidget *qwtBackgroundWidget( QWidget *w )
+{
+    if ( w->parentWidget() == NULL )
+        return w;
+
+    if ( w->autoFillBackground() )
+    {
+        const QBrush brush = w->palette().brush( w->backgroundRole() );
+        if ( brush.color().alpha() > 0 )
+            return w;
+    }
+
+    if ( w->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        QImage image( 1, 1, QImage::Format_ARGB32 );
+        image.fill( Qt::transparent );
+
+        QPainter painter( &image );
+        painter.translate( -w->rect().center() );
+        qwtDrawStyledBackground( w, &painter );
+        painter.end();
+
+        if ( qAlpha( image.pixel( 0, 0 ) ) != 0 )
+            return w;
+    }
+
+    return qwtBackgroundWidget( w->parentWidget() );
+}
+
+static void qwtFillBackground( QPainter *painter, 
+    QWidget *widget, const QVector<QRectF> &fillRects )
+{
+    if ( fillRects.isEmpty() )
+        return;
+
+    QRegion clipRegion;
+    if ( painter->hasClipping() )
+        clipRegion = painter->transform().map( painter->clipRegion() );
+    else
+        clipRegion = widget->contentsRect();
+
+    // Try to find out which widget fills
+    // the unfilled areas of the styled background
+
+    QWidget *bgWidget = qwtBackgroundWidget( widget->parentWidget() );
+
+    for ( int i = 0; i < fillRects.size(); i++ )
+    {
+        const QRect rect = fillRects[i].toAlignedRect();
+        if ( clipRegion.intersects( rect ) )
+        {
+            QPixmap pm( rect.size() );
+            QwtPainter::fillPixmap( bgWidget, pm, widget->mapTo( bgWidget, rect.topLeft() ) );
+            painter->drawPixmap( rect, pm );
+        }
+    }
+}
+
+static void qwtFillBackground( QPainter *painter, QwtPlotCanvas *canvas )
+{
+    QVector<QRectF> rects;
+
+    if ( canvas->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        QwtStyleSheetRecorder recorder( canvas->size() );
+
+        QPainter p( &recorder );
+        qwtDrawStyledBackground( canvas, &p );
+        p.end();
+
+        if ( recorder.background.brush.isOpaque() )
+            rects = recorder.clipRects;
+        else
+            rects += canvas->rect();
+    }
+    else
+    {
+        const QRectF r = canvas->rect();
+        const double radius = canvas->borderRadius();
+        if ( radius > 0.0 )
+        {
+            QSizeF sz( radius, radius );
+
+            rects += QRectF( r.topLeft(), sz );
+            rects += QRectF( r.topRight() - QPointF( radius, 0 ), sz );
+            rects += QRectF( r.bottomRight() - QPointF( radius, radius ), sz );
+            rects += QRectF( r.bottomLeft() - QPointF( 0, radius ), sz );
+        }
+    }
+
+    qwtFillBackground( painter, canvas, rects);
+}
+
+
+class QwtPlotCanvas::PrivateData
+{
+public:
+    PrivateData():
+        focusIndicator( NoFocusIndicator ),
+        borderRadius( 0 ),
+        paintAttributes( 0 ),
+        backingStore( NULL )
+    {
+        styleSheet.hasBorder = false;
+    }
+
+    ~PrivateData()
+    {
+        delete backingStore;
+    }
+
+    FocusIndicator focusIndicator;
+    double borderRadius;
+    QwtPlotCanvas::PaintAttributes paintAttributes;
+    QPixmap *backingStore;
+
+    struct StyleSheet
+    {
+        bool hasBorder;
+        QPainterPath borderPath;
+        QVector<QRectF> cornerRects;
+
+        struct StyleSheetBackground
+        {
+            QBrush brush;
+            QPointF origin;
+        } background;
+
+    } styleSheet;
+
+};
+
+/*! 
+  \brief Constructor
+
+  \param plot Parent plot widget
+  \sa QwtPlot::setCanvas()
+*/
+QwtPlotCanvas::QwtPlotCanvas( QwtPlot *plot ):
+    QFrame( plot )
+{
+    setFrameStyle( QFrame::Panel | QFrame::Sunken );
+    setLineWidth( 2 );
+
+    d_data = new PrivateData;
+
+#ifndef QT_NO_CURSOR
+    setCursor( Qt::CrossCursor );
+#endif
+
+    setAutoFillBackground( true );
+    setPaintAttribute( QwtPlotCanvas::BackingStore, true );
+    setPaintAttribute( QwtPlotCanvas::Opaque, true );
+    setPaintAttribute( QwtPlotCanvas::HackStyledBackground, true );
+}
+
+//! Destructor
+QwtPlotCanvas::~QwtPlotCanvas()
+{
+    delete d_data;
+}
+
+//! Return parent plot widget
+QwtPlot *QwtPlotCanvas::plot()
+{
+    return qobject_cast<QwtPlot *>( parent() );
+}
+
+//! Return parent plot widget
+const QwtPlot *QwtPlotCanvas::plot() const
+{
+    return qobject_cast<const QwtPlot *>( parent() );
+}
+
+/*!
+  \brief Changing the paint attributes
+
+  \param attribute Paint attribute
+  \param on On/Off
+
+  \sa testPaintAttribute(), backingStore()
+*/
+void QwtPlotCanvas::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( bool( d_data->paintAttributes & attribute ) == on )
+        return;
+
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+
+    switch ( attribute )
+    {
+        case BackingStore:
+        {
+            if ( on )
+            {
+                if ( d_data->backingStore == NULL )
+                    d_data->backingStore = new QPixmap();
+
+                if ( isVisible() )
+                {
+#if QT_VERSION >= 0x050000
+                    *d_data->backingStore = grab( rect() );
+#else
+                    *d_data->backingStore = 
+                        QPixmap::grabWidget( this, rect() );
+#endif
+                }
+            }
+            else
+            {
+                delete d_data->backingStore;
+                d_data->backingStore = NULL;
+            }
+            break;
+        }
+        case Opaque:
+        {
+            if ( on )
+                setAttribute( Qt::WA_OpaquePaintEvent, true );
+
+            break;
+        }
+        case HackStyledBackground:
+        case ImmediatePaint:
+        {
+            break;
+        }
+    }
+}
+
+/*!
+  Test whether a paint attribute is enabled
+
+  \param attribute Paint attribute
+  \return true, when attribute is enabled
+  \sa setPaintAttribute()
+*/
+bool QwtPlotCanvas::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return d_data->paintAttributes & attribute;
+}
+
+//! \return Backing store, might be null
+const QPixmap *QwtPlotCanvas::backingStore() const
+{
+    return d_data->backingStore;
+}
+
+//! Invalidate the internal backing store
+void QwtPlotCanvas::invalidateBackingStore()
+{
+    if ( d_data->backingStore )
+        *d_data->backingStore = QPixmap();
+}
+
+/*!
+  Set the focus indicator
+
+  \sa FocusIndicator, focusIndicator()
+*/
+void QwtPlotCanvas::setFocusIndicator( FocusIndicator focusIndicator )
+{
+    d_data->focusIndicator = focusIndicator;
+}
+
+/*!
+  \return Focus indicator
+
+  \sa FocusIndicator, setFocusIndicator()
+*/
+QwtPlotCanvas::FocusIndicator QwtPlotCanvas::focusIndicator() const
+{
+    return d_data->focusIndicator;
+}
+
+/*!
+  Set the radius for the corners of the border frame
+
+  \param radius Radius of a rounded corner
+  \sa borderRadius()
+*/
+void QwtPlotCanvas::setBorderRadius( double radius )
+{
+    d_data->borderRadius = qMax( 0.0, radius );
+}
+
+/*!
+  \return Radius for the corners of the border frame
+  \sa setBorderRadius()
+*/
+double QwtPlotCanvas::borderRadius() const
+{
+    return d_data->borderRadius;
+}
+
+/*!
+  Qt event handler for QEvent::PolishRequest and QEvent::StyleChange
+
+  \param event Qt Event
+  \return See QFrame::event()
+*/
+bool QwtPlotCanvas::event( QEvent *event )
+{
+    if ( event->type() == QEvent::PolishRequest ) 
+    {
+        if ( testPaintAttribute( QwtPlotCanvas::Opaque ) )
+        {
+            // Setting a style sheet changes the 
+            // Qt::WA_OpaquePaintEvent attribute, but we insist
+            // on painting the background.
+            
+            setAttribute( Qt::WA_OpaquePaintEvent, true );
+        }
+    }
+
+    if ( event->type() == QEvent::PolishRequest || 
+        event->type() == QEvent::StyleChange )
+    {
+        updateStyleSheetInfo();
+    }
+
+    return QFrame::event( event );
+}
+
+/*!
+  Paint event
+  \param event Paint event
+*/
+void QwtPlotCanvas::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    if ( testPaintAttribute( QwtPlotCanvas::BackingStore ) &&
+        d_data->backingStore != NULL )
+    {
+        QPixmap &bs = *d_data->backingStore;
+        if ( bs.size() != size() )
+        {
+            bs = QwtPainter::backingStore( this, size() );
+
+            if ( testAttribute(Qt::WA_StyledBackground) )
+            {
+                QPainter p( &bs );
+                qwtFillBackground( &p, this );
+                drawCanvas( &p, true );
+            }
+            else
+            {
+                QPainter p;
+                if ( d_data->borderRadius <= 0.0 )
+                {
+                    QwtPainter::fillPixmap( this, bs );
+                    p.begin( &bs );
+                    drawCanvas( &p, false );
+                }
+                else
+                {
+                    p.begin( &bs );
+                    qwtFillBackground( &p, this );
+                    drawCanvas( &p, true );
+                }
+
+                if ( frameWidth() > 0 )
+                    drawBorder( &p );
+            }
+        }
+
+        painter.drawPixmap( 0, 0, *d_data->backingStore );
+    }
+    else
+    {
+        if ( testAttribute(Qt::WA_StyledBackground ) )
+        {
+            if ( testAttribute( Qt::WA_OpaquePaintEvent ) )
+            {
+                qwtFillBackground( &painter, this );
+                drawCanvas( &painter, true );
+            }
+            else
+            {
+                drawCanvas( &painter, false );
+            }
+        }
+        else
+        {
+            if ( testAttribute( Qt::WA_OpaquePaintEvent ) )
+            {
+                if ( autoFillBackground() )
+                {
+                    qwtFillBackground( &painter, this );
+                    qwtDrawBackground( &painter, this );
+                }
+            }
+            else
+            {
+                if ( borderRadius() > 0.0 )
+                {
+                    QPainterPath clipPath;
+                    clipPath.addRect( rect() );
+                    clipPath = clipPath.subtracted( borderPath( rect() ) );
+
+                    painter.save();
+
+                    painter.setClipPath( clipPath, Qt::IntersectClip );
+                    qwtFillBackground( &painter, this );
+                    qwtDrawBackground( &painter, this );
+
+                    painter.restore();
+                }
+            }
+
+            drawCanvas( &painter, false );
+
+            if ( frameWidth() > 0 ) 
+                drawBorder( &painter );
+        }
+    }
+
+    if ( hasFocus() && focusIndicator() == CanvasFocusIndicator )
+        drawFocusIndicator( &painter );
+}
+
+void QwtPlotCanvas::drawCanvas( QPainter *painter, bool withBackground ) 
+{
+    bool hackStyledBackground = false;
+
+    if ( withBackground && testAttribute( Qt::WA_StyledBackground ) 
+        && testPaintAttribute( HackStyledBackground ) )
+    {
+        // Antialiasing rounded borders is done by
+        // inserting pixels with colors between the 
+        // border color and the color on the canvas,
+        // When the border is painted before the plot items
+        // these colors are interpolated for the canvas
+        // and the plot items need to be clipped excluding
+        // the anialiased pixels. In situations, where
+        // the plot items fill the area at the rounded
+        // borders this is noticeable.
+        // The only way to avoid these annoying "artefacts"
+        // is to paint the border on top of the plot items.
+
+        if ( d_data->styleSheet.hasBorder &&
+            !d_data->styleSheet.borderPath.isEmpty() )
+        {
+            // We have a border with at least one rounded corner
+            hackStyledBackground = true;
+        }
+    }
+
+    if ( withBackground )
+    {
+        painter->save();
+
+        if ( testAttribute( Qt::WA_StyledBackground ) )
+        {
+            if ( hackStyledBackground )
+            {
+                // paint background without border
+
+                painter->setPen( Qt::NoPen );
+                painter->setBrush( d_data->styleSheet.background.brush ); 
+                painter->setBrushOrigin( d_data->styleSheet.background.origin );
+                painter->setClipPath( d_data->styleSheet.borderPath );
+                painter->drawRect( contentsRect() );
+            }
+            else
+            {
+                qwtDrawStyledBackground( this, painter );
+            }
+        }
+        else if ( autoFillBackground() )
+        {
+            painter->setPen( Qt::NoPen );
+            painter->setBrush( palette().brush( backgroundRole() ) );
+
+            if ( d_data->borderRadius > 0.0 && ( rect() == frameRect() ) )
+            {
+                if ( frameWidth() > 0 )
+                {
+                    painter->setClipPath( borderPath( rect() ) );
+                    painter->drawRect( rect() );
+                }
+                else
+                {
+                    painter->setRenderHint( QPainter::Antialiasing, true );
+                    painter->drawPath( borderPath( rect() ) );
+                }
+            }
+            else
+            {
+                painter->drawRect( rect() );
+            }
+        }
+
+        painter->restore();
+    }
+
+    painter->save();
+
+    if ( !d_data->styleSheet.borderPath.isEmpty() )
+    {
+        painter->setClipPath( 
+            d_data->styleSheet.borderPath, Qt::IntersectClip );
+    }
+    else
+    {
+        if ( d_data->borderRadius > 0.0 )
+            painter->setClipPath( borderPath( frameRect() ), Qt::IntersectClip );
+        else
+            painter->setClipRect( contentsRect(), Qt::IntersectClip );
+    }
+
+    plot()->drawCanvas( painter );
+
+    painter->restore();
+
+    if ( withBackground && hackStyledBackground )
+    {
+        // Now paint the border on top
+        QStyleOptionFrame opt;
+        opt.initFrom(this);
+        style()->drawPrimitive( QStyle::PE_Frame, &opt, painter, this);
+    }
+}
+
+/*!
+  Draw the border of the plot canvas
+
+  \param painter Painter
+  \sa setBorderRadius()
+*/
+void QwtPlotCanvas::drawBorder( QPainter *painter )
+{
+    if ( d_data->borderRadius > 0 )
+    {
+        if ( frameWidth() > 0 )
+        {
+            QwtPainter::drawRoundedFrame( painter, QRectF( frameRect() ), 
+                d_data->borderRadius, d_data->borderRadius,
+                palette(), frameWidth(), frameStyle() );
+        }
+    }
+    else
+    {
+#if QT_VERSION >= 0x040500
+#if QT_VERSION < 0x050000
+        QStyleOptionFrameV3 opt;
+#else
+        QStyleOptionFrame opt;
+#endif
+        opt.init(this);
+
+        int frameShape  = frameStyle() & QFrame::Shape_Mask;
+        int frameShadow = frameStyle() & QFrame::Shadow_Mask;
+
+        opt.frameShape = QFrame::Shape( int( opt.frameShape ) | frameShape );
+#if 0
+        opt.rect = frameRect();
+#endif
+
+        switch (frameShape) 
+        {
+            case QFrame::Box:
+            case QFrame::HLine:
+            case QFrame::VLine:
+            case QFrame::StyledPanel:
+            case QFrame::Panel:
+            {
+                opt.lineWidth = lineWidth();
+                opt.midLineWidth = midLineWidth();
+                break; 
+            }
+            default: 
+            {
+                opt.lineWidth = frameWidth();
+                break;
+            }
+        }
+    
+        if ( frameShadow == Sunken )
+            opt.state |= QStyle::State_Sunken;
+        else if ( frameShadow == Raised )
+            opt.state |= QStyle::State_Raised;
+
+        style()->drawControl(QStyle::CE_ShapedFrame, &opt, painter, this);
+#else
+        drawFrame( painter );
+#endif
+    }
+}
+
+/*!
+  Resize event
+  \param event Resize event
+*/
+void QwtPlotCanvas::resizeEvent( QResizeEvent *event )
+{
+    QFrame::resizeEvent( event );
+    updateStyleSheetInfo();
+}
+
+/*!
+  Draw the focus indication
+  \param painter Painter
+*/
+void QwtPlotCanvas::drawFocusIndicator( QPainter *painter )
+{
+    const int margin = 1;
+
+    QRect focusRect = contentsRect();
+    focusRect.setRect( focusRect.x() + margin, focusRect.y() + margin,
+        focusRect.width() - 2 * margin, focusRect.height() - 2 * margin );
+
+    QwtPainter::drawFocusRect( painter, this, focusRect );
+}
+
+/*!
+   Invalidate the paint cache and repaint the canvas
+   \sa invalidatePaintCache()
+*/
+void QwtPlotCanvas::replot()
+{
+    invalidateBackingStore();
+
+    if ( testPaintAttribute( QwtPlotCanvas::ImmediatePaint ) )
+        repaint( contentsRect() );
+    else
+        update( contentsRect() );
+}
+
+//! Update the cached information about the current style sheet
+void QwtPlotCanvas::updateStyleSheetInfo()
+{
+    if ( !testAttribute(Qt::WA_StyledBackground ) )
+        return;
+
+    QwtStyleSheetRecorder recorder( size() );
+    
+    QPainter painter( &recorder );
+    
+    QStyleOption opt;
+    opt.initFrom(this);
+    style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this);
+    
+    painter.end();
+
+    d_data->styleSheet.hasBorder = !recorder.border.rectList.isEmpty();
+    d_data->styleSheet.cornerRects = recorder.clipRects;
+
+    if ( recorder.background.path.isEmpty() )
+    {
+        if ( !recorder.border.rectList.isEmpty() )
+        {
+            d_data->styleSheet.borderPath = 
+                qwtCombinePathList( rect(), recorder.border.pathList );
+        }
+    }
+    else
+    {
+        d_data->styleSheet.borderPath = recorder.background.path;
+        d_data->styleSheet.background.brush = recorder.background.brush;
+        d_data->styleSheet.background.origin = recorder.background.origin;
+    }
+}
+
+/*!
+   Calculate the painter path for a styled or rounded border
+
+   When the canvas has no styled background or rounded borders
+   the painter path is empty.
+
+   \param rect Bounding rectangle of the canvas
+   \return Painter path, that can be used for clipping
+*/
+QPainterPath QwtPlotCanvas::borderPath( const QRect &rect ) const
+{
+    if ( testAttribute(Qt::WA_StyledBackground ) )
+    {
+        QwtStyleSheetRecorder recorder( rect.size() );
+
+        QPainter painter( &recorder );
+
+        QStyleOption opt;
+        opt.initFrom(this);
+        opt.rect = rect;
+        style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this);
+
+        painter.end();
+
+        if ( !recorder.background.path.isEmpty() )
+            return recorder.background.path;
+
+        if ( !recorder.border.rectList.isEmpty() )
+            return qwtCombinePathList( rect, recorder.border.pathList );
+    }
+    else if ( d_data->borderRadius > 0.0 )
+    {
+        double fw2 = frameWidth() * 0.5;
+        QRectF r = QRectF(rect).adjusted( fw2, fw2, -fw2, -fw2 );
+
+        QPainterPath path;
+        path.addRoundedRect( r, d_data->borderRadius, d_data->borderRadius );
+        return path;
+    }
+    
+    return QPainterPath();
+}
diff --git a/source/third_party/qwt/qwt_plot_curve.cpp b/source/third_party/qwt/qwt_plot_curve.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b5068ce7b1c4d7e230bd6bf3c6cce09c06940d8
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_curve.cpp
@@ -0,0 +1,1204 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_curve.h"
+#include "qwt/qwt_point_data.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_clipper.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_curve_fitter.h"
+#include "qwt/qwt_symbol.h"
+#include "qwt/qwt_point_mapper.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qalgorithms.h>
+#include <qmath.h>
+
+static void qwtUpdateLegendIconSize( QwtPlotCurve *curve )
+{
+    if ( curve->symbol() && 
+        curve->testLegendAttribute( QwtPlotCurve::LegendShowSymbol ) )
+    {
+        QSize sz = curve->symbol()->boundingRect().size();
+        sz += QSize( 2, 2 ); // margin
+
+        if ( curve->testLegendAttribute( QwtPlotCurve::LegendShowLine ) )
+        {
+            // Avoid, that the line is completely covered by the symbol
+
+            int w = qCeil( 1.5 * sz.width() );
+            if ( w % 2 )
+                w++;
+
+            sz.setWidth( qMax( 8, w ) );
+        }
+
+        curve->setLegendIconSize( sz );
+    }
+}
+
+static int qwtVerifyRange( int size, int &i1, int &i2 )
+{
+    if ( size < 1 )
+        return 0;
+
+    i1 = qBound( 0, i1, size - 1 );
+    i2 = qBound( 0, i2, size - 1 );
+
+    if ( i1 > i2 )
+        qSwap( i1, i2 );
+
+    return ( i2 - i1 + 1 );
+}
+
+class QwtPlotCurve::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtPlotCurve::Lines ),
+        baseline( 0.0 ),
+        symbol( NULL ),
+        attributes( 0 ),
+        paintAttributes( 
+            QwtPlotCurve::ClipPolygons | QwtPlotCurve::FilterPoints ),
+        legendAttributes( 0 )
+    {
+        pen = QPen( Qt::black );
+        curveFitter = new QwtSplineCurveFitter;
+    }
+
+    ~PrivateData()
+    {
+        delete symbol;
+        delete curveFitter;
+    }
+
+    QwtPlotCurve::CurveStyle style;
+    double baseline;
+
+    const QwtSymbol *symbol;
+    QwtCurveFitter *curveFitter;
+
+    QPen pen;
+    QBrush brush;
+
+    QwtPlotCurve::CurveAttributes attributes;
+    QwtPlotCurve::PaintAttributes paintAttributes;
+
+    QwtPlotCurve::LegendAttributes legendAttributes;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotCurve::QwtPlotCurve( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotCurve::QwtPlotCurve( const QString &title ):
+    QwtPlotSeriesItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotCurve::~QwtPlotCurve()
+{
+    delete d_data;
+}
+
+//! Initialize internal members
+void QwtPlotCurve::init()
+{
+    setItemAttribute( QwtPlotItem::Legend );
+    setItemAttribute( QwtPlotItem::AutoScale );
+
+    d_data = new PrivateData;
+    setData( new QwtPointSeriesData() );
+
+    setZ( 20.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotCurve
+int QwtPlotCurve::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotCurve;
+}
+
+/*!
+  Specify an attribute how to draw the curve
+
+  \param attribute Paint attribute
+  \param on On/Off
+  \sa testPaintAttribute()
+*/
+void QwtPlotCurve::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa setPaintAttribute()
+*/
+bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Specify an attribute how to draw the legend icon
+
+  \param attribute Attribute
+  \param on On/Off
+  /sa testLegendAttribute(). legendIcon()
+*/
+void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on )
+{
+    if ( on != testLegendAttribute( attribute ) )
+    {
+        if ( on )
+            d_data->legendAttributes |= attribute;
+        else
+            d_data->legendAttributes &= ~attribute;
+
+        qwtUpdateLegendIconSize( this );
+        legendChanged();
+    }
+}
+
+/*!
+  \return True, when attribute is enabled
+  \sa setLegendAttribute()
+*/
+bool QwtPlotCurve::testLegendAttribute( LegendAttribute attribute ) const
+{
+    return ( d_data->legendAttributes & attribute );
+}
+
+/*!
+  Set the curve's drawing style
+
+  \param style Curve style
+  \sa style()
+*/
+void QwtPlotCurve::setStyle( CurveStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Style of the curve
+  \sa setStyle()
+*/
+QwtPlotCurve::CurveStyle QwtPlotCurve::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  \brief Assign a symbol
+
+  The curve will take the ownership of the symbol, hence the previously
+  set symbol will be delete by setting a new one. If \p symbol is 
+  \c NULL no symbol will be drawn.
+
+  \param symbol Symbol
+  \sa symbol()
+*/
+void QwtPlotCurve::setSymbol( QwtSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        qwtUpdateLegendIconSize( this );
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Current symbol or NULL, when no symbol has been assigned
+  \sa setSymbol()
+*/
+const QwtSymbol *QwtPlotCurve::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  Build and assign a pen
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtPlotCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen
+
+  \param pen New pen
+  \sa pen(), brush()
+*/
+void QwtPlotCurve::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen used to draw the lines
+  \sa setPen(), brush()
+*/
+const QPen& QwtPlotCurve::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  \brief Assign a brush.
+
+   In case of brush.style() != QBrush::NoBrush
+   and style() != QwtPlotCurve::Sticks
+   the area between the curve and the baseline will be filled.
+
+   In case !brush.color().isValid() the area will be filled by
+   pen.color(). The fill algorithm simply connects the first and the
+   last curve point to the baseline. So the curve data has to be sorted
+   (ascending or descending).
+
+  \param brush New brush
+  \sa brush(), setBaseline(), baseline()
+*/
+void QwtPlotCurve::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used to fill the area between lines and the baseline
+  \sa setBrush(), setBaseline(), baseline()
+*/
+const QBrush& QwtPlotCurve::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  Draw an interval of the curve
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         curve will be painted to its last point.
+
+  \sa drawCurve(), drawSymbols(),
+*/
+void QwtPlotCurve::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const size_t numSamples = dataSize();
+
+    if ( !painter || numSamples <= 0 )
+        return;
+
+    if ( to < 0 )
+        to = numSamples - 1;
+
+    if ( qwtVerifyRange( numSamples, from, to ) > 0 )
+    {
+        painter->save();
+        painter->setPen( d_data->pen );
+
+        /*
+          Qt 4.0.0 is slow when drawing lines, but it's even
+          slower when the painter has a brush. So we don't
+          set the brush before we really need it.
+         */
+
+        drawCurve( painter, d_data->style, xMap, yMap, canvasRect, from, to );
+        painter->restore();
+
+        if ( d_data->symbol &&
+            ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
+        {
+            painter->save();
+            drawSymbols( painter, *d_data->symbol,
+                xMap, yMap, canvasRect, from, to );
+            painter->restore();
+        }
+    }
+}
+
+/*!
+  \brief Draw the line part (without symbols) of a curve interval.
+  \param painter Painter
+  \param style curve style, see QwtPlotCurve::CurveStyle
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+  \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks()
+*/
+void QwtPlotCurve::drawCurve( QPainter *painter, int style,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    switch ( style )
+    {
+        case Lines:
+            if ( testCurveAttribute( Fitted ) )
+            {
+                // we always need the complete
+                // curve for fitting
+                from = 0;
+                to = dataSize() - 1;
+            }
+            drawLines( painter, xMap, yMap, canvasRect, from, to );
+            break;
+        case Sticks:
+            drawSticks( painter, xMap, yMap, canvasRect, from, to );
+            break;
+        case Steps:
+            drawSteps( painter, xMap, yMap, canvasRect, from, to );
+            break;
+        case Dots:
+            drawDots( painter, xMap, yMap, canvasRect, from, to );
+            break;
+        case NoCurve:
+        default:
+            break;
+    }
+}
+
+/*!
+  \brief Draw lines
+
+  If the CurveAttribute Fitted is enabled a QwtCurveFitter tries
+  to interpolate/smooth the curve, before it is painted.
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+
+  \sa setCurveAttribute(), setCurveFitter(), draw(),
+      drawLines(), drawDots(), drawSteps(), drawSticks()
+*/
+void QwtPlotCurve::drawLines( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( from > to )
+        return;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+    const bool doFit = ( d_data->attributes & Fitted ) && d_data->curveFitter;
+    const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
+            && ( d_data->brush.color().alpha() > 0 );
+
+    QRectF clipRect;
+    if ( d_data->paintAttributes & ClipPolygons )
+    {
+        qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
+        clipRect = canvasRect.adjusted(-pw, -pw, pw, pw);
+    }
+
+    bool doIntegers = false;
+
+#if QT_VERSION < 0x040800
+
+    // For Qt <= 4.7 the raster paint engine is significantly faster
+    // for rendering QPolygon than for QPolygonF. So let's
+    // see if we can use it.
+
+    if ( painter->paintEngine()->type() == QPaintEngine::Raster )
+    {
+        // In case of filling or fitting performance doesn't count
+        // because both operations are much more expensive
+        // then drawing the polyline itself
+
+        if ( !doFit && !doFill )
+            doIntegers = true; 
+    }
+#endif
+
+    const bool noDuplicates = d_data->paintAttributes & FilterPoints;
+
+    QwtPointMapper mapper;
+    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
+    mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates );
+    mapper.setBoundingRect( canvasRect );
+
+    if ( doIntegers )
+    {
+        QPolygon polyline = mapper.toPolygon( 
+            xMap, yMap, data(), from, to );
+
+        if ( d_data->paintAttributes & ClipPolygons )
+        {
+            polyline = QwtClipper::clipPolygon( 
+                clipRect.toAlignedRect(), polyline, false );
+        }
+
+        QwtPainter::drawPolyline( painter, polyline );
+    }
+    else
+    {
+        QPolygonF polyline = mapper.toPolygonF( xMap, yMap, data(), from, to );
+
+        if ( doFit )
+            polyline = d_data->curveFitter->fitCurve( polyline );
+
+        if ( doFill )
+        {
+            if ( painter->pen().style() != Qt::NoPen )
+            {
+                // here we are wasting memory for the filled copy,
+                // do polygon clipping twice etc .. TODO
+
+                QPolygonF filled = polyline;
+                fillCurve( painter, xMap, yMap, canvasRect, filled );
+                filled.clear();
+
+                if ( d_data->paintAttributes & ClipPolygons )
+                {
+                    polyline = QwtClipper::clipPolygonF( 
+                        clipRect, polyline, false );
+                }
+
+                QwtPainter::drawPolyline( painter, polyline );
+            }
+            else
+            {
+                fillCurve( painter, xMap, yMap, canvasRect, polyline );
+            }
+        }
+        else
+        {
+            if ( d_data->paintAttributes & ClipPolygons )
+            {
+                polyline = QwtClipper::clipPolygonF(
+                    clipRect, polyline, false );
+            }
+
+            QwtPainter::drawPolyline( painter, polyline );
+        }
+    }
+}
+
+/*!
+  Draw sticks
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+
+  \sa draw(), drawCurve(), drawDots(), drawLines(), drawSteps()
+*/
+void QwtPlotCurve::drawSticks( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &, int from, int to ) const
+{
+    painter->save();
+    painter->setRenderHint( QPainter::Antialiasing, false );
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double x0 = xMap.transform( d_data->baseline );
+    double y0 = yMap.transform( d_data->baseline );
+    if ( doAlign )
+    {
+        x0 = qRound( x0 );
+        y0 = qRound( y0 );
+    }
+
+    const Qt::Orientation o = orientation();
+
+    const QwtSeriesData<QPointF> *series = data();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QPointF sample = series->sample( i );
+        double xi = xMap.transform( sample.x() );
+        double yi = yMap.transform( sample.y() );
+        if ( doAlign )
+        {
+            xi = qRound( xi );
+            yi = qRound( yi );
+        }
+
+        if ( o == Qt::Horizontal )
+            QwtPainter::drawLine( painter, x0, yi, xi, yi );
+        else
+            QwtPainter::drawLine( painter, xi, y0, xi, yi );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw dots
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+
+  \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps()
+*/
+void QwtPlotCurve::drawDots( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const QColor color = painter->pen().color();
+
+    if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 )
+    {
+        return;
+    }
+
+    const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
+            && ( d_data->brush.color().alpha() > 0 );
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    QwtPointMapper mapper;
+    mapper.setBoundingRect( canvasRect );
+    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
+
+    if ( d_data->paintAttributes & FilterPoints )
+    {
+        if ( ( color.alpha() == 255 )
+            && !( painter->renderHints() & QPainter::Antialiasing ) )
+        {
+            mapper.setFlag( QwtPointMapper::WeedOutPoints, true );
+        }
+    }
+
+    if ( doFill )
+    {
+        mapper.setFlag( QwtPointMapper::WeedOutPoints, false );
+
+        QPolygonF points = mapper.toPointsF( 
+            xMap, yMap, data(), from, to );
+
+        QwtPainter::drawPoints( painter, points );
+        fillCurve( painter, xMap, yMap, canvasRect, points );
+    }
+    else if ( d_data->paintAttributes & ImageBuffer )
+    {
+        const QImage image = mapper.toImage( xMap, yMap,
+            data(), from, to, d_data->pen, 
+            painter->testRenderHint( QPainter::Antialiasing ),
+            renderThreadCount() );
+
+        painter->drawImage( canvasRect.toAlignedRect(), image );
+    }
+    else if ( d_data->paintAttributes & MinimizeMemory )
+    {
+        const QwtSeriesData<QPointF> *series = data();
+
+        for ( int i = from; i <= to; i++ )
+        {
+            const QPointF sample = series->sample( i );
+
+            double xi = xMap.transform( sample.x() );
+            double yi = yMap.transform( sample.y() );
+
+            if ( doAlign )
+            {
+                xi = qRound( xi );
+                yi = qRound( yi );
+            }
+
+            QwtPainter::drawPoint( painter, QPointF( xi, yi ) );
+        }
+    }
+    else
+    {
+        if ( doAlign )
+        {
+            const QPolygon points = mapper.toPoints(
+                xMap, yMap, data(), from, to ); 
+
+            QwtPainter::drawPoints( painter, points );
+        }
+        else
+        {
+            const QPolygonF points = mapper.toPointsF( 
+                xMap, yMap, data(), from, to );
+
+            QwtPainter::drawPoints( painter, points );
+        }
+    }
+}
+
+/*!
+  Draw step function
+
+  The direction of the steps depends on Inverted attribute.
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from index of the first point to be painted
+  \param to index of the last point to be painted
+
+  \sa CurveAttribute, setCurveAttribute(),
+      draw(), drawCurve(), drawDots(), drawLines(), drawSticks()
+*/
+void QwtPlotCurve::drawSteps( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    QPolygonF polygon( 2 * ( to - from ) + 1 );
+    QPointF *points = polygon.data();
+
+    bool inverted = orientation() == Qt::Vertical;
+    if ( d_data->attributes & Inverted )
+        inverted = !inverted;
+
+    const QwtSeriesData<QPointF> *series = data();
+
+    int i, ip;
+    for ( i = from, ip = 0; i <= to; i++, ip += 2 )
+    {
+        const QPointF sample = series->sample( i );
+        double xi = xMap.transform( sample.x() );
+        double yi = yMap.transform( sample.y() );
+        if ( doAlign )
+        {
+            xi = qRound( xi );
+            yi = qRound( yi );
+        }
+
+        if ( ip > 0 )
+        {
+            const QPointF &p0 = points[ip - 2];
+            QPointF &p = points[ip - 1];
+
+            if ( inverted )
+            {
+                p.rx() = p0.x();
+                p.ry() = yi;
+            }
+            else
+            {
+                p.rx() = xi;
+                p.ry() = p0.y();
+            }
+        }
+
+        points[ip].rx() = xi;
+        points[ip].ry() = yi;
+    }
+
+    if ( d_data->paintAttributes & ClipPolygons )
+    {
+        const QPolygonF clipped = QwtClipper::clipPolygonF( 
+            canvasRect, polygon, false );
+
+        QwtPainter::drawPolyline( painter, clipped );
+    }
+    else
+    {
+        QwtPainter::drawPolyline( painter, polygon );
+    }
+
+    if ( d_data->brush.style() != Qt::NoBrush )
+        fillCurve( painter, xMap, yMap, canvasRect, polygon );
+}
+
+
+/*!
+  Specify an attribute for drawing the curve
+
+  \param attribute Curve attribute
+  \param on On/Off
+
+  /sa testCurveAttribute(), setCurveFitter()
+*/
+void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on )
+{
+    if ( bool( d_data->attributes & attribute ) == on )
+        return;
+
+    if ( on )
+        d_data->attributes |= attribute;
+    else
+        d_data->attributes &= ~attribute;
+
+    itemChanged();
+}
+
+/*!
+    \return true, if attribute is enabled
+    \sa setCurveAttribute()
+*/
+bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const
+{
+    return d_data->attributes & attribute;
+}
+
+/*!
+  Assign a curve fitter
+
+  The curve fitter "smooths" the curve points, when the Fitted
+  CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting.
+
+  The curve fitter operates on the translated points ( = widget coordinates)
+  to be functional for logarithmic scales. Obviously this is less performant
+  for fitting algorithms, that reduce the number of points.
+
+  For situations, where curve fitting is used to improve the performance
+  of painting huge series of points it might be better to execute the fitter
+  on the curve points once and to cache the result in the QwtSeriesData object.
+
+  \param curveFitter() Curve fitter
+  \sa Fitted
+*/
+void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter )
+{
+    delete d_data->curveFitter;
+    d_data->curveFitter = curveFitter;
+
+    itemChanged();
+}
+
+/*!
+  Get the curve fitter. If curve fitting is disabled NULL is returned.
+
+  \return Curve fitter
+  \sa setCurveFitter(), Fitted
+*/
+QwtCurveFitter *QwtPlotCurve::curveFitter() const
+{
+    return d_data->curveFitter;
+}
+
+/*!
+  Fill the area between the curve and the baseline with
+  the curve brush
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param polygon Polygon - will be modified !
+
+  \sa setBrush(), setBaseline(), setStyle()
+*/
+void QwtPlotCurve::fillCurve( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, QPolygonF &polygon ) const
+{
+    if ( d_data->brush.style() == Qt::NoBrush )
+        return;
+
+    closePolyline( painter, xMap, yMap, polygon );
+    if ( polygon.count() <= 2 ) // a line can't be filled
+        return;
+
+    QBrush brush = d_data->brush;
+    if ( !brush.color().isValid() )
+        brush.setColor( d_data->pen.color() );
+
+    if ( d_data->paintAttributes & ClipPolygons )
+        polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true );
+
+    painter->save();
+
+    painter->setPen( Qt::NoPen );
+    painter->setBrush( brush );
+
+    QwtPainter::drawPolygon( painter, polygon );
+
+    painter->restore();
+}
+
+/*!
+  \brief Complete a polygon to be a closed polygon including the 
+         area between the original polygon and the baseline.
+
+  \param painter Painter
+  \param xMap X map
+  \param yMap Y map
+  \param polygon Polygon to be completed
+*/
+void QwtPlotCurve::closePolyline( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    QPolygonF &polygon ) const
+{
+    if ( polygon.size() < 2 )
+        return;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double baseline = d_data->baseline;
+    
+    if ( orientation() == Qt::Vertical )
+    {
+        if ( yMap.transformation() )
+            baseline = yMap.transformation()->bounded( baseline );
+
+        double refY = yMap.transform( baseline );
+        if ( doAlign )
+            refY = qRound( refY );
+
+        polygon += QPointF( polygon.last().x(), refY );
+        polygon += QPointF( polygon.first().x(), refY );
+    }
+    else
+    {
+        if ( xMap.transformation() )
+            baseline = xMap.transformation()->bounded( baseline );
+
+        double refX = xMap.transform( baseline );
+        if ( doAlign )
+            refX = qRound( refX );
+
+        polygon += QPointF( refX, polygon.last().y() );
+        polygon += QPointF( refX, polygon.first().y() );
+    }
+}
+
+/*!
+  Draw symbols
+
+  \param painter Painter
+  \param symbol Curve symbol
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \sa setSymbol(), drawSeries(), drawCurve()
+*/
+void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    QwtPointMapper mapper;
+    mapper.setFlag( QwtPointMapper::RoundPoints, 
+        QwtPainter::roundingAlignment( painter ) );
+    mapper.setFlag( QwtPointMapper::WeedOutPoints, 
+        testPaintAttribute( QwtPlotCurve::FilterPoints ) );
+    mapper.setBoundingRect( canvasRect );
+
+    const int chunkSize = 500;
+
+    for ( int i = from; i <= to; i += chunkSize )
+    {
+        const int n = qMin( chunkSize, to - i + 1 );
+
+        const QPolygonF points = mapper.toPointsF( xMap, yMap,
+            data(), i, i + n - 1 );
+
+        if ( points.size() > 0 )
+            symbol.drawSymbols( painter, points );
+    }
+}
+
+/*!
+  \brief Set the value of the baseline
+
+  The baseline is needed for filling the curve with a brush or
+  the Sticks drawing style.
+
+  The interpretation of the baseline depends on the orientation().
+  With Qt::Horizontal, the baseline is interpreted as a horizontal line
+  at y = baseline(), with Qt::Vertical, it is interpreted as a vertical
+  line at x = baseline().
+
+  The default value is 0.0.
+
+  \param value Value of the baseline
+  \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation()
+*/
+void QwtPlotCurve::setBaseline( double value )
+{
+    if ( d_data->baseline != value )
+    {
+        d_data->baseline = value;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Value of the baseline
+  \sa setBaseline()
+*/
+double QwtPlotCurve::baseline() const
+{
+    return d_data->baseline;
+}
+
+/*!
+  Find the closest curve point for a specific position
+
+  \param pos Position, where to look for the closest curve point
+  \param dist If dist != NULL, closestPoint() returns the distance between
+              the position and the closest curve point
+  \return Index of the closest curve point, or -1 if none can be found
+          ( f.e when the curve has no points )
+  \note closestPoint() implements a dumb algorithm, that iterates
+        over all points
+*/
+int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const
+{
+    const size_t numSamples = dataSize();
+
+    if ( plot() == NULL || numSamples <= 0 )
+        return -1;
+
+    const QwtSeriesData<QPointF> *series = data();
+
+    const QwtScaleMap xMap = plot()->canvasMap( xAxis() );
+    const QwtScaleMap yMap = plot()->canvasMap( yAxis() );
+
+    int index = -1;
+    double dmin = 1.0e10;
+
+    for ( uint i = 0; i < numSamples; i++ )
+    {
+        const QPointF sample = series->sample( i );
+
+        const double cx = xMap.transform( sample.x() ) - pos.x();
+        const double cy = yMap.transform( sample.y() ) - pos.y();
+
+        const double f = qwtSqr( cx ) + qwtSqr( cy );
+        if ( f < dmin )
+        {
+            index = i;
+            dmin = f;
+        }
+    }
+    if ( dist )
+        *dist = qSqrt( dmin );
+
+    return index;
+}
+
+/*!
+   \return Icon representing the curve on the legend
+
+   \param index Index of the legend entry 
+                ( ignored as there is only one )
+   \param size Icon size
+
+   \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
+ */
+QwtGraphic QwtPlotCurve::legendIcon( int index, 
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+
+    if ( size.isEmpty() )
+        return QwtGraphic();
+
+    QwtGraphic graphic;
+    graphic.setDefaultSize( size );
+    graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &graphic );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    if ( d_data->legendAttributes == 0 ||
+        d_data->legendAttributes & QwtPlotCurve::LegendShowBrush )
+    {
+        QBrush brush = d_data->brush;
+
+        if ( brush.style() == Qt::NoBrush &&
+            d_data->legendAttributes == 0 )
+        {
+            if ( style() != QwtPlotCurve::NoCurve )
+            {
+                brush = QBrush( pen().color() );
+            }
+            else if ( d_data->symbol &&
+                ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
+            {
+                brush = QBrush( d_data->symbol->pen().color() );
+            }
+        }
+
+        if ( brush.style() != Qt::NoBrush )
+        {
+            QRectF r( 0, 0, size.width(), size.height() );
+            painter.fillRect( r, brush );
+        }
+    }
+
+    if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine )
+    {
+        if ( pen() != Qt::NoPen )
+        {
+            QPen pn = pen();
+            pn.setCapStyle( Qt::FlatCap );
+
+            painter.setPen( pn );
+
+            const double y = 0.5 * size.height();
+            QwtPainter::drawLine( &painter, 0.0, y, size.width(), y );
+        }
+    }
+
+    if ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol )
+    {
+        if ( d_data->symbol )
+        {
+            QRectF r( 0, 0, size.width(), size.height() );
+            d_data->symbol->drawSymbol( &painter, r );
+        }
+    }
+
+    return graphic;
+}
+
+/*!
+  Initialize data with an array of points.
+
+  \param samples Vector of points
+  \note QVector is implicitly shared
+  \note QPolygonF is derived from QVector<QPointF>
+*/
+void QwtPlotCurve::setSamples( const QVector<QPointF> &samples )
+{
+    setData( new QwtPointSeriesData( samples ) );
+}
+
+/*!
+  Assign a series of points
+
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/
+void QwtPlotCurve::setSamples( QwtSeriesData<QPointF> *data )
+{
+    setData( data );
+}
+
+#ifndef QWT_NO_COMPAT
+
+/*!
+  \brief Initialize the data by pointing to memory blocks which 
+         are not managed by QwtPlotCurve.
+
+  setRawSamples is provided for efficiency. 
+  It is important to keep the pointers
+  during the lifetime of the underlying QwtCPointerData class.
+
+  \param xData pointer to x data
+  \param yData pointer to y data
+  \param size size of x and y
+
+  \sa QwtCPointerData
+*/
+void QwtPlotCurve::setRawSamples( 
+    const double *xData, const double *yData, int size )
+{
+    setData( new QwtCPointerData( xData, yData, size ) );
+}
+
+/*!
+  Set data by copying x- and y-values from specified memory blocks.
+  Contrary to setRawSamples(), this function makes a 'deep copy' of
+  the data.
+
+  \param xData pointer to x values
+  \param yData pointer to y values
+  \param size size of xData and yData
+
+  \sa QwtPointArrayData
+*/
+void QwtPlotCurve::setSamples( 
+    const double *xData, const double *yData, int size )
+{
+    setData( new QwtPointArrayData( xData, yData, size ) );
+}
+
+/*!
+  \brief Initialize data with x- and y-arrays (explicitly shared)
+
+  \param xData x data
+  \param yData y data
+
+  \sa QwtPointArrayData
+*/
+void QwtPlotCurve::setSamples( const QVector<double> &xData,
+    const QVector<double> &yData )
+{
+    setData( new QwtPointArrayData( xData, yData ) );
+}
+
+#endif // !QWT_NO_COMPAT
+
diff --git a/source/third_party/qwt/qwt_plot_dict.cpp b/source/third_party/qwt/qwt_plot_dict.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1a2f5474aaf777226b7b5a53a1652db0512da6af
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_dict.cpp
@@ -0,0 +1,191 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_dict.h"
+
+class QwtPlotDict::PrivateData
+{
+public:
+
+    class ItemList: public QList<QwtPlotItem *>
+    {
+    public:
+        void insertItem( QwtPlotItem *item )
+        {
+            if ( item == NULL )
+                return;
+
+            QList<QwtPlotItem *>::iterator it =
+                qUpperBound( begin(), end(), item, LessZThan() );
+            insert( it, item );
+        }
+
+        void removeItem( QwtPlotItem *item )
+        {
+            if ( item == NULL )
+                return;
+
+            QList<QwtPlotItem *>::iterator it =
+                qLowerBound( begin(), end(), item, LessZThan() );
+
+            for ( ; it != end(); ++it )
+            {
+                if ( item == *it )
+                {
+                    erase( it );
+                    break;
+                }
+            }
+        }
+    private:
+        class LessZThan
+        {
+        public:
+            inline bool operator()( const QwtPlotItem *item1,
+                const QwtPlotItem *item2 ) const
+            {
+                return item1->z() < item2->z();
+            }
+        };
+    };
+
+    ItemList itemList;
+    bool autoDelete;
+};
+
+/*!
+   Constructor
+
+   Auto deletion is enabled.
+   \sa setAutoDelete(), QwtPlotItem::attach()
+*/
+QwtPlotDict::QwtPlotDict()
+{
+    d_data = new QwtPlotDict::PrivateData;
+    d_data->autoDelete = true;
+}
+
+/*!
+   Destructor
+
+   If autoDelete() is on, all attached items will be deleted
+   \sa setAutoDelete(), autoDelete(), QwtPlotItem::attach()
+*/
+QwtPlotDict::~QwtPlotDict()
+{
+    detachItems( QwtPlotItem::Rtti_PlotItem, d_data->autoDelete );
+    delete d_data;
+}
+
+/*!
+   En/Disable Auto deletion
+
+   If Auto deletion is on all attached plot items will be deleted
+   in the destructor of QwtPlotDict. The default value is on.
+
+   \sa autoDelete(), insertItem()
+*/
+void QwtPlotDict::setAutoDelete( bool autoDelete )
+{
+    d_data->autoDelete = autoDelete;
+}
+
+/*!
+   \return true if auto deletion is enabled
+   \sa setAutoDelete(), insertItem()
+*/
+bool QwtPlotDict::autoDelete() const
+{
+    return d_data->autoDelete;
+}
+
+/*!
+  Insert a plot item
+
+  \param item PlotItem
+  \sa removeItem()
+ */
+void QwtPlotDict::insertItem( QwtPlotItem *item )
+{
+    d_data->itemList.insertItem( item );
+}
+
+/*!
+  Remove a plot item
+
+  \param item PlotItem
+  \sa insertItem()
+ */
+void QwtPlotDict::removeItem( QwtPlotItem *item )
+{
+    d_data->itemList.removeItem( item );
+}
+
+/*!
+   Detach items from the dictionary
+
+   \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items
+               otherwise only those items of the type rtti.
+   \param autoDelete If true, delete all detached items
+*/
+void QwtPlotDict::detachItems( int rtti, bool autoDelete )
+{
+    PrivateData::ItemList list = d_data->itemList;
+    QwtPlotItemIterator it = list.begin();
+    while ( it != list.end() )
+    {
+        QwtPlotItem *item = *it;
+
+        ++it; // increment before removing item from the list
+
+        if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti )
+        {
+            item->attach( NULL );
+            if ( autoDelete )
+                delete item;
+        }
+    }
+}
+
+/*!
+  \brief A QwtPlotItemList of all attached plot items.
+
+  Use caution when iterating these lists, as removing/detaching an item will
+  invalidate the iterator. Instead you can place pointers to objects to be
+  removed in a removal list, and traverse that list later.
+
+  \return List of all attached plot items.
+*/
+const QwtPlotItemList &QwtPlotDict::itemList() const
+{
+    return d_data->itemList;
+}
+
+/*!
+  \return List of all attached plot items of a specific type.
+  \param rtti See QwtPlotItem::RttiValues
+  \sa QwtPlotItem::rtti()
+*/
+QwtPlotItemList QwtPlotDict::itemList( int rtti ) const
+{
+    if ( rtti == QwtPlotItem::Rtti_PlotItem )
+        return d_data->itemList;
+
+    QwtPlotItemList items;
+
+    PrivateData::ItemList list = d_data->itemList;
+    for ( QwtPlotItemIterator it = list.begin(); it != list.end(); ++it )
+    {
+        QwtPlotItem *item = *it;
+        if ( item->rtti() == rtti )
+            items += item;
+    }
+
+    return items;
+}
diff --git a/source/third_party/qwt/qwt_plot_directpainter.cpp b/source/third_party/qwt/qwt_plot_directpainter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..71c2d7057eaa4e379dda5886da3fe857256c40a2
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_directpainter.cpp
@@ -0,0 +1,321 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_directpainter.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_plot_canvas.h"
+#include "qwt/qwt_plot_seriesitem.h"
+#include <qpainter.h>
+#include <qevent.h>
+#include <qapplication.h>
+#include <qpixmap.h>
+
+static inline void qwtRenderItem( 
+    QPainter *painter, const QRect &canvasRect,
+    QwtPlotSeriesItem *seriesItem, int from, int to )
+{
+    // A minor performance improvement is possible
+    // with caching the maps. TODO ...
+
+    QwtPlot *plot = seriesItem->plot();
+    const QwtScaleMap xMap = plot->canvasMap( seriesItem->xAxis() );
+    const QwtScaleMap yMap = plot->canvasMap( seriesItem->yAxis() );
+
+    painter->setRenderHint( QPainter::Antialiasing,
+        seriesItem->testRenderHint( QwtPlotItem::RenderAntialiased ) );
+    seriesItem->drawSeries( painter, xMap, yMap, canvasRect, from, to );
+}
+
+static inline bool qwtHasBackingStore( const QwtPlotCanvas *canvas )
+{
+    return canvas->testPaintAttribute( QwtPlotCanvas::BackingStore )
+        && canvas->backingStore() && !canvas->backingStore()->isNull();
+}
+
+class QwtPlotDirectPainter::PrivateData
+{
+public:
+    PrivateData():
+        attributes( 0 ),
+        hasClipping(false),
+        seriesItem( NULL ),
+        from( 0 ),
+        to( 0 )
+    {
+    }
+
+    QwtPlotDirectPainter::Attributes attributes;
+
+    bool hasClipping;
+    QRegion clipRegion;
+
+    QPainter painter;
+
+    QwtPlotSeriesItem *seriesItem;
+    int from;
+    int to;
+};
+
+//! Constructor
+QwtPlotDirectPainter::QwtPlotDirectPainter( QObject *parent ):
+    QObject( parent )
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtPlotDirectPainter::~QwtPlotDirectPainter()
+{
+    delete d_data;
+}
+
+/*!
+  Change an attribute
+
+  \param attribute Attribute to change
+  \param on On/Off
+
+  \sa Attribute, testAttribute()
+*/
+void QwtPlotDirectPainter::setAttribute( Attribute attribute, bool on )
+{
+    if ( bool( d_data->attributes & attribute ) != on )
+    {
+        if ( on )
+            d_data->attributes |= attribute;
+        else
+            d_data->attributes &= ~attribute;
+
+        if ( ( attribute == AtomicPainter ) && on )
+            reset();
+    }
+}
+
+/*!
+  \return True, when attribute is enabled
+  \param attribute Attribute to be tested
+  \sa Attribute, setAttribute()
+*/
+bool QwtPlotDirectPainter::testAttribute( Attribute attribute ) const
+{
+    return d_data->attributes & attribute;
+}
+
+/*!
+  En/Disables clipping 
+
+  \param enable Enables clipping is true, disable it otherwise
+  \sa hasClipping(), clipRegion(), setClipRegion()
+*/
+void QwtPlotDirectPainter::setClipping( bool enable )
+{
+    d_data->hasClipping = enable;
+}
+
+/*!
+  \return true, when clipping is enabled
+  \sa setClipping(), clipRegion(), setClipRegion()
+*/
+bool QwtPlotDirectPainter::hasClipping() const
+{
+    return d_data->hasClipping;
+}
+
+/*!
+   \brief Assign a clip region and enable clipping
+
+   Depending on the environment setting a proper clip region might improve 
+   the performance heavily. F.e. on Qt embedded only the clipped part of
+   the backing store will be copied to a ( maybe unaccelerated ) frame buffer
+   device.
+   
+   \param region Clip region
+   \sa clipRegion(), hasClipping(), setClipping()
+*/
+void QwtPlotDirectPainter::setClipRegion( const QRegion &region )
+{
+    d_data->clipRegion = region;
+    d_data->hasClipping = true;
+}
+
+/*!
+   \return Currently set clip region.
+   \sa setClipRegion(), setClipping(), hasClipping()
+*/
+QRegion QwtPlotDirectPainter::clipRegion() const
+{
+    return d_data->clipRegion;
+}
+
+/*!
+  \brief Draw a set of points of a seriesItem.
+
+  When observing an measurement while it is running, new points have to be
+  added to an existing seriesItem. drawSeries() can be used to display them avoiding
+  a complete redraw of the canvas.
+
+  Setting plot()->canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true);
+  will result in faster painting, if the paint engine of the canvas widget
+  supports this feature.
+
+  \param seriesItem Item to be painted
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         series will be painted to its last point.
+*/
+void QwtPlotDirectPainter::drawSeries(
+    QwtPlotSeriesItem *seriesItem, int from, int to )
+{
+    if ( seriesItem == NULL || seriesItem->plot() == NULL )
+        return;
+
+    QWidget *canvas = seriesItem->plot()->canvas();
+    const QRect canvasRect = canvas->contentsRect();
+
+    QwtPlotCanvas *plotCanvas = qobject_cast<QwtPlotCanvas *>( canvas );
+
+    if ( plotCanvas && qwtHasBackingStore( plotCanvas ) )
+    {
+        QPainter painter( const_cast<QPixmap *>( plotCanvas->backingStore() ) );
+
+        if ( d_data->hasClipping )
+            painter.setClipRegion( d_data->clipRegion );
+
+        qwtRenderItem( &painter, canvasRect, seriesItem, from, to );
+
+        painter.end();
+
+        if ( testAttribute( QwtPlotDirectPainter::FullRepaint ) )
+        {
+            plotCanvas->repaint();
+            return;
+        }
+    }
+
+    bool immediatePaint = true;
+    if ( !canvas->testAttribute( Qt::WA_WState_InPaintEvent ) ) 
+    {
+#if QT_VERSION < 0x050000
+        if ( !canvas->testAttribute( Qt::WA_PaintOutsidePaintEvent ) )
+#endif
+            immediatePaint = false;
+    }
+
+    if ( immediatePaint )
+    {
+        if ( !d_data->painter.isActive() )
+        {
+            reset();
+
+            d_data->painter.begin( canvas );
+            canvas->installEventFilter( this );
+        }
+
+        if ( d_data->hasClipping )
+        {
+            d_data->painter.setClipRegion( 
+                QRegion( canvasRect ) & d_data->clipRegion );
+        }
+        else
+        {
+            if ( !d_data->painter.hasClipping() )
+                d_data->painter.setClipRect( canvasRect );
+        }
+
+        qwtRenderItem( &d_data->painter, canvasRect, seriesItem, from, to );
+
+        if ( d_data->attributes & QwtPlotDirectPainter::AtomicPainter )
+        {
+            reset();
+        }
+        else
+        {
+            if ( d_data->hasClipping )
+                d_data->painter.setClipping( false );
+        }
+    }
+    else
+    {
+        reset();
+
+        d_data->seriesItem = seriesItem;
+        d_data->from = from;
+        d_data->to = to;
+
+        QRegion clipRegion = canvasRect;
+        if ( d_data->hasClipping )
+            clipRegion &= d_data->clipRegion;
+
+        canvas->installEventFilter( this );
+        canvas->repaint(clipRegion);
+        canvas->removeEventFilter( this );
+
+        d_data->seriesItem = NULL;
+    }
+}
+
+//! Close the internal QPainter
+void QwtPlotDirectPainter::reset()
+{
+    if ( d_data->painter.isActive() )
+    {
+        QWidget *w = static_cast<QWidget *>( d_data->painter.device() );
+        if ( w )
+            w->removeEventFilter( this );
+
+        d_data->painter.end();
+    }
+}
+
+//! Event filter
+bool QwtPlotDirectPainter::eventFilter( QObject *, QEvent *event )
+{
+    if ( event->type() == QEvent::Paint )
+    {
+        reset();
+
+        if ( d_data->seriesItem )
+        {
+            const QPaintEvent *pe = static_cast< QPaintEvent *>( event );
+
+            QWidget *canvas = d_data->seriesItem->plot()->canvas();
+
+            QPainter painter( canvas );
+            painter.setClipRegion( pe->region() );
+
+            bool doCopyCache = testAttribute( CopyBackingStore );
+
+            if ( doCopyCache )
+            {
+                QwtPlotCanvas *plotCanvas = 
+                    qobject_cast<QwtPlotCanvas *>( canvas );
+                if ( plotCanvas )
+                {
+                    doCopyCache = qwtHasBackingStore( plotCanvas );
+                    if ( doCopyCache )
+                    {
+                        painter.drawPixmap( plotCanvas->contentsRect().topLeft(), 
+                            *plotCanvas->backingStore() );
+                    }
+                }
+            }
+
+            if ( !doCopyCache )
+            {
+                qwtRenderItem( &painter, canvas->contentsRect(),
+                    d_data->seriesItem, d_data->from, d_data->to );
+            }
+
+            return true; // don't call QwtPlotCanvas::paintEvent()
+        }
+    }
+
+    return false;
+}
diff --git a/source/third_party/qwt/qwt_plot_glcanvas.cpp b/source/third_party/qwt/qwt_plot_glcanvas.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8e5d43d66a5ae060f8c2ec02e5fa8242bdd9d0a2
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_glcanvas.cpp
@@ -0,0 +1,378 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_glcanvas.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_painter.h"
+#include <qevent.h>
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <QtOpenGL/QGLFormat>
+
+#define FIX_GL_TRANSLATION 0
+
+static QWidget *qwtBGWidget( QWidget *widget )
+{
+    QWidget *w = widget;
+
+    for ( ; w->parentWidget() != NULL; w = w->parentWidget() )
+    {
+        if ( w->autoFillBackground() || 
+            w->testAttribute( Qt::WA_StyledBackground ) )
+        {
+            return w;
+        }
+    }
+
+    return w;
+}
+
+static void qwtUpdateContentsRect( QwtPlotGLCanvas *canvas )
+{
+    const int fw = canvas->frameWidth();
+    canvas->setContentsMargins( fw, fw, fw, fw );
+}
+
+class QwtPlotGLCanvas::PrivateData
+{
+public:
+    PrivateData():
+        frameStyle( QFrame::Panel | QFrame::Sunken),
+        lineWidth( 2 ),
+        midLineWidth( 0 )
+    {
+    }
+
+    int frameStyle;
+    int lineWidth;
+    int midLineWidth;
+};
+
+class QwtPlotGLCanvasFormat: public QGLFormat
+{
+public:
+    QwtPlotGLCanvasFormat():
+        QGLFormat( QGLFormat::defaultFormat() )
+    {
+        setSampleBuffers( true );
+    }
+};
+
+/*! 
+  \brief Constructor
+
+  \param plot Parent plot widget
+  \sa QwtPlot::setCanvas()
+*/
+QwtPlotGLCanvas::QwtPlotGLCanvas( QwtPlot *plot ):
+    QOpenGLWidget( plot )
+{
+    d_data = new PrivateData;
+
+#ifndef QT_NO_CURSOR
+    setCursor( Qt::CrossCursor );
+#endif
+
+    setAutoFillBackground( true );
+    qwtUpdateContentsRect( this );
+}
+
+//! Destructor
+QwtPlotGLCanvas::~QwtPlotGLCanvas()
+{
+    delete d_data;
+}
+
+/*!
+  Set the frame style
+
+  \param style The bitwise OR between a shape and a shadow. 
+  
+  \sa frameStyle(), QFrame::setFrameStyle(), 
+      setFrameShadow(), setFrameShape()
+ */
+void QwtPlotGLCanvas::setFrameStyle( int style )
+{
+    if ( style != d_data->frameStyle )
+    {
+        d_data->frameStyle = style;
+        qwtUpdateContentsRect( this );
+
+        update();
+    }
+}
+
+/*!
+  \return The bitwise OR between a frameShape() and a frameShadow()
+  \sa setFrameStyle(), QFrame::frameStyle()
+ */
+int QwtPlotGLCanvas::frameStyle() const
+{
+    return d_data->frameStyle;
+}
+
+/*!
+  Set the frame shadow
+
+  \param shadow Frame shadow
+  \sa frameShadow(), setFrameShape(), QFrame::setFrameShadow()
+ */
+void QwtPlotGLCanvas::setFrameShadow( Shadow shadow )
+{
+    setFrameStyle(( d_data->frameStyle & QFrame::Shape_Mask ) | shadow );
+}
+
+/*!
+  \return Frame shadow
+  \sa setFrameShadow(), QFrame::setFrameShadow()
+ */
+QwtPlotGLCanvas::Shadow QwtPlotGLCanvas::frameShadow() const
+{
+    return (Shadow) ( d_data->frameStyle & QFrame::Shadow_Mask );
+}
+
+/*!
+  Set the frame shape
+
+  \param shape Frame shape
+  \sa frameShape(), setFrameShadow(), QFrame::frameShape()
+ */
+void QwtPlotGLCanvas::setFrameShape( Shape shape )
+{
+    setFrameStyle( ( d_data->frameStyle & QFrame::Shadow_Mask ) | shape );
+}
+
+/*!
+  \return Frame shape
+  \sa setFrameShape(), QFrame::frameShape()
+ */
+QwtPlotGLCanvas::Shape QwtPlotGLCanvas::frameShape() const
+{
+    return (Shape) ( d_data->frameStyle & QFrame::Shape_Mask );
+}
+
+/*!
+   Set the frame line width
+
+   The default line width is 2 pixels.
+
+   \param width Line width of the frame
+   \sa lineWidth(), setMidLineWidth()
+*/
+void QwtPlotGLCanvas::setLineWidth( int width )
+{
+    width = qMax( width, 0 );
+    if ( width != d_data->lineWidth )
+    {
+        d_data->lineWidth = qMax( width, 0 );
+        qwtUpdateContentsRect( this );
+        update();
+    }
+}
+
+/*!
+  \return Line width of the frame
+  \sa setLineWidth(), midLineWidth()
+ */
+int QwtPlotGLCanvas::lineWidth() const
+{
+    return d_data->lineWidth;
+}
+
+/*!
+   Set the frame mid line width
+
+   The default midline width is 0 pixels.
+
+   \param width Midline width of the frame
+   \sa midLineWidth(), setLineWidth()
+*/
+void QwtPlotGLCanvas::setMidLineWidth( int width )
+{
+    width = qMax( width, 0 );
+    if ( width != d_data->midLineWidth )
+    {
+        d_data->midLineWidth = width;
+        qwtUpdateContentsRect( this );
+        update();
+    }
+}
+
+/*!
+  \return Midline width of the frame
+  \sa setMidLineWidth(), lineWidth()
+ */ 
+int QwtPlotGLCanvas::midLineWidth() const
+{
+    return d_data->midLineWidth;
+}
+
+/*!
+  \return Frame width depending on the style, line width and midline width.
+ */
+int QwtPlotGLCanvas::frameWidth() const
+{
+    return ( frameStyle() != NoFrame ) ? d_data->lineWidth : 0;
+}
+
+/*!
+  Paint event
+
+  \param event Paint event
+  \sa QwtPlot::drawCanvas()
+*/
+void QwtPlotGLCanvas::paintEvent( QPaintEvent *event )
+{
+    Q_UNUSED( event );
+
+    QPainter painter( this );
+
+#if FIX_GL_TRANSLATION
+    if ( painter.paintEngine()->type() == QPaintEngine::OpenGL2 )
+    {
+        // work around a translation bug of QPaintEngine::OpenGL2
+        painter.translate( 1, 1 );
+    }
+#endif
+
+    drawBackground( &painter );
+    drawItems( &painter );
+
+    if ( !testAttribute( Qt::WA_StyledBackground ) )
+    {
+        if ( frameWidth() > 0 )
+            drawBorder( &painter );
+    }
+}
+/*!
+  Qt event handler for QEvent::PolishRequest and QEvent::StyleChange
+  \param event Qt Event
+  \return See QGLWidget::event()
+*/
+bool QwtPlotGLCanvas::event( QEvent *event )
+{
+    const bool ok = QOpenGLWidget::event( event );
+
+    if ( event->type() == QEvent::PolishRequest ||
+        event->type() == QEvent::StyleChange )
+    {
+        // assuming, that we always have a styled background
+        // when we have a style sheet
+
+        setAttribute( Qt::WA_StyledBackground,
+            testAttribute( Qt::WA_StyleSheet ) );
+    }
+
+    return ok;
+}
+
+/*!
+  Draw the plot items
+  \param painter Painter
+
+  \sa QwtPlot::drawCanvas()
+*/  
+void QwtPlotGLCanvas::drawItems( QPainter *painter )
+{
+    painter->save();
+
+    painter->setClipRect( contentsRect(), Qt::IntersectClip );
+
+    QwtPlot *plot = qobject_cast< QwtPlot *>( parent() );
+    if ( plot )
+        plot->drawCanvas( painter );
+
+    painter->restore();
+}
+
+/*!
+  Draw the background of the canvas
+  \param painter Painter
+*/ 
+void QwtPlotGLCanvas::drawBackground( QPainter *painter )
+{
+    painter->save();
+
+    QWidget *w = qwtBGWidget( this );
+
+    const QPoint off = mapTo( w, QPoint() );
+    painter->translate( -off );
+
+    const QRect fillRect = rect().translated( off );
+
+    if ( w->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        painter->setClipRect( fillRect );
+
+        QStyleOption opt;
+        opt.initFrom( w );
+        w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w);
+    }
+    else 
+    {
+        painter->fillRect( fillRect,
+            w->palette().brush( w->backgroundRole() ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw the border of the canvas
+  \param painter Painter
+*/
+void QwtPlotGLCanvas::drawBorder( QPainter *painter )
+{
+    const int fw = frameWidth();
+    if ( fw <= 0 )
+        return;
+
+    if ( frameShadow() == QwtPlotGLCanvas::Plain )
+    {
+        qDrawPlainRect( painter, frameRect(), 
+            palette().shadow().color(), lineWidth() );
+    }
+    else
+    {
+        if ( frameShape() == QwtPlotGLCanvas::Box )
+        {
+            qDrawShadeRect( painter, frameRect(), palette(),
+                frameShadow() == Sunken, lineWidth(), midLineWidth() );
+        }
+        else
+        {
+            qDrawShadePanel( painter, frameRect(), palette(), 
+                frameShadow() == Sunken, lineWidth() );
+        }
+    }
+}
+
+//! Calls repaint()
+void QwtPlotGLCanvas::replot()
+{
+    repaint();
+}
+
+/*!
+   \return Empty path
+*/
+QPainterPath QwtPlotGLCanvas::borderPath( const QRect &rect ) const
+{
+    Q_UNUSED( rect );
+    return QPainterPath();
+}
+
+//! \return The rectangle where the frame is drawn in.
+QRect QwtPlotGLCanvas::frameRect() const
+{
+    const int fw = frameWidth();
+    return contentsRect().adjusted( -fw, -fw, fw, fw );
+}
diff --git a/source/third_party/qwt/qwt_plot_grid.cpp b/source/third_party/qwt/qwt_plot_grid.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4868089c100269d721f8e3e85efd5f3812a452ef
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_grid.cpp
@@ -0,0 +1,438 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_grid.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_math.h"
+#include <qpainter.h>
+#include <qpen.h>
+
+class QwtPlotGrid::PrivateData
+{
+public:
+    PrivateData():
+        xEnabled( true ),
+        yEnabled( true ),
+        xMinEnabled( false ),
+        yMinEnabled( false )
+    {
+    }
+
+    bool xEnabled;
+    bool yEnabled;
+    bool xMinEnabled;
+    bool yMinEnabled;
+
+    QwtScaleDiv xScaleDiv;
+    QwtScaleDiv yScaleDiv;
+
+    QPen majorPen;
+    QPen minorPen;
+};
+
+//! Enables major grid, disables minor grid
+QwtPlotGrid::QwtPlotGrid():
+    QwtPlotItem( QwtText( "Grid" ) )
+{
+    d_data = new PrivateData;
+
+    setItemInterest( QwtPlotItem::ScaleInterest, true );
+    setZ( 10.0 );
+}
+
+//! Destructor
+QwtPlotGrid::~QwtPlotGrid()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotGrid
+int QwtPlotGrid::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotGrid;
+}
+
+/*!
+  \brief Enable or disable vertical grid lines
+  \param on Enable (true) or disable
+
+  \sa Minor grid lines can be enabled or disabled with
+      enableXMin()
+*/
+void QwtPlotGrid::enableX( bool on )
+{
+    if ( d_data->xEnabled != on )
+    {
+        d_data->xEnabled = on;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Enable or disable horizontal grid lines
+  \param on Enable (true) or disable
+  \sa Minor grid lines can be enabled or disabled with enableYMin()
+*/
+void QwtPlotGrid::enableY( bool on )
+{
+    if ( d_data->yEnabled != on )
+    {
+        d_data->yEnabled = on;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Enable or disable  minor vertical grid lines.
+  \param on Enable (true) or disable
+  \sa enableX()
+*/
+void QwtPlotGrid::enableXMin( bool on )
+{
+    if ( d_data->xMinEnabled != on )
+    {
+        d_data->xMinEnabled = on;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Enable or disable minor horizontal grid lines
+  \param on Enable (true) or disable
+  \sa enableY()
+*/
+void QwtPlotGrid::enableYMin( bool on )
+{
+    if ( d_data->yMinEnabled != on )
+    {
+        d_data->yMinEnabled = on;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  Assign an x axis scale division
+
+  \param scaleDiv Scale division
+*/
+void QwtPlotGrid::setXDiv( const QwtScaleDiv &scaleDiv )
+{
+    if ( d_data->xScaleDiv != scaleDiv )
+    {
+        d_data->xScaleDiv = scaleDiv;
+        itemChanged();
+    }
+}
+
+/*!
+  Assign a y axis division
+
+  \param scaleDiv Scale division
+*/
+void QwtPlotGrid::setYDiv( const QwtScaleDiv &scaleDiv )
+{
+    if ( d_data->yScaleDiv != scaleDiv )
+    {
+        d_data->yScaleDiv = scaleDiv;
+        itemChanged();
+    }
+}
+
+/*!
+  Build and assign a pen for both major and minor grid lines
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtPlotGrid::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen for both major and minor grid lines
+
+  \param pen Pen
+  \sa setMajorPen(), setMinorPen()
+*/
+void QwtPlotGrid::setPen( const QPen &pen )
+{
+    if ( d_data->majorPen != pen || d_data->minorPen != pen )
+    {
+        d_data->majorPen = pen;
+        d_data->minorPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  Build and assign a pen for both major grid lines
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtPlotGrid::setMajorPen( const QColor &color, qreal width, Qt::PenStyle style )
+{
+    setMajorPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen for the major grid lines
+
+  \param pen Pen
+  \sa majorPen(), setMinorPen(), setPen()
+*/
+void QwtPlotGrid::setMajorPen( const QPen &pen )
+{
+    if ( d_data->majorPen != pen )
+    {
+        d_data->majorPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  Build and assign a pen for the minor grid lines
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtPlotGrid::setMinorPen( const QColor &color, qreal width, Qt::PenStyle style )
+{
+    setMinorPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen for the minor grid lines
+
+  \param pen Pen
+  \sa minorPen(), setMajorPen(), setPen()
+*/
+void QwtPlotGrid::setMinorPen( const QPen &pen )
+{
+    if ( d_data->minorPen != pen )
+    {
+        d_data->minorPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Draw the grid
+
+  The grid is drawn into the bounding rectangle such that
+  grid lines begin and end at the rectangle's borders. The X and Y
+  maps are used to map the scale divisions into the drawing region
+  screen.
+
+  \param painter  Painter
+  \param xMap X axis map
+  \param yMap Y axis
+  \param canvasRect Contents rectangle of the plot canvas
+*/
+void QwtPlotGrid::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    //  draw minor grid lines
+    QPen minorPen = d_data->minorPen;
+    minorPen.setCapStyle( Qt::FlatCap );
+
+    painter->setPen( minorPen );
+
+    if ( d_data->xEnabled && d_data->xMinEnabled )
+    {
+        drawLines( painter, canvasRect, Qt::Vertical, xMap,
+            d_data->xScaleDiv.ticks( QwtScaleDiv::MinorTick ) );
+        drawLines( painter, canvasRect, Qt::Vertical, xMap,
+            d_data->xScaleDiv.ticks( QwtScaleDiv::MediumTick ) );
+    }
+
+    if ( d_data->yEnabled && d_data->yMinEnabled )
+    {
+        drawLines( painter, canvasRect, Qt::Horizontal, yMap,
+            d_data->yScaleDiv.ticks( QwtScaleDiv::MinorTick ) );
+        drawLines( painter, canvasRect, Qt::Horizontal, yMap,
+            d_data->yScaleDiv.ticks( QwtScaleDiv::MediumTick ) );
+    }
+
+    //  draw major grid lines
+    QPen majorPen = d_data->majorPen;
+    majorPen.setCapStyle( Qt::FlatCap );
+
+    painter->setPen( majorPen );
+
+    if ( d_data->xEnabled )
+    {
+        drawLines( painter, canvasRect, Qt::Vertical, xMap,
+            d_data->xScaleDiv.ticks( QwtScaleDiv::MajorTick ) );
+    }
+
+    if ( d_data->yEnabled )
+    {
+        drawLines( painter, canvasRect, Qt::Horizontal, yMap,
+            d_data->yScaleDiv.ticks( QwtScaleDiv::MajorTick ) );
+    }
+}
+
+void QwtPlotGrid::drawLines( QPainter *painter, const QRectF &canvasRect,
+    Qt::Orientation orientation, const QwtScaleMap &scaleMap,
+    const QList<double> &values ) const
+{
+    const double x1 = canvasRect.left();
+    const double x2 = canvasRect.right() - 1.0;
+    const double y1 = canvasRect.top();
+    const double y2 = canvasRect.bottom() - 1.0;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    for ( int i = 0; i < values.count(); i++ )
+    {
+        double value = scaleMap.transform( values[i] );
+        if ( doAlign )
+            value = qRound( value );
+
+        if ( orientation == Qt::Horizontal )
+        {
+            if ( qwtFuzzyGreaterOrEqual( value, y1 ) &&
+                qwtFuzzyLessOrEqual( value, y2 ) )
+            {
+                QwtPainter::drawLine( painter, x1, value, x2, value );
+            }
+        }
+        else
+        {
+            if ( qwtFuzzyGreaterOrEqual( value, x1 ) &&
+                qwtFuzzyLessOrEqual( value, x2 ) )
+            {
+                QwtPainter::drawLine( painter, value, y1, value, y2 );
+            }
+        }
+    }
+}
+
+/*!
+  \return the pen for the major grid lines
+  \sa setMajorPen(), setMinorPen(), setPen()
+*/
+const QPen &QwtPlotGrid::majorPen() const
+{
+    return d_data->majorPen;
+}
+
+/*!
+  \return the pen for the minor grid lines
+  \sa setMinorPen(), setMajorPen(), setPen()
+*/
+const QPen &QwtPlotGrid::minorPen() const
+{
+    return d_data->minorPen;
+}
+
+/*!
+  \return true if vertical grid lines are enabled
+  \sa enableX()
+*/
+bool QwtPlotGrid::xEnabled() const
+{
+    return d_data->xEnabled;
+}
+
+/*!
+  \return true if minor vertical grid lines are enabled
+  \sa enableXMin()
+*/
+bool QwtPlotGrid::xMinEnabled() const
+{
+    return d_data->xMinEnabled;
+}
+
+/*!
+  \return true if horizontal grid lines are enabled
+  \sa enableY()
+*/
+bool QwtPlotGrid::yEnabled() const
+{
+    return d_data->yEnabled;
+}
+
+/*!
+  \return true if minor horizontal grid lines are enabled
+  \sa enableYMin()
+*/
+bool QwtPlotGrid::yMinEnabled() const
+{
+    return d_data->yMinEnabled;
+}
+
+
+/*! \return the scale division of the x axis */
+const QwtScaleDiv &QwtPlotGrid::xScaleDiv() const
+{
+    return d_data->xScaleDiv;
+}
+
+/*! \return the scale division of the y axis */
+const QwtScaleDiv &QwtPlotGrid::yScaleDiv() const
+{
+    return d_data->yScaleDiv;
+}
+
+/*!
+   Update the grid to changes of the axes scale division
+
+   \param xScaleDiv Scale division of the x-axis
+   \param yScaleDiv Scale division of the y-axis
+
+   \sa QwtPlot::updateAxes()
+*/
+void QwtPlotGrid::updateScaleDiv( const QwtScaleDiv& xScaleDiv,
+    const QwtScaleDiv& yScaleDiv )
+{
+    setXDiv( xScaleDiv );
+    setYDiv( yScaleDiv );
+}
diff --git a/source/third_party/qwt/qwt_plot_histogram.cpp b/source/third_party/qwt/qwt_plot_histogram.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2c0b6dbc76e6efc3133c663af0b0342fbf356c05
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_histogram.cpp
@@ -0,0 +1,690 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_histogram.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_column_symbol.h"
+#include "qwt/qwt_scale_map.h"
+#include <qstring.h>
+#include <qpainter.h>
+
+static inline bool qwtIsCombinable( const QwtInterval &d1,
+    const QwtInterval &d2 )
+{
+    if ( d1.isValid() && d2.isValid() )
+    {
+        if ( d1.maxValue() == d2.minValue() )
+        {
+            if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum
+                && d2.borderFlags() & QwtInterval::ExcludeMinimum ) )
+            {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+class QwtPlotHistogram::PrivateData
+{
+public:
+    PrivateData():
+        baseline( 0.0 ),
+        style( Columns ),
+        symbol( NULL )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete symbol;
+    }
+
+    double baseline;
+
+    QPen pen;
+    QBrush brush;
+    QwtPlotHistogram::HistogramStyle style;
+    const QwtColumnSymbol *symbol;
+};
+
+/*!
+  Constructor
+  \param title Title of the histogram.
+*/
+QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the histogram.
+*/
+QwtPlotHistogram::QwtPlotHistogram( const QString &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotHistogram::~QwtPlotHistogram()
+{
+    delete d_data;
+}
+
+//! Initialize data members
+void QwtPlotHistogram::init()
+{
+    d_data = new PrivateData();
+    setData( new QwtIntervalSeriesData() );
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, true );
+
+    setZ( 20.0 );
+}
+
+/*!
+  Set the histogram's drawing style
+
+  \param style Histogram style
+  \sa HistogramStyle, style()
+*/
+void QwtPlotHistogram::setStyle( HistogramStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+    \return Style of the histogram
+    \sa HistogramStyle, setStyle()
+*/
+QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */
+void QwtPlotHistogram::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}   
+
+/*!
+  Assign a pen, that is used in a style() depending way.
+
+  \param pen New pen
+  \sa pen(), brush()
+*/
+void QwtPlotHistogram::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen used in a style() depending way.
+  \sa setPen(), brush()
+*/
+const QPen &QwtPlotHistogram::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  Assign a brush, that is used in a style() depending way.
+
+  \param brush New brush
+  \sa pen(), brush()
+*/
+void QwtPlotHistogram::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used in a style() depending way.
+  \sa setPen(), brush()
+*/
+const QBrush &QwtPlotHistogram::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  \brief Assign a symbol
+
+  In Column style an optional symbol can be assigned, that is responsible
+  for displaying the rectangle that is defined by the interval and
+  the distance between baseline() and value. When no symbol has been
+  defined the area is displayed as plain rectangle using pen() and brush().
+
+  \sa style(), symbol(), drawColumn(), pen(), brush()
+
+  \note In applications, where different intervals need to be displayed
+        in a different way ( f.e different colors or even using different symbols)
+        it is recommended to overload drawColumn().
+*/
+void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Current symbol or NULL, when no symbol has been assigned
+  \sa setSymbol()
+*/
+const QwtColumnSymbol *QwtPlotHistogram::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  \brief Set the value of the baseline
+
+  Each column representing an QwtIntervalSample is defined by its
+  interval and the interval between baseline and the value of the sample.
+
+  The default value of the baseline is 0.0.
+
+  \param value Value of the baseline
+  \sa baseline()
+*/
+void QwtPlotHistogram::setBaseline( double value )
+{
+    if ( d_data->baseline != value )
+    {
+        d_data->baseline = value;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Value of the baseline
+  \sa setBaseline()
+*/
+double QwtPlotHistogram::baseline() const
+{
+    return d_data->baseline;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotHistogram::boundingRect() const
+{
+    QRectF rect = data()->boundingRect();
+    if ( !rect.isValid() )
+        return rect;
+
+    if ( orientation() == Qt::Horizontal )
+    {
+        rect = QRectF( rect.y(), rect.x(),
+            rect.height(), rect.width() );
+
+        if ( rect.left() > d_data->baseline )
+            rect.setLeft( d_data->baseline );
+        else if ( rect.right() < d_data->baseline )
+            rect.setRight( d_data->baseline );
+    }
+    else
+    {
+        if ( rect.bottom() < d_data->baseline )
+            rect.setBottom( d_data->baseline );
+        else if ( rect.top() > d_data->baseline )
+            rect.setTop( d_data->baseline );
+    }
+
+    return rect;
+}
+
+//! \return QwtPlotItem::Rtti_PlotHistogram
+int QwtPlotHistogram::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotHistogram;
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of points
+*/
+void QwtPlotHistogram::setSamples(
+    const QVector<QwtIntervalSample> &samples )
+{
+    setData( new QwtIntervalSeriesData( samples ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/
+void QwtPlotHistogram::setSamples( 
+    QwtSeriesData<QwtIntervalSample> *data )
+{
+    setData( data );
+}
+
+/*!
+  Draw a subset of the histogram samples
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawOutline(), drawLines(), drawColumns
+*/
+void QwtPlotHistogram::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &, int from, int to ) const
+{
+    if ( !painter || dataSize() <= 0 )
+        return;
+
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    switch ( d_data->style )
+    {
+        case Outline:
+            drawOutline( painter, xMap, yMap, from, to );
+            break;
+        case Lines:
+            drawLines( painter, xMap, yMap, from, to );
+            break;
+        case Columns:
+            drawColumns( painter, xMap, yMap, from, to );
+            break;
+        default:
+            break;
+    }
+}
+
+/*!
+  Draw a histogram in Outline style()
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         histogram will be painted to its last point.
+
+  \sa setStyle(), style()
+  \warning The outline style requires, that the intervals are in increasing
+           order and not overlapping.
+*/
+void QwtPlotHistogram::drawOutline( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    int from, int to ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double v0 = ( orientation() == Qt::Horizontal ) ?
+        xMap.transform( baseline() ) : yMap.transform( baseline() );
+    if ( doAlign )
+        v0 = qRound( v0 );
+
+    QwtIntervalSample previous;
+
+    QPolygonF polygon;
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtIntervalSample sample = this->sample( i );
+
+        if ( !sample.interval.isValid() )
+        {
+            flushPolygon( painter, v0, polygon );
+            previous = sample;
+            continue;
+        }
+
+        if ( previous.interval.isValid() )
+        {
+            if ( !qwtIsCombinable( previous.interval, sample.interval ) )
+                flushPolygon( painter, v0, polygon );
+        }
+
+        if ( orientation() == Qt::Vertical )
+        {
+            double x1 = xMap.transform( sample.interval.minValue() );
+            double x2 = xMap.transform( sample.interval.maxValue() );
+            double y = yMap.transform( sample.value );
+            if ( doAlign )
+            {
+                x1 = qRound( x1 );
+                x2 = qRound( x2 );
+                y = qRound( y );
+            }
+
+            if ( polygon.size() == 0 )
+                polygon += QPointF( x1, v0 );
+
+            polygon += QPointF( x1, y );
+            polygon += QPointF( x2, y );
+        }
+        else
+        {
+            double y1 = yMap.transform( sample.interval.minValue() );
+            double y2 = yMap.transform( sample.interval.maxValue() );
+            double x = xMap.transform( sample.value );
+            if ( doAlign )
+            {
+                y1 = qRound( y1 );
+                y2 = qRound( y2 );
+                x = qRound( x );
+            }
+
+            if ( polygon.size() == 0 )
+                polygon += QPointF( v0, y1 );
+
+            polygon += QPointF( x, y1 );
+            polygon += QPointF( x, y2 );
+        }
+        previous = sample;
+    }
+
+    flushPolygon( painter, v0, polygon );
+}
+
+/*!
+  Draw a histogram in Columns style()
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         histogram will be painted to its last point.
+
+  \sa setStyle(), style(), setSymbol(), drawColumn()
+*/
+void QwtPlotHistogram::drawColumns( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    int from, int to ) const
+{
+    painter->setPen( d_data->pen );
+    painter->setBrush( d_data->brush );
+
+    const QwtSeriesData<QwtIntervalSample> *series = data();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtIntervalSample sample = series->sample( i );
+        if ( !sample.interval.isNull() )
+        {
+            const QwtColumnRect rect = columnRect( sample, xMap, yMap );
+            drawColumn( painter, rect, sample );
+        }
+    }
+}
+
+/*!
+  Draw a histogram in Lines style()
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         histogram will be painted to its last point.
+
+  \sa setStyle(), style(), setPen()
+*/
+void QwtPlotHistogram::drawLines( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    int from, int to ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    painter->setPen( d_data->pen );
+    painter->setBrush( Qt::NoBrush );
+
+    const QwtSeriesData<QwtIntervalSample> *series = data();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtIntervalSample sample = series->sample( i );
+        if ( !sample.interval.isNull() )
+        {
+            const QwtColumnRect rect = columnRect( sample, xMap, yMap );
+
+            QRectF r = rect.toRect();
+            if ( doAlign )
+            {
+                r.setLeft( qRound( r.left() ) );
+                r.setRight( qRound( r.right() ) );
+                r.setTop( qRound( r.top() ) );
+                r.setBottom( qRound( r.bottom() ) );
+            }
+
+            switch ( rect.direction )
+            {
+                case QwtColumnRect::LeftToRight:
+                {
+                    QwtPainter::drawLine( painter,
+                        r.topRight(), r.bottomRight() );
+                    break;
+                }
+                case QwtColumnRect::RightToLeft:
+                {
+                    QwtPainter::drawLine( painter,
+                        r.topLeft(), r.bottomLeft() );
+                    break;
+                }
+                case QwtColumnRect::TopToBottom:
+                {
+                    QwtPainter::drawLine( painter,
+                        r.bottomRight(), r.bottomLeft() );
+                    break;
+                }
+                case QwtColumnRect::BottomToTop:
+                {
+                    QwtPainter::drawLine( painter,
+                        r.topRight(), r.topLeft() );
+                    break;
+                }
+            }
+        }
+    }
+}
+
+//! Internal, used by the Outline style.
+void QwtPlotHistogram::flushPolygon( QPainter *painter,
+    double baseLine, QPolygonF &polygon ) const
+{
+    if ( polygon.size() == 0 )
+        return;
+
+    if ( orientation() == Qt::Horizontal )
+        polygon += QPointF( baseLine, polygon.last().y() );
+    else
+        polygon += QPointF( polygon.last().x(), baseLine );
+
+    if ( d_data->brush.style() != Qt::NoBrush )
+    {
+        painter->setPen( Qt::NoPen );
+        painter->setBrush( d_data->brush );
+
+        if ( orientation() == Qt::Horizontal )
+        {
+            polygon += QPointF( polygon.last().x(), baseLine );
+            polygon += QPointF( polygon.first().x(), baseLine );
+        }
+        else
+        {
+            polygon += QPointF( baseLine, polygon.last().y() );
+            polygon += QPointF( baseLine, polygon.first().y() );
+        }
+
+        QwtPainter::drawPolygon( painter, polygon );
+
+        polygon.pop_back();
+        polygon.pop_back();
+    }
+    if ( d_data->pen.style() != Qt::NoPen )
+    {
+        painter->setBrush( Qt::NoBrush );
+        painter->setPen( d_data->pen );
+        QwtPainter::drawPolyline( painter, polygon );
+    }
+    polygon.clear();
+}
+
+/*!
+  Calculate the area that is covered by a sample
+
+  \param sample Sample
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+
+  \return Rectangle, that is covered by a sample
+*/
+QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const
+{
+    QwtColumnRect rect;
+
+    const QwtInterval &iv = sample.interval;
+    if ( !iv.isValid() )
+        return rect;
+
+    if ( orientation() == Qt::Horizontal )
+    {
+        const double x0 = xMap.transform( baseline() );
+        const double x  = xMap.transform( sample.value );
+        const double y1 = yMap.transform( iv.minValue() );
+        const double y2 = yMap.transform( iv.maxValue() );
+
+        rect.hInterval.setInterval( x0, x );
+        rect.vInterval.setInterval( y1, y2, iv.borderFlags() );
+        rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft :
+                         QwtColumnRect::LeftToRight;
+    }
+    else
+    {
+        const double x1 = xMap.transform( iv.minValue() );
+        const double x2 = xMap.transform( iv.maxValue() );
+        const double y0 = yMap.transform( baseline() );
+        const double y = yMap.transform( sample.value );
+
+        rect.hInterval.setInterval( x1, x2, iv.borderFlags() );
+        rect.vInterval.setInterval( y0, y );
+        rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop :
+            QwtColumnRect::TopToBottom;
+    }
+
+    return rect;
+}
+
+/*!
+  Draw a column for a sample in Columns style().
+
+  When a symbol() has been set the symbol is used otherwise the
+  column is displayed as plain rectangle using pen() and brush().
+
+  \param painter Painter
+  \param rect Rectangle where to paint the column in paint device coordinates
+  \param sample Sample to be displayed
+
+  \note In applications, where different intervals need to be displayed
+        in a different way ( f.e different colors or even using different symbols)
+        it is recommended to overload drawColumn().
+*/
+void QwtPlotHistogram::drawColumn( QPainter *painter,
+    const QwtColumnRect &rect, const QwtIntervalSample &sample ) const
+{
+    Q_UNUSED( sample );
+
+    if ( d_data->symbol &&
+        ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) )
+    {
+        d_data->symbol->draw( painter, rect );
+    }
+    else
+    {
+        QRectF r = rect.toRect();
+        if ( QwtPainter::roundingAlignment( painter ) )
+        {
+            r.setLeft( qRound( r.left() ) );
+            r.setRight( qRound( r.right() ) );
+            r.setTop( qRound( r.top() ) );
+            r.setBottom( qRound( r.bottom() ) );
+        }
+
+        QwtPainter::drawRect( painter, r );
+    }
+}
+
+/*!
+  A plain rectangle without pen using the brush()
+
+  \param index Index of the legend entry 
+                ( ignored as there is only one )
+  \param size Icon size
+  \return A graphic displaying the icon
+    
+  \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
+*/
+QwtGraphic QwtPlotHistogram::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+    return defaultIcon( d_data->brush, size );
+}
diff --git a/source/third_party/qwt/qwt_plot_intervalcurve.cpp b/source/third_party/qwt/qwt_plot_intervalcurve.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe3a623450c67154d0708b44a268209f786f1a8b
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_intervalcurve.cpp
@@ -0,0 +1,603 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_intervalcurve.h"
+#include "qwt/qwt_interval_symbol.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_clipper.h"
+#include "qwt/qwt_painter.h"
+#include <string.h>
+
+#include <qpainter.h>
+
+static inline bool qwtIsHSampleInside( const QwtIntervalSample &sample,
+    double xMin, double xMax, double yMin, double yMax )
+{
+    const double y = sample.value;
+    const double x1 = sample.interval.minValue();
+    const double x2 = sample.interval.maxValue();
+
+    const bool isOffScreen = ( y < yMin ) || ( y > yMax )
+        || ( x1 < xMin && x2 < xMin ) || ( x1 > xMax && x2 > xMax );
+
+    return !isOffScreen;
+}
+
+static inline bool qwtIsVSampleInside( const QwtIntervalSample &sample,
+    double xMin, double xMax, double yMin, double yMax )
+{
+    const double x = sample.value;
+    const double y1 = sample.interval.minValue();
+    const double y2 = sample.interval.maxValue();
+
+    const bool isOffScreen = ( x < xMin ) || ( x > xMax )
+        || ( y1 < yMin && y2 < yMin ) || ( y1 > yMax && y2 > yMax );
+
+    return !isOffScreen;
+}
+
+class QwtPlotIntervalCurve::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtPlotIntervalCurve::Tube ),
+        symbol( NULL ),
+        pen( Qt::black ),
+        brush( Qt::white )
+    {
+        paintAttributes = QwtPlotIntervalCurve::ClipPolygons;
+        paintAttributes |= QwtPlotIntervalCurve::ClipSymbol;
+
+        pen.setCapStyle( Qt::FlatCap );
+    }
+
+    ~PrivateData()
+    {
+        delete symbol;
+    }
+
+    QwtPlotIntervalCurve::CurveStyle style;
+    const QwtIntervalSymbol *symbol;
+
+    QPen pen;
+    QBrush brush;
+
+    QwtPlotIntervalCurve::PaintAttributes paintAttributes;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QString &title ):
+    QwtPlotSeriesItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotIntervalCurve::~QwtPlotIntervalCurve()
+{
+    delete d_data;
+}
+
+//! Initialize internal members
+void QwtPlotIntervalCurve::init()
+{
+    setItemAttribute( QwtPlotItem::Legend, true );
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+
+    d_data = new PrivateData;
+    setData( new QwtIntervalSeriesData() );
+
+    setZ( 19.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotIntervalCurve
+int QwtPlotIntervalCurve::rtti() const
+{
+    return QwtPlotIntervalCurve::Rtti_PlotIntervalCurve;
+}
+
+/*!
+  Specify an attribute how to draw the curve
+
+  \param attribute Paint attribute
+  \param on On/Off
+  \sa testPaintAttribute()
+*/
+void QwtPlotIntervalCurve::setPaintAttribute(
+    PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa PaintAttribute, setPaintAttribute()
+*/
+bool QwtPlotIntervalCurve::testPaintAttribute(
+    PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of samples
+*/
+void QwtPlotIntervalCurve::setSamples(
+    const QVector<QwtIntervalSample> &samples )
+{
+    setData( new QwtIntervalSeriesData( samples ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/
+void QwtPlotIntervalCurve::setSamples( 
+    QwtSeriesData<QwtIntervalSample> *data )
+{
+    setData( data );
+}
+
+/*!
+  Set the curve's drawing style
+
+  \param style Curve style
+  \sa CurveStyle, style()
+*/
+void QwtPlotIntervalCurve::setStyle( CurveStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+    \return Style of the curve
+    \sa setStyle()
+*/
+QwtPlotIntervalCurve::CurveStyle QwtPlotIntervalCurve::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  Assign a symbol.
+
+  \param symbol Symbol
+  \sa symbol()
+*/
+void QwtPlotIntervalCurve::setSymbol( const QwtIntervalSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Current symbol or NULL, when no symbol has been assigned
+  \sa setSymbol()
+*/
+const QwtIntervalSymbol *QwtPlotIntervalCurve::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */
+void QwtPlotIntervalCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}   
+
+/*!
+  \brief Assign a pen
+  \param pen New pen
+  \sa pen(), brush()
+*/
+void QwtPlotIntervalCurve::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+    \return Pen used to draw the lines
+    \sa setPen(), brush()
+*/
+const QPen& QwtPlotIntervalCurve::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  Assign a brush.
+
+  The brush is used to fill the area in Tube style().
+
+  \param brush Brush
+  \sa brush(), pen(), setStyle(), CurveStyle
+*/
+void QwtPlotIntervalCurve::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used to fill the area in Tube style()
+  \sa setBrush(), setStyle(), CurveStyle
+*/
+const QBrush& QwtPlotIntervalCurve::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotIntervalCurve::boundingRect() const
+{
+    QRectF rect = QwtPlotSeriesItem::boundingRect();
+    if ( rect.isValid() && orientation() == Qt::Vertical )
+        rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
+
+    return rect;
+}
+
+/*!
+  Draw a subset of the samples
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawTube(), drawSymbols()
+*/
+void QwtPlotIntervalCurve::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+    switch ( d_data->style )
+    {
+        case Tube:
+            drawTube( painter, xMap, yMap, canvasRect, from, to );
+            break;
+
+        case NoCurve:
+        default:
+            break;
+    }
+
+    if ( d_data->symbol &&
+        ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) )
+    {
+        drawSymbols( painter, *d_data->symbol,
+            xMap, yMap, canvasRect, from, to );
+    }
+}
+
+/*!
+  Draw a tube
+
+  Builds 2 curves from the upper and lower limits of the intervals
+  and draws them with the pen(). The area between the curves is
+  filled with the brush().
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawSeries(), drawSymbols()
+*/
+void QwtPlotIntervalCurve::drawTube( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    painter->save();
+
+    const size_t size = to - from + 1;
+    QPolygonF polygon( 2 * size );
+    QPointF *points = polygon.data();
+
+    for ( uint i = 0; i < size; i++ )
+    {
+        QPointF &minValue = points[i];
+        QPointF &maxValue = points[2 * size - 1 - i];
+
+        const QwtIntervalSample intervalSample = sample( from + i );
+        if ( orientation() == Qt::Vertical )
+        {
+            double x = xMap.transform( intervalSample.value );
+            double y1 = yMap.transform( intervalSample.interval.minValue() );
+            double y2 = yMap.transform( intervalSample.interval.maxValue() );
+            if ( doAlign )
+            {
+                x = qRound( x );
+                y1 = qRound( y1 );
+                y2 = qRound( y2 );
+            }
+
+            minValue.rx() = x;
+            minValue.ry() = y1;
+            maxValue.rx() = x;
+            maxValue.ry() = y2;
+        }
+        else
+        {
+            double y = yMap.transform( intervalSample.value );
+            double x1 = xMap.transform( intervalSample.interval.minValue() );
+            double x2 = xMap.transform( intervalSample.interval.maxValue() );
+            if ( doAlign )
+            {
+                y = qRound( y );
+                x1 = qRound( x1 );
+                x2 = qRound( x2 );
+            }
+
+            minValue.rx() = x1;
+            minValue.ry() = y;
+            maxValue.rx() = x2;
+            maxValue.ry() = y;
+        }
+    }
+
+    if ( d_data->brush.style() != Qt::NoBrush )
+    {
+        painter->setPen( QPen( Qt::NoPen ) );
+        painter->setBrush( d_data->brush );
+
+        if ( d_data->paintAttributes & ClipPolygons )
+        {
+            const qreal m = 1.0;
+            const QPolygonF p = QwtClipper::clipPolygonF(
+               canvasRect.adjusted( -m, -m, m, m ), polygon, true );
+
+            QwtPainter::drawPolygon( painter, p );
+        }
+        else
+        {
+            QwtPainter::drawPolygon( painter, polygon );
+        }
+    }
+
+    if ( d_data->pen.style() != Qt::NoPen )
+    {
+        painter->setPen( d_data->pen );
+        painter->setBrush( Qt::NoBrush );
+
+        if ( d_data->paintAttributes & ClipPolygons )
+        {
+            qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF() );
+            const QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw );
+
+            QPolygonF p;
+
+            p.resize( size );
+            ::memcpy( p.data(), points, size * sizeof( QPointF ) );
+            p = QwtClipper::clipPolygonF( clipRect, p );
+            QwtPainter::drawPolyline( painter, p );
+
+            p.resize( size );
+            ::memcpy( p.data(), points + size, size * sizeof( QPointF ) );
+            p = QwtClipper::clipPolygonF( clipRect, p );
+            QwtPainter::drawPolyline( painter, p );
+        }
+        else
+        {
+            QwtPainter::drawPolyline( painter, points, size );
+            QwtPainter::drawPolyline( painter, points + size, size );
+        }
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw symbols for a subset of the samples
+
+  \param painter Painter
+  \param symbol Interval symbol
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted
+
+  \sa setSymbol(), drawSeries(), drawTube()
+*/
+void QwtPlotIntervalCurve::drawSymbols(
+    QPainter *painter, const QwtIntervalSymbol &symbol,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    painter->save();
+
+    QPen pen = symbol.pen();
+    pen.setCapStyle( Qt::FlatCap );
+
+    painter->setPen( pen );
+    painter->setBrush( symbol.brush() );
+
+    const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect );
+
+    const double xMin = tr.left();
+    const double xMax = tr.right();
+    const double yMin = tr.top();
+    const double yMax = tr.bottom();
+
+    const bool doClip = d_data->paintAttributes & ClipSymbol;
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtIntervalSample s = sample( i );
+
+        if ( orientation() == Qt::Vertical )
+        {
+            if ( !doClip || qwtIsVSampleInside( s, xMin, xMax, yMin, yMax ) )
+            {
+                const double x = xMap.transform( s.value );
+                const double y1 = yMap.transform( s.interval.minValue() );
+                const double y2 = yMap.transform( s.interval.maxValue() );
+
+                symbol.draw( painter, orientation(),
+                    QPointF( x, y1 ), QPointF( x, y2 ) );
+            }
+        }
+        else
+        {
+            if ( !doClip || qwtIsHSampleInside( s, xMin, xMax, yMin, yMax ) )
+            {
+                const double y = yMap.transform( s.value );
+                const double x1 = xMap.transform( s.interval.minValue() );
+                const double x2 = xMap.transform( s.interval.maxValue() );
+
+                symbol.draw( painter, orientation(),
+                    QPointF( x1, y ), QPointF( x2, y ) );
+            }
+        }
+    }
+
+    painter->restore();
+}
+
+/*!
+  \return Icon for the legend
+
+  In case of Tube style() the icon is a plain rectangle filled with the brush().
+  If a symbol is assigned it is scaled to size.
+
+  \param index Index of the legend entry 
+               ( ignored as there is only one )
+  \param size Icon size
+    
+  \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData()
+*/
+QwtGraphic QwtPlotIntervalCurve::legendIcon( 
+    int index, const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+
+    if ( size.isEmpty() )
+        return QwtGraphic();
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+    icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &icon );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    if ( d_data->style == Tube )
+    {
+        QRectF r( 0, 0, size.width(), size.height() );
+        painter.fillRect( r, d_data->brush );
+    }
+
+    if ( d_data->symbol &&
+        ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) )
+    {
+        QPen pen = d_data->symbol->pen();
+        pen.setWidthF( pen.widthF() );
+        pen.setCapStyle( Qt::FlatCap );
+
+        painter.setPen( pen );
+        painter.setBrush( d_data->symbol->brush() );
+
+        if ( orientation() == Qt::Vertical )
+        {
+            const double x = 0.5 * size.width();
+
+            d_data->symbol->draw( &painter, orientation(),
+                QPointF( x, 0 ), QPointF( x, size.height() - 1.0 ) );
+        }
+        else
+        {
+            const double y = 0.5 * size.height();
+
+            d_data->symbol->draw( &painter, orientation(),
+                QPointF( 0.0, y ), QPointF( size.width() - 1.0, y ) );
+        }
+    }
+
+    return icon;
+}
diff --git a/source/third_party/qwt/qwt_plot_item.cpp b/source/third_party/qwt/qwt_plot_item.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f76c00752625debc3e73889e86cab2f797856673
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_item.cpp
@@ -0,0 +1,698 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_item.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_legend_data.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_graphic.h"
+#include <qpainter.h>
+
+class QwtPlotItem::PrivateData
+{
+public:
+    PrivateData():
+        plot( NULL ),
+        isVisible( true ),
+        attributes( 0 ),
+        interests( 0 ),
+        renderHints( 0 ),
+        renderThreadCount( 1 ),
+        z( 0.0 ),
+        xAxis( QwtPlot::xBottom ),
+        yAxis( QwtPlot::yLeft ),
+        legendIconSize( 8, 8 )
+    {
+    }
+
+    mutable QwtPlot *plot;
+
+    bool isVisible;
+
+    QwtPlotItem::ItemAttributes attributes;
+    QwtPlotItem::ItemInterests interests;
+
+    QwtPlotItem::RenderHints renderHints;
+    uint renderThreadCount;
+
+    double z;
+
+    int xAxis;
+    int yAxis;
+
+    QwtText title;
+    QSize legendIconSize;
+};
+
+/*!
+   Constructor
+   \param title Title of the item
+*/
+QwtPlotItem::QwtPlotItem( const QwtText &title )
+{
+    d_data = new PrivateData;
+    d_data->title = title;
+}
+
+//! Destroy the QwtPlotItem
+QwtPlotItem::~QwtPlotItem()
+{
+    attach( NULL );
+    delete d_data;
+}
+
+/*!
+  \brief Attach the item to a plot.
+
+  This method will attach a QwtPlotItem to the QwtPlot argument. It will first
+  detach the QwtPlotItem from any plot from a previous call to attach (if
+  necessary). If a NULL argument is passed, it will detach from any QwtPlot it
+  was attached to.
+
+  \param plot Plot widget
+  \sa detach()
+*/
+void QwtPlotItem::attach( QwtPlot *plot )
+{
+    if ( plot == d_data->plot )
+        return;
+
+    if ( d_data->plot )
+        d_data->plot->attachItem( this, false );
+
+    d_data->plot = plot;
+
+    if ( d_data->plot )
+        d_data->plot->attachItem( this, true );
+}
+
+/*!
+   \brief This method detaches a QwtPlotItem from any 
+          QwtPlot it has been associated with.
+
+   detach() is equivalent to calling attach( NULL )
+   \sa attach()
+*/
+void QwtPlotItem::detach()
+{
+    attach( NULL );
+}
+
+/*!
+   Return rtti for the specific class represented. QwtPlotItem is simply
+   a virtual interface class, and base classes will implement this method
+   with specific rtti values so a user can differentiate them.
+
+   The rtti value is useful for environments, where the
+   runtime type information is disabled and it is not possible
+   to do a dynamic_cast<...>.
+
+   \return rtti value
+   \sa RttiValues
+*/
+int QwtPlotItem::rtti() const
+{
+    return Rtti_PlotItem;
+}
+
+//! Return attached plot
+QwtPlot *QwtPlotItem::plot() const
+{
+    return d_data->plot;
+}
+
+/*!
+   Plot items are painted in increasing z-order.
+
+   \return setZ(), QwtPlotDict::itemList()
+*/
+double QwtPlotItem::z() const
+{
+    return d_data->z;
+}
+
+/*!
+   \brief Set the z value
+
+   Plot items are painted in increasing z-order.
+
+   \param z Z-value
+   \sa z(), QwtPlotDict::itemList()
+*/
+void QwtPlotItem::setZ( double z )
+{
+    if ( d_data->z != z )
+    {
+        if ( d_data->plot ) // update the z order
+            d_data->plot->attachItem( this, false );
+
+        d_data->z = z;
+
+        if ( d_data->plot )
+            d_data->plot->attachItem( this, true );
+
+        itemChanged();
+    }
+}
+
+/*!
+   Set a new title
+
+   \param title Title
+   \sa title()
+*/
+void QwtPlotItem::setTitle( const QString &title )
+{
+    setTitle( QwtText( title ) );
+}
+
+/*!
+   Set a new title
+
+   \param title Title
+   \sa title()
+*/
+void QwtPlotItem::setTitle( const QwtText &title )
+{
+    if ( d_data->title != title )
+    {
+        d_data->title = title;
+
+        legendChanged();
+#if 0
+        itemChanged();
+#endif
+    }
+}
+
+/*!
+   \return Title of the item
+   \sa setTitle()
+*/
+const QwtText &QwtPlotItem::title() const
+{
+    return d_data->title;
+}
+
+/*!
+   Toggle an item attribute
+
+   \param attribute Attribute type
+   \param on true/false
+
+   \sa testItemAttribute(), ItemInterest
+*/
+void QwtPlotItem::setItemAttribute( ItemAttribute attribute, bool on )
+{
+    if ( d_data->attributes.testFlag( attribute ) != on )
+    {
+        if ( on )
+            d_data->attributes |= attribute;
+        else
+            d_data->attributes &= ~attribute;
+
+        if ( attribute == QwtPlotItem::Legend )
+            legendChanged();
+
+        itemChanged();
+    }
+}
+
+/*!
+   Test an item attribute
+
+   \param attribute Attribute type
+   \return true/false
+   \sa setItemAttribute(), ItemInterest
+*/
+bool QwtPlotItem::testItemAttribute( ItemAttribute attribute ) const
+{
+    return d_data->attributes.testFlag( attribute );
+}
+
+/*!
+   Toggle an item interest
+
+   \param interest Interest type
+   \param on true/false
+
+   \sa testItemInterest(), ItemAttribute
+*/
+void QwtPlotItem::setItemInterest( ItemInterest interest, bool on )
+{
+    if ( d_data->interests.testFlag( interest ) != on )
+    {
+        if ( on )
+            d_data->interests |= interest;
+        else
+            d_data->interests &= ~interest;
+
+        itemChanged();
+    }
+}
+
+/*!
+   Test an item interest
+
+   \param interest Interest type
+   \return true/false
+   \sa setItemInterest(), ItemAttribute
+*/
+bool QwtPlotItem::testItemInterest( ItemInterest interest ) const
+{
+    return d_data->interests.testFlag( interest );
+}
+
+/*!
+   Toggle an render hint
+
+   \param hint Render hint
+   \param on true/false
+
+   \sa testRenderHint(), RenderHint
+*/
+void QwtPlotItem::setRenderHint( RenderHint hint, bool on )
+{
+    if ( d_data->renderHints.testFlag( hint ) != on )
+    {
+        if ( on )
+            d_data->renderHints |= hint;
+        else
+            d_data->renderHints &= ~hint;
+
+        itemChanged();
+    }
+}
+
+/*!
+   Test a render hint
+
+   \param hint Render hint
+   \return true/false
+   \sa setRenderHint(), RenderHint
+*/
+bool QwtPlotItem::testRenderHint( RenderHint hint ) const
+{
+    return d_data->renderHints.testFlag( hint );
+}
+
+/*!
+   On multi core systems rendering of certain plot item 
+   ( f.e QwtPlotRasterItem ) can be done in parallel in 
+   several threads.
+
+   The default setting is set to 1.
+
+   \param numThreads Number of threads to be used for rendering.
+                     If numThreads is set to 0, the system specific
+                     ideal thread count is used.
+
+   The default thread count is 1 ( = no additional threads )
+*/
+void QwtPlotItem::setRenderThreadCount( uint numThreads )
+{
+    d_data->renderThreadCount = numThreads;
+}
+
+/*!
+   \return Number of threads to be used for rendering.
+           If numThreads() is set to 0, the system specific
+           ideal thread count is used.
+*/
+uint QwtPlotItem::renderThreadCount() const
+{
+    return d_data->renderThreadCount;
+}
+
+/*!
+   Set the size of the legend icon
+
+   The default setting is 8x8 pixels
+
+   \param size Size
+   \sa legendIconSize(), legendIcon()
+*/
+void QwtPlotItem::setLegendIconSize( const QSize &size )
+{
+    if ( d_data->legendIconSize != size )
+    {
+        d_data->legendIconSize = size;
+        legendChanged();
+    }
+}
+
+/*!
+   \return Legend icon size
+   \sa setLegendIconSize(), legendIcon()
+*/
+QSize QwtPlotItem::legendIconSize() const
+{
+    return d_data->legendIconSize;
+}
+
+/*!
+   \return Icon representing the item on the legend
+
+   The default implementation returns an invalid icon
+
+   \param index Index of the legend entry 
+                ( usually there is only one )
+   \param size Icon size
+
+   \sa setLegendIconSize(), legendData()
+ */
+QwtGraphic QwtPlotItem::legendIcon( 
+    int index, const QSizeF &size ) const
+{
+    Q_UNUSED( index )
+    Q_UNUSED( size )
+
+    return QwtGraphic();
+}
+
+/*!
+   \brief Return a default icon from a brush
+
+   The default icon is a filled rectangle used
+   in several derived classes as legendIcon().
+
+   \param brush Fill brush
+   \param size Icon size
+
+   \return A filled rectangle
+ */
+QwtGraphic QwtPlotItem::defaultIcon( 
+    const QBrush &brush, const QSizeF &size ) const
+{   
+    QwtGraphic icon;
+    if ( !size.isEmpty() )
+    {
+        icon.setDefaultSize( size );
+        
+        QRectF r( 0, 0, size.width(), size.height() );
+        
+        QPainter painter( &icon );
+        painter.fillRect( r, brush );
+    }   
+    
+    return icon;
+}   
+
+//! Show the item
+void QwtPlotItem::show()
+{
+    setVisible( true );
+}
+
+//! Hide the item
+void QwtPlotItem::hide()
+{
+    setVisible( false );
+}
+
+/*!
+    Show/Hide the item
+
+    \param on Show if true, otherwise hide
+    \sa isVisible(), show(), hide()
+*/
+void QwtPlotItem::setVisible( bool on )
+{
+    if ( on != d_data->isVisible )
+    {
+        d_data->isVisible = on;
+        itemChanged();
+    }
+}
+
+/*!
+    \return true if visible
+    \sa setVisible(), show(), hide()
+*/
+bool QwtPlotItem::isVisible() const
+{
+    return d_data->isVisible;
+}
+
+/*!
+   Update the legend and call QwtPlot::autoRefresh() for the
+   parent plot.
+
+   \sa QwtPlot::legendChanged(), QwtPlot::autoRefresh()
+*/
+void QwtPlotItem::itemChanged()
+{
+    if ( d_data->plot )
+        d_data->plot->autoRefresh();
+}
+
+/*!
+   Update the legend of the parent plot.
+   \sa QwtPlot::updateLegend(), itemChanged()
+*/
+void QwtPlotItem::legendChanged()
+{
+    if ( testItemAttribute( QwtPlotItem::Legend ) && d_data->plot )
+        d_data->plot->updateLegend( this );
+}
+
+/*!
+   Set X and Y axis
+
+   The item will painted according to the coordinates of its Axes.
+
+   \param xAxis X Axis ( QwtPlot::xBottom or QwtPlot::xTop )
+   \param yAxis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight )
+
+   \sa setXAxis(), setYAxis(), xAxis(), yAxis(), QwtPlot::Axis
+*/
+void QwtPlotItem::setAxes( int xAxis, int yAxis )
+{
+    if ( xAxis == QwtPlot::xBottom || xAxis == QwtPlot::xTop )
+        d_data->xAxis = xAxis;
+
+    if ( yAxis == QwtPlot::yLeft || yAxis == QwtPlot::yRight )
+        d_data->yAxis = yAxis;
+
+    itemChanged();
+}
+
+/*!
+   Set the X axis
+
+   The item will painted according to the coordinates its Axes.
+
+   \param axis X Axis ( QwtPlot::xBottom or QwtPlot::xTop )
+   \sa setAxes(), setYAxis(), xAxis(), QwtPlot::Axis
+*/
+void QwtPlotItem::setXAxis( int axis )
+{
+    if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
+    {
+        d_data->xAxis = axis;
+        itemChanged();
+    }
+}
+
+/*!
+   Set the Y axis
+
+   The item will painted according to the coordinates its Axes.
+
+   \param axis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight )
+   \sa setAxes(), setXAxis(), yAxis(), QwtPlot::Axis
+*/
+void QwtPlotItem::setYAxis( int axis )
+{
+    if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight )
+    {
+        d_data->yAxis = axis;
+        itemChanged();
+    }
+}
+
+//! Return xAxis
+int QwtPlotItem::xAxis() const
+{
+    return d_data->xAxis;
+}
+
+//! Return yAxis
+int QwtPlotItem::yAxis() const
+{
+    return d_data->yAxis;
+}
+
+/*!
+   \return An invalid bounding rect: QRectF(1.0, 1.0, -2.0, -2.0)
+   \note A width or height < 0.0 is ignored by the autoscaler
+*/
+QRectF QwtPlotItem::boundingRect() const
+{
+    return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid
+}
+
+/*!
+   \brief Calculate a hint for the canvas margin
+
+   When the QwtPlotItem::Margins flag is enabled the plot item
+   indicates, that it needs some margins at the borders of the canvas.
+   This is f.e. used by bar charts to reserve space for displaying
+   the bars.
+
+   The margins are in target device coordinates ( pixels on screen )
+
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+   \param canvasRect Contents rectangle of the canvas in painter coordinates
+   \param left Returns the left margin
+   \param top Returns the top margin
+   \param right Returns the right margin
+   \param bottom Returns the bottom margin
+
+   \return The default implementation returns 0 for all margins
+
+   \sa QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins()
+ */
+void QwtPlotItem::getCanvasMarginHint( const QwtScaleMap &xMap, 
+    const QwtScaleMap &yMap, const QRectF &canvasRect,
+    double &left, double &top, double &right, double &bottom ) const
+{
+    Q_UNUSED( xMap );
+    Q_UNUSED( yMap );
+    Q_UNUSED( canvasRect );
+
+    // use QMargins, when we don't need to support Qt < 4.6 anymore
+    left = top = right = bottom = 0.0;
+}
+
+/*!
+   \brief Return all information, that is needed to represent
+          the item on the legend
+
+   Most items are represented by one entry on the legend
+   showing an icon and a text, but f.e. QwtPlotMultiBarChart
+   displays one entry for each bar.
+
+   QwtLegendData is basically a list of QVariants that makes it
+   possible to overload and reimplement legendData() to 
+   return almost any type of information, that is understood
+   by the receiver that acts as the legend.
+
+   The default implementation returns one entry with 
+   the title() of the item and the legendIcon().
+
+   \return Data, that is needed to represent the item on the legend
+   \sa title(), legendIcon(), QwtLegend, QwtPlotLegendItem
+ */
+QList<QwtLegendData> QwtPlotItem::legendData() const
+{
+    QwtLegendData data;
+
+    QwtText label = title();
+    label.setRenderFlags( label.renderFlags() & Qt::AlignLeft );
+            
+    QVariant titleValue;
+    qVariantSetValue( titleValue, label );
+    data.setValue( QwtLegendData::TitleRole, titleValue );
+        
+    const QwtGraphic graphic = legendIcon( 0, legendIconSize() );
+    if ( !graphic.isNull() )
+    {   
+        QVariant iconValue;
+        qVariantSetValue( iconValue, graphic );
+        data.setValue( QwtLegendData::IconRole, iconValue );
+    }   
+        
+    QList<QwtLegendData> list;
+    list += data;
+
+    return list;
+}
+
+/*!
+   \brief Update the item to changes of the axes scale division
+
+   Update the item, when the axes of plot have changed.
+   The default implementation does nothing, but items that depend
+   on the scale division (like QwtPlotGrid()) have to reimplement
+   updateScaleDiv()
+
+   updateScaleDiv() is only called when the ScaleInterest interest
+   is enabled. The default implementation does nothing.
+
+   \param xScaleDiv Scale division of the x-axis
+   \param yScaleDiv Scale division of the y-axis
+
+   \sa QwtPlot::updateAxes(), ScaleInterest
+*/
+void QwtPlotItem::updateScaleDiv( const QwtScaleDiv &xScaleDiv,
+    const QwtScaleDiv &yScaleDiv )
+{
+    Q_UNUSED( xScaleDiv );
+    Q_UNUSED( yScaleDiv );
+}
+
+/*!
+   \brief Update the item to changes of the legend info
+
+   Plot items that want to display a legend ( not those, that want to
+   be displayed on a legend ! ) will have to implement updateLegend().
+
+   updateLegend() is only called when the LegendInterest interest
+   is enabled. The default implementation does nothing.
+
+   \param item Plot item to be displayed on a legend
+   \param data Attributes how to display item on the legend
+
+   \sa QwtPlotLegendItem
+
+   \note Plot items, that want to be displayed on a legend
+         need to enable the QwtPlotItem::Legend flag and to implement
+         legendData() and legendIcon()
+ */
+void QwtPlotItem::updateLegend( const QwtPlotItem *item, 
+    const QList<QwtLegendData> &data )
+{
+    Q_UNUSED( item );
+    Q_UNUSED( data );
+}
+
+/*!
+   \brief Calculate the bounding scale rectangle of 2 maps
+
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+
+   \return Bounding scale rect of the scale maps, not normalized
+*/
+QRectF QwtPlotItem::scaleRect( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap ) const
+{
+    return QRectF( xMap.s1(), yMap.s1(),
+        xMap.sDist(), yMap.sDist() );
+}
+
+/*!
+   \brief Calculate the bounding paint rectangle of 2 maps
+
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+
+   \return Bounding paint rectangle of the scale maps, not normalized
+*/
+QRectF QwtPlotItem::paintRect( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap ) const
+{
+    const QRectF rect( xMap.p1(), yMap.p1(),
+        xMap.pDist(), yMap.pDist() );
+
+    return rect;
+}
diff --git a/source/third_party/qwt/qwt_plot_layout.cpp b/source/third_party/qwt/qwt_plot_layout.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d7c4bd982c8977fbcc21c8fcc40c8334296193b
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_layout.cpp
@@ -0,0 +1,1442 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_layout.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_text_label.h"
+#include "qwt/qwt_scale_widget.h"
+#include "qwt/qwt_abstract_legend.h"
+#include <qscrollbar.h>
+#include <qmath.h>
+
+class QwtPlotLayout::LayoutData
+{
+public:
+    void init( const QwtPlot *, const QRectF &rect );
+
+    struct t_legendData
+    {
+        int frameWidth;
+        int hScrollExtent;
+        int vScrollExtent;
+        QSize hint;
+    } legend;
+
+    struct t_titleData
+    {
+        QwtText text;
+        int frameWidth;
+    } title;
+
+    struct t_footerData
+    {
+        QwtText text;
+        int frameWidth;
+    } footer;
+
+    struct t_scaleData
+    {
+        bool isEnabled;
+        const QwtScaleWidget *scaleWidget;
+        QFont scaleFont;
+        int start;
+        int end;
+        int baseLineOffset;
+        double tickOffset;
+        int dimWithoutTitle;
+    } scale[QwtPlot::axisCnt];
+
+    struct t_canvasData
+    {
+        int contentsMargins[ QwtPlot::axisCnt ];
+
+    } canvas;
+};
+
+/*
+  Extract all layout relevant data from the plot components
+*/
+void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect )
+{
+    // legend
+
+    if ( plot->legend() )
+    {
+        legend.frameWidth = plot->legend()->frameWidth();
+        legend.hScrollExtent =
+            plot->legend()->scrollExtent( Qt::Horizontal );
+        legend.vScrollExtent =
+            plot->legend()->scrollExtent( Qt::Vertical );
+
+        const QSize hint = plot->legend()->sizeHint();
+
+        const int w = qMin( hint.width(), qFloor( rect.width() ) );
+
+        int h = plot->legend()->heightForWidth( w );
+        if ( h <= 0 )
+            h = hint.height();
+
+        legend.hint = QSize( w, h );
+    }
+
+    // title
+
+    title.frameWidth = 0;
+    title.text = QwtText();
+
+    if ( plot->titleLabel() )
+    {
+        const QwtTextLabel *label = plot->titleLabel();
+        title.text = label->text();
+        if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
+            title.text.setFont( label->font() );
+
+        title.frameWidth = plot->titleLabel()->frameWidth();
+    }
+
+    // footer
+
+    footer.frameWidth = 0;
+    footer.text = QwtText();
+
+    if ( plot->footerLabel() )
+    {
+        const QwtTextLabel *label = plot->footerLabel();
+        footer.text = label->text();
+        if ( !( footer.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
+            footer.text.setFont( label->font() );
+
+        footer.frameWidth = plot->footerLabel()->frameWidth();
+    }
+
+    // scales
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( plot->axisEnabled( axis ) )
+        {
+            const QwtScaleWidget *scaleWidget = plot->axisWidget( axis );
+
+            scale[axis].isEnabled = true;
+
+            scale[axis].scaleWidget = scaleWidget;
+
+            scale[axis].scaleFont = scaleWidget->font();
+
+            scale[axis].start = scaleWidget->startBorderDist();
+            scale[axis].end = scaleWidget->endBorderDist();
+
+            scale[axis].baseLineOffset = scaleWidget->margin();
+            scale[axis].tickOffset = scaleWidget->margin();
+            if ( scaleWidget->scaleDraw()->hasComponent(
+                QwtAbstractScaleDraw::Ticks ) )
+            {
+                scale[axis].tickOffset +=
+                    scaleWidget->scaleDraw()->maxTickLength();
+            }
+
+            scale[axis].dimWithoutTitle = scaleWidget->dimForLength(
+                QWIDGETSIZE_MAX, scale[axis].scaleFont );
+
+            if ( !scaleWidget->title().isEmpty() )
+            {
+                scale[axis].dimWithoutTitle -=
+                    scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX );
+            }
+        }
+        else
+        {
+            scale[axis].isEnabled = false;
+            scale[axis].start = 0;
+            scale[axis].end = 0;
+            scale[axis].baseLineOffset = 0;
+            scale[axis].tickOffset = 0.0;
+            scale[axis].dimWithoutTitle = 0;
+        }
+    }
+
+    // canvas
+
+    plot->canvas()->getContentsMargins( 
+        &canvas.contentsMargins[ QwtPlot::yLeft ], 
+        &canvas.contentsMargins[ QwtPlot::xTop ],
+        &canvas.contentsMargins[ QwtPlot::yRight ],
+        &canvas.contentsMargins[ QwtPlot::xBottom ] );
+}
+
+class QwtPlotLayout::PrivateData
+{
+public:
+    PrivateData():
+        spacing( 5 )
+    {
+    }
+
+    QRectF titleRect;
+    QRectF footerRect;
+    QRectF legendRect;
+    QRectF scaleRect[QwtPlot::axisCnt];
+    QRectF canvasRect;
+
+    QwtPlotLayout::LayoutData layoutData;
+
+    QwtPlot::LegendPosition legendPos;
+    double legendRatio;
+    unsigned int spacing;
+    unsigned int canvasMargin[QwtPlot::axisCnt];
+    bool alignCanvasToScales[QwtPlot::axisCnt];
+};
+
+/*!
+  \brief Constructor
+ */
+
+QwtPlotLayout::QwtPlotLayout()
+{
+    d_data = new PrivateData;
+
+    setLegendPosition( QwtPlot::BottomLegend );
+    setCanvasMargin( 4 );
+    setAlignCanvasToScales( false );
+
+    invalidate();
+}
+
+//! Destructor
+QwtPlotLayout::~QwtPlotLayout()
+{
+    delete d_data;
+}
+
+/*!
+  Change a margin of the canvas. The margin is the space
+  above/below the scale ticks. A negative margin will
+  be set to -1, excluding the borders of the scales.
+
+  \param margin New margin
+  \param axis One of QwtPlot::Axis. Specifies where the position of the margin.
+              -1 means margin at all borders.
+  \sa canvasMargin()
+
+  \warning The margin will have no effect when alignCanvasToScale() is true
+*/
+
+void QwtPlotLayout::setCanvasMargin( int margin, int axis )
+{
+    if ( margin < -1 )
+        margin = -1;
+
+    if ( axis == -1 )
+    {
+        for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
+            d_data->canvasMargin[axis] = margin;
+    }
+    else if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->canvasMargin[axis] = margin;
+}
+
+/*!
+    \param axisId Axis index
+    \return Margin around the scale tick borders
+    \sa setCanvasMargin()
+*/
+int QwtPlotLayout::canvasMargin( int axisId ) const
+{
+    if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
+        return 0;
+
+    return d_data->canvasMargin[axisId];
+}
+
+/*!
+  \brief Set the align-canvas-to-axis-scales flag for all axes
+
+  \param on True/False
+  \sa setAlignCanvasToScale(), alignCanvasToScale()
+*/
+void QwtPlotLayout::setAlignCanvasToScales( bool on )
+{
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        d_data->alignCanvasToScales[axis] = on;
+}
+
+/*!
+  Change the align-canvas-to-axis-scales setting. The canvas may:
+
+  - extend beyond the axis scale ends to maximize its size,
+  - align with the axis scale ends to control its size.
+
+  The axisId parameter is somehow confusing as it identifies a border
+  of the plot and not the axes, that are aligned. F.e when QwtPlot::yLeft
+  is set, the left end of the the x-axes ( QwtPlot::xTop, QwtPlot::xBottom )
+  is aligned.
+
+  \param axisId Axis index
+  \param on New align-canvas-to-axis-scales setting
+
+  \sa setCanvasMargin(), alignCanvasToScale(), setAlignCanvasToScales()
+  \warning In case of on == true canvasMargin() will have no effect
+*/
+void QwtPlotLayout::setAlignCanvasToScale( int axisId, bool on )
+{
+    if ( axisId >= 0 && axisId < QwtPlot::axisCnt )
+        d_data->alignCanvasToScales[axisId] = on;
+}
+
+/*!
+  Return the align-canvas-to-axis-scales setting. The canvas may:
+  - extend beyond the axis scale ends to maximize its size
+  - align with the axis scale ends to control its size.
+
+  \param axisId Axis index
+  \return align-canvas-to-axis-scales setting
+  \sa setAlignCanvasToScale(), setAlignCanvasToScale(), setCanvasMargin()
+*/
+bool QwtPlotLayout::alignCanvasToScale( int axisId ) const
+{
+    if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
+        return false;
+
+    return d_data->alignCanvasToScales[ axisId ];
+}
+
+/*!
+  Change the spacing of the plot. The spacing is the distance
+  between the plot components.
+
+  \param spacing New spacing
+  \sa setCanvasMargin(), spacing()
+*/
+void QwtPlotLayout::setSpacing( int spacing )
+{
+    d_data->spacing = qMax( 0, spacing );
+}
+
+/*!
+  \return Spacing
+  \sa margin(), setSpacing()
+*/
+int QwtPlotLayout::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+  \brief Specify the position of the legend
+  \param pos The legend's position.
+  \param ratio Ratio between legend and the bounding rectangle
+               of title, footer, canvas and axes. The legend will be shrunk
+               if it would need more space than the given ratio.
+               The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
+               it will be reset to the default ratio.
+               The default vertical/horizontal ratio is 0.33/0.5.
+
+  \sa QwtPlot::setLegendPosition()
+*/
+
+void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos, double ratio )
+{
+    if ( ratio > 1.0 )
+        ratio = 1.0;
+
+    switch ( pos )
+    {
+        case QwtPlot::TopLegend:
+        case QwtPlot::BottomLegend:
+            if ( ratio <= 0.0 )
+                ratio = 0.33;
+            d_data->legendRatio = ratio;
+            d_data->legendPos = pos;
+            break;
+        case QwtPlot::LeftLegend:
+        case QwtPlot::RightLegend:
+            if ( ratio <= 0.0 )
+                ratio = 0.5;
+            d_data->legendRatio = ratio;
+            d_data->legendPos = pos;
+            break;
+        default:
+            break;
+    }
+}
+
+/*!
+  \brief Specify the position of the legend
+  \param pos The legend's position. Valid values are
+      \c QwtPlot::LeftLegend, \c QwtPlot::RightLegend,
+      \c QwtPlot::TopLegend, \c QwtPlot::BottomLegend.
+
+  \sa QwtPlot::setLegendPosition()
+*/
+void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos )
+{
+    setLegendPosition( pos, 0.0 );
+}
+
+/*!
+  \return Position of the legend
+  \sa setLegendPosition(), QwtPlot::setLegendPosition(),
+      QwtPlot::legendPosition()
+*/
+QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const
+{
+    return d_data->legendPos;
+}
+
+/*!
+  Specify the relative size of the legend in the plot
+  \param ratio Ratio between legend and the bounding rectangle
+               of title, footer, canvas and axes. The legend will be shrunk
+               if it would need more space than the given ratio.
+               The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
+               it will be reset to the default ratio.
+               The default vertical/horizontal ratio is 0.33/0.5.
+*/
+void QwtPlotLayout::setLegendRatio( double ratio )
+{
+    setLegendPosition( legendPosition(), ratio );
+}
+
+/*!
+  \return The relative size of the legend in the plot.
+  \sa setLegendPosition()
+*/
+double QwtPlotLayout::legendRatio() const
+{
+    return d_data->legendRatio;
+}
+
+/*!
+  \brief Set the geometry for the title
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \sa titleRect(), activate()
+ */
+void QwtPlotLayout::setTitleRect( const QRectF &rect )
+{
+    d_data->titleRect = rect;
+}
+
+/*!
+  \return Geometry for the title
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::titleRect() const
+{
+    return d_data->titleRect;
+}
+
+/*!
+  \brief Set the geometry for the footer
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \sa footerRect(), activate()
+ */
+void QwtPlotLayout::setFooterRect( const QRectF &rect )
+{
+    d_data->footerRect = rect;
+}
+
+/*!
+  \return Geometry for the footer
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::footerRect() const
+{
+    return d_data->footerRect;
+}
+
+/*!
+  \brief Set the geometry for the legend
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \param rect Rectangle for the legend
+
+  \sa legendRect(), activate()
+ */
+void QwtPlotLayout::setLegendRect( const QRectF &rect )
+{
+    d_data->legendRect = rect;
+}
+
+/*!
+  \return Geometry for the legend
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::legendRect() const
+{
+    return d_data->legendRect;
+}
+
+/*!
+  \brief Set the geometry for an axis
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \param axis Axis index
+  \param rect Rectangle for the scale
+
+  \sa scaleRect(), activate()
+ */
+void QwtPlotLayout::setScaleRect( int axis, const QRectF &rect )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->scaleRect[axis] = rect;
+}
+
+/*!
+  \param axis Axis index
+  \return Geometry for the scale
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::scaleRect( int axis ) const
+{
+    if ( axis < 0 || axis >= QwtPlot::axisCnt )
+    {
+        static QRectF dummyRect;
+        return dummyRect;
+    }
+    return d_data->scaleRect[axis];
+}
+
+/*!
+  \brief Set the geometry for the canvas
+
+  This method is intended to be used from derived layouts
+  overloading activate()
+
+  \sa canvasRect(), activate()
+ */
+void QwtPlotLayout::setCanvasRect( const QRectF &rect )
+{
+    d_data->canvasRect = rect;
+}
+
+/*!
+  \return Geometry for the canvas
+  \sa activate(), invalidate()
+*/
+QRectF QwtPlotLayout::canvasRect() const
+{
+    return d_data->canvasRect;
+}
+
+/*!
+  Invalidate the geometry of all components.
+  \sa activate()
+*/
+void QwtPlotLayout::invalidate()
+{
+    d_data->titleRect = d_data->footerRect
+        = d_data->legendRect = d_data->canvasRect = QRect();
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        d_data->scaleRect[axis] = QRect();
+}
+
+/*!
+  \return Minimum size hint
+  \param plot Plot widget
+
+  \sa QwtPlot::minimumSizeHint()
+*/
+
+QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const
+{
+    class ScaleData
+    {
+    public:
+        ScaleData()
+        {
+            w = h = minLeft = minRight = tickOffset = 0;
+        }
+
+        int w;
+        int h;
+        int minLeft;
+        int minRight;
+        int tickOffset;
+    } scaleData[QwtPlot::axisCnt];
+
+    int canvasBorder[QwtPlot::axisCnt];
+
+    int fw;
+    plot->canvas()->getContentsMargins( &fw, NULL, NULL, NULL );
+
+    int axis;
+    for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( plot->axisEnabled( axis ) )
+        {
+            const QwtScaleWidget *scl = plot->axisWidget( axis );
+            ScaleData &sd = scaleData[axis];
+
+            const QSize hint = scl->minimumSizeHint();
+            sd.w = hint.width();
+            sd.h = hint.height();
+            scl->getBorderDistHint( sd.minLeft, sd.minRight );
+            sd.tickOffset = scl->margin();
+            if ( scl->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) )
+                sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() );
+        }
+
+        canvasBorder[axis] = fw + d_data->canvasMargin[axis] + 1;
+    }
+
+
+    for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        ScaleData &sd = scaleData[axis];
+        if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) )
+        {
+            if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] )
+                && scaleData[QwtPlot::yLeft].w )
+            {
+                int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft];
+                if ( shiftLeft > scaleData[QwtPlot::yLeft].w )
+                    shiftLeft = scaleData[QwtPlot::yLeft].w;
+
+                sd.w -= shiftLeft;
+            }
+            if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] )
+                && scaleData[QwtPlot::yRight].w )
+            {
+                int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight];
+                if ( shiftRight > scaleData[QwtPlot::yRight].w )
+                    shiftRight = scaleData[QwtPlot::yRight].w;
+
+                sd.w -= shiftRight;
+            }
+        }
+
+        if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) )
+        {
+            if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) &&
+                scaleData[QwtPlot::xBottom].h )
+            {
+                int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom];
+                if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset )
+                    shiftBottom = scaleData[QwtPlot::xBottom].tickOffset;
+
+                sd.h -= shiftBottom;
+            }
+            if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) &&
+                scaleData[QwtPlot::xTop].h )
+            {
+                int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop];
+                if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset )
+                    shiftTop = scaleData[QwtPlot::xTop].tickOffset;
+
+                sd.h -= shiftTop;
+            }
+        }
+    }
+
+    const QWidget *canvas = plot->canvas();
+
+    int left, top, right, bottom;
+    canvas->getContentsMargins( &left, &top, &right, &bottom );
+
+    const QSize minCanvasSize = canvas->minimumSize();
+
+    int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w;
+    int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w )
+        + left + 1 + right + 1;
+    w += qMax( cw, minCanvasSize.width() );
+
+    int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h;
+    int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h )
+        + top + 1 + bottom + 1;
+    h += qMax( ch, minCanvasSize.height() );
+
+    const QwtTextLabel *labels[2];
+    labels[0] = plot->titleLabel();
+    labels[1] = plot->footerLabel();
+
+    for ( int i = 0; i < 2; i++ )
+    {
+        const QwtTextLabel *label   = labels[i];
+        if ( label && !label->text().isEmpty() )
+        {
+            // If only QwtPlot::yLeft or QwtPlot::yRight is showing,
+            // we center on the plot canvas.
+            const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft )
+                && plot->axisEnabled( QwtPlot::yRight ) );
+
+            int labelW = w;
+            if ( centerOnCanvas )
+            {
+                labelW -= scaleData[QwtPlot::yLeft].w
+                    + scaleData[QwtPlot::yRight].w;
+            }
+
+            int labelH = label->heightForWidth( labelW );
+            if ( labelH > labelW ) // Compensate for a long title
+            {
+                w = labelW = labelH;
+                if ( centerOnCanvas )
+                {
+                    w += scaleData[QwtPlot::yLeft].w
+                        + scaleData[QwtPlot::yRight].w;
+                }
+
+                labelH = label->heightForWidth( labelW );
+            }
+            h += labelH + d_data->spacing;
+        }
+    }
+
+    // Compute the legend contribution
+
+    const QwtAbstractLegend *legend = plot->legend();
+    if ( legend && !legend->isEmpty() )
+    {
+        if ( d_data->legendPos == QwtPlot::LeftLegend
+            || d_data->legendPos == QwtPlot::RightLegend )
+        {
+            int legendW = legend->sizeHint().width();
+            int legendH = legend->heightForWidth( legendW );
+
+            if ( legend->frameWidth() > 0 )
+                w += d_data->spacing;
+
+            if ( legendH > h )
+                legendW += legend->scrollExtent( Qt::Horizontal );
+
+            if ( d_data->legendRatio < 1.0 )
+                legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) );
+
+            w += legendW + d_data->spacing;
+        }
+        else // QwtPlot::Top, QwtPlot::Bottom
+        {
+            int legendW = qMin( legend->sizeHint().width(), w );
+            int legendH = legend->heightForWidth( legendW );
+
+            if ( legend->frameWidth() > 0 )
+                h += d_data->spacing;
+
+            if ( d_data->legendRatio < 1.0 )
+                legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) );
+
+            h += legendH + d_data->spacing;
+        }
+    }
+
+    return QSize( w, h );
+}
+
+/*!
+  Find the geometry for the legend
+
+  \param options Options how to layout the legend
+  \param rect Rectangle where to place the legend
+
+  \return Geometry for the legend
+  \sa Options
+*/
+
+QRectF QwtPlotLayout::layoutLegend( Options options,
+    const QRectF &rect ) const
+{
+    const QSize hint( d_data->layoutData.legend.hint );
+
+    int dim;
+    if ( d_data->legendPos == QwtPlot::LeftLegend
+        || d_data->legendPos == QwtPlot::RightLegend )
+    {
+        // We don't allow vertical legends to take more than
+        // half of the available space.
+
+        dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) );
+
+        if ( !( options & IgnoreScrollbars ) )
+        {
+            if ( hint.height() > rect.height() )
+            {
+                // The legend will need additional
+                // space for the vertical scrollbar.
+
+                dim += d_data->layoutData.legend.hScrollExtent;
+            }
+        }
+    }
+    else
+    {
+        dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) );
+        dim = qMax( dim, d_data->layoutData.legend.vScrollExtent );
+    }
+
+    QRectF legendRect = rect;
+    switch ( d_data->legendPos )
+    {
+        case QwtPlot::LeftLegend:
+            legendRect.setWidth( dim );
+            break;
+        case QwtPlot::RightLegend:
+            legendRect.setX( rect.right() - dim );
+            legendRect.setWidth( dim );
+            break;
+        case QwtPlot::TopLegend:
+            legendRect.setHeight( dim );
+            break;
+        case QwtPlot::BottomLegend:
+            legendRect.setY( rect.bottom() - dim );
+            legendRect.setHeight( dim );
+            break;
+    }
+
+    return legendRect;
+}
+
+/*!
+  Align the legend to the canvas
+
+  \param canvasRect Geometry of the canvas
+  \param legendRect Maximum geometry for the legend
+
+  \return Geometry for the aligned legend
+*/
+QRectF QwtPlotLayout::alignLegend( const QRectF &canvasRect,
+    const QRectF &legendRect ) const
+{
+    QRectF alignedRect = legendRect;
+
+    if ( d_data->legendPos == QwtPlot::BottomLegend
+        || d_data->legendPos == QwtPlot::TopLegend )
+    {
+        if ( d_data->layoutData.legend.hint.width() < canvasRect.width() )
+        {
+            alignedRect.setX( canvasRect.x() );
+            alignedRect.setWidth( canvasRect.width() );
+        }
+    }
+    else
+    {
+        if ( d_data->layoutData.legend.hint.height() < canvasRect.height() )
+        {
+            alignedRect.setY( canvasRect.y() );
+            alignedRect.setHeight( canvasRect.height() );
+        }
+    }
+
+    return alignedRect;
+}
+
+/*!
+  Expand all line breaks in text labels, and calculate the height
+  of their widgets in orientation of the text.
+
+  \param options Options how to layout the legend
+  \param rect Bounding rectangle for title, footer, axes and canvas.
+  \param dimTitle Expanded height of the title widget
+  \param dimFooter Expanded height of the footer widget
+  \param dimAxis Expanded heights of the axis in axis orientation.
+
+  \sa Options
+*/
+void QwtPlotLayout::expandLineBreaks( Options options, const QRectF &rect,
+    int &dimTitle, int &dimFooter, int dimAxis[QwtPlot::axisCnt] ) const
+{
+    dimTitle = dimFooter = 0;
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        dimAxis[axis] = 0;
+
+    int backboneOffset[QwtPlot::axisCnt];
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        backboneOffset[axis] = 0;
+        if ( !( options & IgnoreFrames ) )
+            backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[ axis ];
+
+        if ( !d_data->alignCanvasToScales[axis] )
+            backboneOffset[axis] += d_data->canvasMargin[axis];
+    }
+
+    bool done = false;
+    while ( !done )
+    {
+        done = true;
+
+        // the size for the 4 axis depend on each other. Expanding
+        // the height of a horizontal axis will shrink the height
+        // for the vertical axis, shrinking the height of a vertical
+        // axis will result in a line break what will expand the
+        // width and results in shrinking the width of a horizontal
+        // axis what might result in a line break of a horizontal
+        // axis ... . So we loop as long until no size changes.
+
+        if ( !( ( options & IgnoreTitle ) ||
+            d_data->layoutData.title.text.isEmpty() ) )
+        {
+            double w = rect.width();
+
+            if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
+                != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
+            {
+                // center to the canvas
+                w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
+            }
+
+            int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) );
+            if ( !( options & IgnoreFrames ) )
+                d += 2 * d_data->layoutData.title.frameWidth;
+
+            if ( d > dimTitle )
+            {
+                dimTitle = d;
+                done = false;
+            }
+        }
+
+        if ( !( ( options & IgnoreFooter ) ||
+            d_data->layoutData.footer.text.isEmpty() ) )
+        {
+            double w = rect.width();
+
+            if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled
+                != d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
+            {
+                // center to the canvas
+                w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
+            }
+
+            int d = qCeil( d_data->layoutData.footer.text.heightForWidth( w ) );
+            if ( !( options & IgnoreFrames ) )
+                d += 2 * d_data->layoutData.footer.frameWidth;
+
+            if ( d > dimFooter )
+            {
+                dimFooter = d;
+                done = false;
+            }
+        }
+
+        for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        {
+            const struct LayoutData::t_scaleData &scaleData =
+                d_data->layoutData.scale[axis];
+
+            if ( scaleData.isEnabled )
+            {
+                double length;
+                if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
+                {
+                    length = rect.width() - dimAxis[QwtPlot::yLeft]
+                        - dimAxis[QwtPlot::yRight];
+                    length -= scaleData.start + scaleData.end;
+
+                    if ( dimAxis[QwtPlot::yRight] > 0 )
+                        length -= 1;
+
+                    length += qMin( dimAxis[QwtPlot::yLeft],
+                        scaleData.start - backboneOffset[QwtPlot::yLeft] );
+                    length += qMin( dimAxis[QwtPlot::yRight],
+                        scaleData.end - backboneOffset[QwtPlot::yRight] );
+                }
+                else // QwtPlot::yLeft, QwtPlot::yRight
+                {
+                    length = rect.height() - dimAxis[QwtPlot::xTop]
+                        - dimAxis[QwtPlot::xBottom];
+                    length -= scaleData.start + scaleData.end;
+                    length -= 1;
+
+                    if ( dimAxis[QwtPlot::xBottom] <= 0 )
+                        length -= 1;
+                    if ( dimAxis[QwtPlot::xTop] <= 0 )
+                        length -= 1;
+
+                    if ( dimAxis[QwtPlot::xBottom] > 0 )
+                    {
+                        length += qMin(
+                            d_data->layoutData.scale[QwtPlot::xBottom].tickOffset,
+                            double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) );
+                    }
+                    if ( dimAxis[QwtPlot::xTop] > 0 )
+                    {
+                        length += qMin(
+                            d_data->layoutData.scale[QwtPlot::xTop].tickOffset,
+                            double( scaleData.end - backboneOffset[QwtPlot::xTop] ) );
+                    }
+
+                    if ( dimTitle > 0 )
+                        length -= dimTitle + d_data->spacing;
+                }
+
+                int d = scaleData.dimWithoutTitle;
+                if ( !scaleData.scaleWidget->title().isEmpty() )
+                {
+                    d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) );
+                }
+
+
+                if ( d > dimAxis[axis] )
+                {
+                    dimAxis[axis] = d;
+                    done = false;
+                }
+            }
+        }
+    }
+}
+
+/*!
+  Align the ticks of the axis to the canvas borders using
+  the empty corners.
+
+  \param options Layout options
+  \param canvasRect Geometry of the canvas ( IN/OUT )
+  \param scaleRect Geometries of the scales ( IN/OUT )
+
+  \sa Options
+*/
+
+void QwtPlotLayout::alignScales( Options options,
+    QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const
+{
+    int backboneOffset[QwtPlot::axisCnt];
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        backboneOffset[axis] = 0;
+
+        if ( !d_data->alignCanvasToScales[axis] )
+        {
+            backboneOffset[axis] += d_data->canvasMargin[axis];
+        }
+
+        if ( !( options & IgnoreFrames ) )
+        {
+            backboneOffset[axis] += 
+                d_data->layoutData.canvas.contentsMargins[axis];
+        }
+    }
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( !scaleRect[axis].isValid() )
+            continue;
+
+        const int startDist = d_data->layoutData.scale[axis].start;
+        const int endDist = d_data->layoutData.scale[axis].end;
+
+        QRectF &axisRect = scaleRect[axis];
+
+        if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
+        {
+            const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft];
+            const int leftOffset =
+                backboneOffset[QwtPlot::yLeft] - startDist;
+
+            if ( leftScaleRect.isValid() )
+            {
+                const double dx = leftOffset + leftScaleRect.width();
+                if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && dx < 0.0 )
+                {
+                    /*
+                      The axis needs more space than the width
+                      of the left scale.
+                     */
+                    const double cLeft = canvasRect.left(); // qreal -> double
+                    canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) );
+                }
+                else
+                {
+                    const double minLeft = leftScaleRect.left();
+                    const double left = axisRect.left() + leftOffset;
+                    axisRect.setLeft( qMax( left, minLeft ) );
+                }
+            }
+            else
+            {
+                if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && leftOffset < 0 )
+                {
+                    canvasRect.setLeft( qMax( canvasRect.left(),
+                        axisRect.left() - leftOffset ) );
+                }
+                else
+                {
+                    if ( leftOffset > 0 )
+                        axisRect.setLeft( axisRect.left() + leftOffset );
+                }
+            }
+
+            const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight];
+            const int rightOffset =
+                backboneOffset[QwtPlot::yRight] - endDist + 1;
+
+            if ( rightScaleRect.isValid() )
+            {
+                const double dx = rightOffset + rightScaleRect.width();
+                if ( d_data->alignCanvasToScales[QwtPlot::yRight] && dx < 0 )
+                {
+                    /*
+                      The axis needs more space than the width
+                      of the right scale.
+                     */
+                    const double cRight = canvasRect.right(); // qreal -> double
+                    canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) );
+                }   
+
+                const double maxRight = rightScaleRect.right();
+                const double right = axisRect.right() - rightOffset;
+                axisRect.setRight( qMin( right, maxRight ) );
+            }
+            else
+            {
+                if ( d_data->alignCanvasToScales[QwtPlot::yRight] && rightOffset < 0 )
+                {
+                    canvasRect.setRight( qMin( canvasRect.right(),
+                        axisRect.right() + rightOffset ) );
+                }
+                else
+                {
+                    if ( rightOffset > 0 )
+                        axisRect.setRight( axisRect.right() - rightOffset );
+                }
+            }
+        }
+        else // QwtPlot::yLeft, QwtPlot::yRight
+        {
+            const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom];
+            const int bottomOffset =
+                backboneOffset[QwtPlot::xBottom] - endDist + 1;
+
+            if ( bottomScaleRect.isValid() )
+            {
+                const double dy = bottomOffset + bottomScaleRect.height();
+                if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && dy < 0 )
+                {
+                    /*
+                      The axis needs more space than the height
+                      of the bottom scale.
+                     */
+                    const double cBottom = canvasRect.bottom(); // qreal -> double
+                    canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) );
+                }
+                else
+                {
+                    const double maxBottom = bottomScaleRect.top() +
+                        d_data->layoutData.scale[QwtPlot::xBottom].tickOffset;
+                    const double bottom = axisRect.bottom() - bottomOffset;
+                    axisRect.setBottom( qMin( bottom, maxBottom ) );
+                }
+            }
+            else
+            {
+                if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && bottomOffset < 0 )
+                {
+                    canvasRect.setBottom( qMin( canvasRect.bottom(),
+                        axisRect.bottom() + bottomOffset ) );
+                }
+                else
+                {
+                    if ( bottomOffset > 0 )
+                        axisRect.setBottom( axisRect.bottom() - bottomOffset );
+                }
+            }
+
+            const QRectF &topScaleRect = scaleRect[QwtPlot::xTop];
+            const int topOffset = backboneOffset[QwtPlot::xTop] - startDist;
+
+            if ( topScaleRect.isValid() )
+            {
+                const double dy = topOffset + topScaleRect.height();
+                if ( d_data->alignCanvasToScales[QwtPlot::xTop] && dy < 0 )
+                {
+                    /*
+                      The axis needs more space than the height
+                      of the top scale.
+                     */
+                    const double cTop = canvasRect.top(); // qreal -> double
+                    canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) );
+                }
+                else
+                {
+                    const double minTop = topScaleRect.bottom() -
+                        d_data->layoutData.scale[QwtPlot::xTop].tickOffset;
+                    const double top = axisRect.top() + topOffset;
+                    axisRect.setTop( qMax( top, minTop ) );
+                }
+            }
+            else
+            {
+                if ( d_data->alignCanvasToScales[QwtPlot::xTop] && topOffset < 0 )
+                {
+                    canvasRect.setTop( qMax( canvasRect.top(),
+                        axisRect.top() - topOffset ) );
+                }
+                else
+                {
+                    if ( topOffset > 0 )
+                        axisRect.setTop( axisRect.top() + topOffset );
+                }
+            }
+        }
+    }
+
+    /*
+      The canvas has been aligned to the scale with largest
+      border distances. Now we have to realign the other scale.
+     */
+
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        QRectF &sRect = scaleRect[axis];
+
+        if ( !sRect.isValid() )
+            continue;
+
+        if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
+        {
+            if ( d_data->alignCanvasToScales[QwtPlot::yLeft] )
+            {
+                double y = canvasRect.left() - d_data->layoutData.scale[axis].start;
+                if ( !( options & IgnoreFrames ) )
+                    y += d_data->layoutData.canvas.contentsMargins[ QwtPlot::yLeft ];
+
+                sRect.setLeft( y );
+            }
+            if ( d_data->alignCanvasToScales[QwtPlot::yRight] )
+            {
+                double y = canvasRect.right() - 1 + d_data->layoutData.scale[axis].end;
+                if ( !( options & IgnoreFrames ) )
+                    y -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::yRight ];
+
+                sRect.setRight( y );
+            }
+
+            if ( d_data->alignCanvasToScales[ axis ] )
+            {
+                if ( axis == QwtPlot::xTop )
+                    sRect.setBottom( canvasRect.top() );
+                else
+                    sRect.setTop( canvasRect.bottom() );
+            }
+        }
+        else
+        {
+            if ( d_data->alignCanvasToScales[QwtPlot::xTop] )
+            {
+                double x = canvasRect.top() - d_data->layoutData.scale[axis].start;
+                if ( !( options & IgnoreFrames ) )
+                    x += d_data->layoutData.canvas.contentsMargins[ QwtPlot::xTop ];
+
+                sRect.setTop( x );
+            }
+            if ( d_data->alignCanvasToScales[QwtPlot::xBottom] )
+            {
+                double x = canvasRect.bottom() - 1 + d_data->layoutData.scale[axis].end;
+                if ( !( options & IgnoreFrames ) )
+                    x -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::xBottom ];
+
+                sRect.setBottom( x );
+            }
+
+            if ( d_data->alignCanvasToScales[ axis ] )
+            {
+                if ( axis == QwtPlot::yLeft )
+                    sRect.setRight( canvasRect.left() );
+                else
+                    sRect.setLeft( canvasRect.right() );
+            }
+        }
+    }
+}
+
+/*!
+  \brief Recalculate the geometry of all components.
+
+  \param plot Plot to be layout
+  \param plotRect Rectangle where to place the components
+  \param options Layout options
+
+  \sa invalidate(), titleRect(), footerRect()
+      legendRect(), scaleRect(), canvasRect()
+*/
+void QwtPlotLayout::activate( const QwtPlot *plot,
+    const QRectF &plotRect, Options options )
+{
+    invalidate();
+
+    QRectF rect( plotRect );  // undistributed rest of the plot rect
+
+    // We extract all layout relevant parameters from the widgets,
+    // and save them to d_data->layoutData.
+
+    d_data->layoutData.init( plot, rect );
+
+    if ( !( options & IgnoreLegend )
+        && plot->legend() && !plot->legend()->isEmpty() )
+    {
+        d_data->legendRect = layoutLegend( options, rect );
+
+        // subtract d_data->legendRect from rect
+
+        const QRegion region( rect.toRect() );
+        rect = region.subtracted( d_data->legendRect.toRect() ).boundingRect();
+
+        switch ( d_data->legendPos )
+        {
+            case QwtPlot::LeftLegend:
+                rect.setLeft( rect.left() + d_data->spacing );
+                break;
+            case QwtPlot::RightLegend:
+                rect.setRight( rect.right() - d_data->spacing );
+                break;
+            case QwtPlot::TopLegend:
+                rect.setTop( rect.top() + d_data->spacing );
+                break;
+            case QwtPlot::BottomLegend:
+                rect.setBottom( rect.bottom() - d_data->spacing );
+                break;
+        }
+    }
+
+    /*
+     +---+-----------+---+
+     |       Title       |
+     +---+-----------+---+
+     |   |   Axis    |   |
+     +---+-----------+---+
+     | A |           | A |
+     | x |  Canvas   | x |
+     | i |           | i |
+     | s |           | s |
+     +---+-----------+---+
+     |   |   Axis    |   |
+     +---+-----------+---+
+     |      Footer       |
+     +---+-----------+---+
+    */
+
+    // title, footer and axes include text labels. The height of each
+    // label depends on its line breaks, that depend on the width
+    // for the label. A line break in a horizontal text will reduce
+    // the available width for vertical texts and vice versa.
+    // expandLineBreaks finds the height/width for title, footer and axes
+    // including all line breaks.
+
+    int dimTitle, dimFooter, dimAxes[QwtPlot::axisCnt];
+    expandLineBreaks( options, rect, dimTitle, dimFooter, dimAxes );
+
+    if ( dimTitle > 0 )
+    {
+        d_data->titleRect.setRect(
+            rect.left(), rect.top(), rect.width(), dimTitle );
+
+        rect.setTop( d_data->titleRect.bottom() + d_data->spacing );
+
+        if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
+            d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
+        {
+            // if only one of the y axes is missing we align
+            // the title centered to the canvas
+
+            d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
+            d_data->titleRect.setWidth( rect.width()
+                - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
+        }
+    }
+
+    if ( dimFooter > 0 )
+    {
+        d_data->footerRect.setRect(
+            rect.left(), rect.bottom() - dimFooter, rect.width(), dimFooter );
+
+        rect.setBottom( d_data->footerRect.top() - d_data->spacing );
+
+        if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled !=
+            d_data->layoutData.scale[QwtPlot::yRight].isEnabled )
+        {
+            // if only one of the y axes is missing we align
+            // the footer centered to the canvas
+
+            d_data->footerRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
+            d_data->footerRect.setWidth( rect.width()
+                - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
+        }
+    }
+
+    d_data->canvasRect.setRect(
+        rect.x() + dimAxes[QwtPlot::yLeft],
+        rect.y() + dimAxes[QwtPlot::xTop],
+        rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft],
+        rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] );
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        // set the rects for the axes
+
+        if ( dimAxes[axis] )
+        {
+            int dim = dimAxes[axis];
+            QRectF &scaleRect = d_data->scaleRect[axis];
+
+            scaleRect = d_data->canvasRect;
+            switch ( axis )
+            {
+                case QwtPlot::yLeft:
+                    scaleRect.setX( d_data->canvasRect.left() - dim );
+                    scaleRect.setWidth( dim );
+                    break;
+                case QwtPlot::yRight:
+                    scaleRect.setX( d_data->canvasRect.right() );
+                    scaleRect.setWidth( dim );
+                    break;
+                case QwtPlot::xBottom:
+                    scaleRect.setY( d_data->canvasRect.bottom() );
+                    scaleRect.setHeight( dim );
+                    break;
+                case QwtPlot::xTop:
+                    scaleRect.setY( d_data->canvasRect.top() - dim );
+                    scaleRect.setHeight( dim );
+                    break;
+            }
+            scaleRect = scaleRect.normalized();
+        }
+    }
+
+    // +---+-----------+---+
+    // |  <-   Axis   ->   |
+    // +-^-+-----------+-^-+
+    // | | |           | | |
+    // |   |           |   |
+    // | A |           | A |
+    // | x |  Canvas   | x |
+    // | i |           | i |
+    // | s |           | s |
+    // |   |           |   |
+    // | | |           | | |
+    // +-V-+-----------+-V-+
+    // |   <-  Axis   ->   |
+    // +---+-----------+---+
+
+    // The ticks of the axes - not the labels above - should
+    // be aligned to the canvas. So we try to use the empty
+    // corners to extend the axes, so that the label texts
+    // left/right of the min/max ticks are moved into them.
+
+    alignScales( options, d_data->canvasRect, d_data->scaleRect );
+
+    if ( !d_data->legendRect.isEmpty() )
+    {
+        // We prefer to align the legend to the canvas - not to
+        // the complete plot - if possible.
+
+        d_data->legendRect = alignLegend( d_data->canvasRect, d_data->legendRect );
+    }
+}
diff --git a/source/third_party/qwt/qwt_plot_legenditem.cpp b/source/third_party/qwt/qwt_plot_legenditem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..633c0661d6e30f2bace54fc51398ab5e00c0ddbd
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_legenditem.cpp
@@ -0,0 +1,872 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_legenditem.h"
+#include "qwt/qwt_dyngrid_layout.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_painter.h"
+#include <qlayoutitem.h>
+#include <qpen.h>
+#include <qbrush.h>
+#include <qpainter.h>
+#include <qmath.h>
+
+class QwtLegendLayoutItem: public QLayoutItem
+{
+public:
+    QwtLegendLayoutItem( const QwtPlotLegendItem *, const QwtPlotItem * );
+    virtual ~QwtLegendLayoutItem();
+
+    const QwtPlotItem *plotItem() const;
+
+    void setData( const QwtLegendData & );
+    const QwtLegendData &data() const;
+
+    virtual Qt::Orientations expandingDirections() const;
+    virtual QRect geometry() const;
+    virtual bool hasHeightForWidth() const;
+    virtual int heightForWidth( int w ) const;
+    virtual bool isEmpty() const;
+    virtual QSize maximumSize() const;
+    virtual int minimumHeightForWidth( int w ) const;
+    virtual QSize minimumSize() const;
+    virtual void setGeometry( const QRect & r );
+    virtual QSize sizeHint() const;
+
+private:
+
+    const QwtPlotLegendItem *d_legendItem;
+    const QwtPlotItem *d_plotItem;
+    QwtLegendData d_data;
+
+    QRect d_rect;
+};
+
+QwtLegendLayoutItem::QwtLegendLayoutItem( 
+        const QwtPlotLegendItem *legendItem, const QwtPlotItem *plotItem ):
+    d_legendItem( legendItem ),
+    d_plotItem( plotItem)
+{
+}
+
+QwtLegendLayoutItem::~QwtLegendLayoutItem()
+{
+}
+
+const QwtPlotItem *QwtLegendLayoutItem::plotItem() const
+{
+    return d_plotItem;
+}
+
+void QwtLegendLayoutItem::setData( const QwtLegendData &data )
+{
+    d_data = data;
+}
+
+const QwtLegendData &QwtLegendLayoutItem::data() const
+{
+    return d_data;
+}
+
+Qt::Orientations QwtLegendLayoutItem::expandingDirections() const
+{
+    return Qt::Horizontal;
+}
+
+bool QwtLegendLayoutItem::hasHeightForWidth() const
+{
+    return !d_data.title().isEmpty();
+}
+
+int QwtLegendLayoutItem::minimumHeightForWidth( int w ) const
+{
+    return d_legendItem->heightForWidth( d_data, w );
+}
+
+int QwtLegendLayoutItem::heightForWidth( int w ) const
+{
+    return d_legendItem->heightForWidth( d_data, w );
+}
+
+bool QwtLegendLayoutItem::isEmpty() const
+{
+    return false;
+}
+
+QSize QwtLegendLayoutItem::maximumSize() const
+{
+    return QSize( QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX );
+}
+
+QSize QwtLegendLayoutItem::minimumSize() const
+{
+    return d_legendItem->minimumSize( d_data );
+}
+
+QSize QwtLegendLayoutItem::sizeHint() const
+{
+    return minimumSize();
+}
+
+void QwtLegendLayoutItem::setGeometry( const QRect &rect )
+{
+    d_rect = rect;
+}
+
+QRect QwtLegendLayoutItem::geometry() const
+{
+    return d_rect;
+}
+
+class QwtPlotLegendItem::PrivateData
+{
+public:
+    PrivateData():
+        itemMargin( 4 ),
+        itemSpacing( 4 ),
+        borderRadius( 0.0 ),
+        borderPen( Qt::NoPen ),
+        backgroundBrush( Qt::NoBrush ),
+        backgroundMode( QwtPlotLegendItem::LegendBackground ),
+        borderDistance( 10 ),
+        alignment( Qt::AlignRight | Qt::AlignBottom )
+    {
+        layout = new QwtDynGridLayout();
+        layout->setMaxColumns( 2 );
+
+        layout->setSpacing( 0 );
+        layout->setContentsMargins( 0, 0, 0, 0 );
+    }
+
+    ~PrivateData()
+    {
+        delete layout;
+    }
+
+    QFont font;
+    QPen textPen;
+    int itemMargin;
+    int itemSpacing;
+
+    double borderRadius;
+    QPen borderPen;
+    QBrush backgroundBrush;
+    QwtPlotLegendItem::BackgroundMode backgroundMode;
+
+    int borderDistance;
+    Qt::Alignment alignment;
+
+    QMap< const QwtPlotItem *, QList<QwtLegendLayoutItem *> > map;
+    QwtDynGridLayout *layout;
+};
+
+//! Constructor 
+QwtPlotLegendItem::QwtPlotLegendItem():
+    QwtPlotItem( QwtText( "Legend" ) )
+{
+    d_data = new PrivateData;
+
+    setItemInterest( QwtPlotItem::LegendInterest, true );
+    setZ( 100.0 );
+}
+
+//! Destructor
+QwtPlotLegendItem::~QwtPlotLegendItem()
+{
+    clearLegend();
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotLegend
+int QwtPlotLegendItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotLegend;
+}
+
+/*!
+  \brief Set the alignmnet
+
+  Alignment means the position of the legend relative
+  to the geometry of the plot canvas. 
+
+  \param alignment Alignment flags
+
+  \sa alignment(), setMaxColumns()
+
+  \note To align a legend with many items horizontally 
+        the number of columns need to be limited
+ */
+void QwtPlotLegendItem::setAlignment( Qt::Alignment alignment )
+{
+    if ( d_data->alignment != alignment )
+    {
+        d_data->alignment = alignment;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Alignment flags
+  \sa setAlignment()
+ */
+Qt::Alignment QwtPlotLegendItem::alignment() const
+{
+    return d_data->alignment;
+}
+
+/*!
+  \brief Limit the number of columns
+
+  When aligning the legend horizontally ( Qt::AlignLeft, Qt::AlignRight )
+  the number of columns needs to be limited to avoid, that
+  the width of the legend grows with an increasing number of entries.
+
+  \param maxColumns Maximum number of columns. 0 means unlimited.
+  \sa maxColumns(), QwtDynGridLayout::setMaxColumns()
+ */
+void QwtPlotLegendItem::setMaxColumns( uint maxColumns )
+{
+    if ( maxColumns != d_data->layout->maxColumns() )
+    {
+        d_data->layout->setMaxColumns( maxColumns );
+        itemChanged();
+    }
+}
+
+/*!
+  \return Maximum number of columns
+  \sa maxColumns(), QwtDynGridLayout::maxColumns()
+ */
+uint QwtPlotLegendItem::maxColumns() const
+{
+    return d_data->layout->maxColumns();
+}
+
+/*!
+  \brief Set the margin around legend items
+
+  The default setting for the margin is 0.
+
+  \param margin Margin in pixels
+  \sa margin(), setSpacing(), setItemMargin(), setItemSpacing
+ */
+void QwtPlotLegendItem::setMargin( int margin )
+{
+    margin = qMax( margin, 0 );
+    if ( margin != this->margin() )
+    {
+        d_data->layout->setContentsMargins( 
+            margin, margin, margin, margin );
+
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin around the legend items
+  \sa setMargin(), spacing(), itemMargin(), itemSpacing()
+ */
+int QwtPlotLegendItem::margin() const
+{
+    int left;
+    d_data->layout->getContentsMargins( &left, NULL, NULL, NULL );
+
+    return left;
+}
+
+/*!
+  \brief Set the spacing between the legend items
+
+  \param spacing Spacing in pixels
+  \sa spacing(), setMargin()
+*/
+void QwtPlotLegendItem::setSpacing( int spacing )
+{
+    spacing = qMax( spacing, 0 );
+    if ( spacing != d_data->layout->spacing() )
+    {
+        d_data->layout->setSpacing( spacing );
+        itemChanged();
+    }
+}
+
+/*!
+  \return Spacing between the legend items
+  \sa setSpacing(), margin(), itemSpacing(), itemMargin()
+ */
+int QwtPlotLegendItem::spacing() const
+{
+    return d_data->layout->spacing();
+}
+
+/*!
+  Set the margin around each item
+
+  \param margin Margin
+  \sa itemMargin(), setItemSpacing(), setMargin(), setSpacing()
+ */
+void QwtPlotLegendItem::setItemMargin( int margin )
+{
+    margin = qMax( margin, 0 );
+    if ( margin != d_data->itemMargin )
+    {
+        d_data->itemMargin = margin;
+
+        d_data->layout->invalidate();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin around each item
+  \sa setItemMargin(), itemSpacing(), margin(), spacing()
+*/
+int QwtPlotLegendItem::itemMargin() const
+{
+    return d_data->itemMargin;
+}
+
+/*!
+  Set the spacing inside of each item
+
+  \param spacing Spacing
+  \sa itemSpacing(), setItemMargin(), setMargin(), setSpacing()
+ */
+void QwtPlotLegendItem::setItemSpacing( int spacing )
+{
+    spacing = qMax( spacing, 0 );
+    if ( spacing != d_data->itemSpacing )
+    {
+        d_data->itemSpacing = spacing;
+
+        d_data->layout->invalidate();
+        itemChanged();
+    }
+
+}
+
+/*!
+  \return Spacing inside of each item
+  \sa setItemSpacing(), itemMargin(), margin(), spacing()
+*/
+int QwtPlotLegendItem::itemSpacing() const
+{
+    return d_data->itemSpacing;
+}
+
+/*!
+   Change the font used for drawing the text label
+
+   \param font Legend font
+   \sa font()
+*/
+void QwtPlotLegendItem::setFont( const QFont &font )
+{
+    if ( font != d_data->font )
+    {
+        d_data->font = font;
+
+        d_data->layout->invalidate();
+        itemChanged();
+    }
+}
+
+/*!
+   \return Font used for drawing the text label
+   \sa setFont()
+*/
+QFont QwtPlotLegendItem::font() const
+{
+    return d_data->font;
+}
+
+/*!
+  \brief Set the margin between the legend and the canvas border
+
+  The default setting for the margin is 10 pixels.
+
+  \param distance Margin in pixels
+  \sa setMargin()
+ */
+void QwtPlotLegendItem::setBorderDistance( int distance )
+{
+    if ( distance < 0 )
+        distance = -1;
+
+    if ( distance != d_data->borderDistance )
+    {
+        d_data->borderDistance = distance;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin between the legend and the canvas border
+  \sa margin()
+ */
+int QwtPlotLegendItem::borderDistance() const
+{
+    return d_data->borderDistance;
+}
+
+/*!
+  Set the radius for the border
+  
+  \param radius A value <= 0 defines a rectangular border
+  \sa borderRadius(), setBorderPen()
+ */
+void QwtPlotLegendItem::setBorderRadius( double radius )
+{
+    radius = qMax( 0.0, radius );
+
+    if ( radius != d_data->borderRadius )
+    {
+        d_data->borderRadius = radius;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Radius of the border
+  \sa setBorderRadius(), setBorderPen()
+ */
+double QwtPlotLegendItem::borderRadius() const
+{
+    return d_data->borderRadius;
+}
+
+/*!
+  Set the pen for drawing the border
+
+  \param pen Border pen
+  \sa borderPen(), setBackgroundBrush()
+ */
+void QwtPlotLegendItem::setBorderPen( const QPen &pen )
+{
+    if ( d_data->borderPen != pen )
+    {
+        d_data->borderPen = pen;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen for drawing the border
+  \sa setBorderPen(), backgroundBrush()
+ */
+QPen QwtPlotLegendItem::borderPen() const
+{
+    return d_data->borderPen;
+}
+
+/*!
+  \brief Set the background brush
+
+  The brush is used to fill the background
+
+  \param brush Brush
+  \sa backgroundBrush(), setBackgroundMode(), drawBackground()
+ */
+void QwtPlotLegendItem::setBackgroundBrush( const QBrush &brush )
+{
+    if ( d_data->backgroundBrush != brush )
+    {
+        d_data->backgroundBrush = brush;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush is used to fill the background
+  \sa setBackgroundBrush(), backgroundMode(), drawBackground()
+ */
+QBrush QwtPlotLegendItem::backgroundBrush() const
+{
+    return d_data->backgroundBrush;
+}
+
+/*!
+  \brief Set the background mode
+
+  Depending on the mode the complete legend or each item 
+  might have an background.
+
+  The default setting is LegendBackground.
+
+   \sa backgroundMode(), setBackgroundBrush(), drawBackground()
+ */
+void QwtPlotLegendItem::setBackgroundMode( BackgroundMode mode )
+{
+    if ( mode != d_data->backgroundMode )
+    {
+        d_data->backgroundMode = mode;
+        itemChanged();
+    }
+}
+
+/*! 
+  \return backgroundMode
+  \sa setBackgroundMode(), backgroundBrush(), drawBackground()
+ */
+QwtPlotLegendItem::BackgroundMode QwtPlotLegendItem::backgroundMode() const
+{
+    return d_data->backgroundMode;
+}
+
+/*!
+  \brief Set the pen for drawing text labels
+
+  \param pen Text pen
+  \sa textPen(), setFont()
+ */
+void QwtPlotLegendItem::setTextPen( const QPen &pen )
+{
+    if ( d_data->textPen != pen )
+    {
+        d_data->textPen = pen;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen for drawing text labels
+  \sa setTextPen(), font()
+ */
+QPen QwtPlotLegendItem::textPen() const
+{
+    return d_data->textPen;
+}
+
+/*!
+  Draw the legend
+
+  \param painter Painter
+  \param xMap x Scale Map
+  \param yMap y Scale Map
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+*/
+void QwtPlotLegendItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    Q_UNUSED( xMap );
+    Q_UNUSED( yMap );
+
+    d_data->layout->setGeometry( geometry( canvasRect ) );
+    if ( d_data->layout->geometry().isEmpty() )
+    {
+        // don't draw a legend when having no content
+        return;
+    }
+
+    if ( d_data->backgroundMode == QwtPlotLegendItem::LegendBackground )
+        drawBackground( painter, d_data->layout->geometry() );
+    
+    for ( int i = 0; i <  d_data->layout->count(); i++ )
+    {
+        const QwtLegendLayoutItem *layoutItem = 
+            static_cast<QwtLegendLayoutItem *>( d_data->layout->itemAt( i ) );
+
+        if ( d_data->backgroundMode == QwtPlotLegendItem::ItemBackground )
+            drawBackground( painter, layoutItem->geometry() );
+
+        painter->save();
+
+        drawLegendData( painter, layoutItem->plotItem(),
+            layoutItem->data(), layoutItem->geometry() );
+
+        painter->restore();
+    }
+}
+
+/*!
+  Draw a rounded rect
+
+  \param painter Painter
+  \param rect Bounding rectangle
+
+  \sa setBorderRadius(), setBorderPen(),
+      setBackgroundBrush(), setBackgroundMode()
+ */
+void QwtPlotLegendItem::drawBackground( 
+    QPainter *painter, const QRectF &rect ) const
+{
+    painter->save();
+
+    painter->setPen( d_data->borderPen );
+    painter->setBrush( d_data->backgroundBrush );
+    
+    const double radius = d_data->borderRadius;
+    painter->drawRoundedRect( rect, radius, radius );
+    
+    painter->restore();
+}
+
+/*!
+  Calculate the geometry of the legend on the canvas
+
+  \param canvasRect Geometry of the canvas
+  \return Geometry of the legend
+*/
+QRect QwtPlotLegendItem::geometry( const QRectF &canvasRect ) const
+{
+    QRect rect;
+    rect.setSize( d_data->layout->sizeHint() );
+
+    int margin = d_data->borderDistance;
+    if ( d_data->alignment & Qt::AlignHCenter )
+    {
+        int x = qRound( canvasRect.center().x() );
+        rect.moveCenter( QPoint( x, rect.center().y() ) ); 
+    }
+    else if ( d_data->alignment & Qt::AlignRight )
+    {
+        rect.moveRight( qFloor( canvasRect.right() - margin ) );
+    }
+    else 
+    {
+        rect.moveLeft( qCeil( canvasRect.left() + margin ) );
+    }
+
+    if ( d_data->alignment & Qt::AlignVCenter )
+    {
+        int y = qRound( canvasRect.center().y() );
+        rect.moveCenter( QPoint( rect.center().x(), y ) );
+    }
+    else if ( d_data->alignment & Qt::AlignBottom )
+    {
+        rect.moveBottom( qFloor( canvasRect.bottom() - margin ) );
+    }
+    else 
+    {
+        rect.moveTop( qCeil( canvasRect.top() + margin ) ); 
+    }
+
+    return rect;
+}
+
+/*!
+  Update the legend items according to modifications of a 
+  plot item
+
+  \param plotItem Plot item
+  \param data Attributes of the legend entries
+ */
+void QwtPlotLegendItem::updateLegend( const QwtPlotItem *plotItem,
+        const QList<QwtLegendData> &data )
+{
+    if ( plotItem == NULL )
+        return;
+
+    QList<QwtLegendLayoutItem *> layoutItems;
+
+    QMap<const QwtPlotItem *, QList<QwtLegendLayoutItem *> >::iterator it = 
+        d_data->map.find( plotItem );
+    if ( it != d_data->map.end() )
+        layoutItems = it.value();
+
+    bool changed = false;
+
+    if ( data.size() != layoutItems.size() )
+    {
+        changed = true;
+
+        for ( int i = 0; i < layoutItems.size(); i++ )
+        {
+            d_data->layout->removeItem( layoutItems[i] );
+            delete layoutItems[i];
+        }
+        layoutItems.clear();
+
+        if ( it != d_data->map.end() )
+            d_data->map.remove( plotItem );
+
+        if ( !data.isEmpty() )
+        {
+            for ( int i = 0; i < data.size(); i++ )
+            {
+                QwtLegendLayoutItem *layoutItem = 
+                    new QwtLegendLayoutItem( this, plotItem );
+                d_data->layout->addItem( layoutItem );
+                layoutItems += layoutItem;
+            }
+
+            d_data->map.insert( plotItem, layoutItems );
+        }
+    }
+
+    for ( int i = 0; i < data.size(); i++ )
+    {
+        if ( layoutItems[i]->data().values() != data[i].values() )
+        {
+            layoutItems[i]->setData( data[i] );
+            changed = true;
+        }
+    }
+
+    if ( changed )
+    {
+        d_data->layout->invalidate();
+        itemChanged();
+    }
+}
+
+//! Remove all items from the legend
+void QwtPlotLegendItem::clearLegend()
+{
+    if ( !d_data->map.isEmpty() )
+    {
+        d_data->map.clear();
+
+        for ( int i = d_data->layout->count() - 1; i >= 0; i-- )
+            delete d_data->layout->takeAt( i );
+
+        itemChanged();
+    }
+}
+
+/*!
+  Draw an entry on the legend
+
+  \param painter Qt Painter
+  \param plotItem Plot item, represented by the entry
+  \param data Attributes of the legend entry
+  \param rect Bounding rectangle for the entry
+ */
+void QwtPlotLegendItem::drawLegendData( QPainter *painter,
+    const QwtPlotItem *plotItem, const QwtLegendData &data, 
+    const QRectF &rect ) const
+{
+    Q_UNUSED( plotItem );
+
+    const int m = d_data->itemMargin;
+    const QRectF r = rect.toRect().adjusted( m, m, -m, -m );
+
+    painter->setClipRect( r, Qt::IntersectClip );
+
+    int titleOff = 0;
+
+    const QwtGraphic graphic = data.icon();
+    if ( !graphic.isEmpty() )
+    {
+        QRectF iconRect( r.topLeft(), graphic.defaultSize() );
+
+        iconRect.moveCenter( 
+            QPoint( iconRect.center().x(), rect.center().y() ) );
+
+        graphic.render( painter, iconRect, Qt::KeepAspectRatio );
+
+        titleOff += iconRect.width() + d_data->itemSpacing;
+    }
+
+    const QwtText text = data.title();
+    if ( !text.isEmpty() )
+    {
+        painter->setPen( textPen() );
+        painter->setFont( font() );
+
+        const QRectF textRect = r.adjusted( titleOff, 0, 0, 0 );
+        text.draw( painter, textRect );
+    }
+}
+
+/*!
+  Minimum size hint needed to display an entry
+
+  \param data Attributes of the legend entry
+  \return Minimum size
+ */
+QSize QwtPlotLegendItem::minimumSize( const QwtLegendData &data ) const
+{
+    QSize size( 2 * d_data->itemMargin, 2 * d_data->itemMargin );
+
+    if ( !data.isValid() )
+        return size;
+
+    const QwtGraphic graphic = data.icon();
+    const QwtText text = data.title();
+
+    int w = 0;
+    int h = 0;
+
+    if ( !graphic.isNull() )
+    {
+        w = graphic.width();
+        h = graphic.height();
+    }
+
+    if ( !text.isEmpty() )
+    {
+        const QSizeF sz = text.textSize( font() );
+
+        w += qCeil( sz.width() );
+        h = qMax( h, qCeil( sz.height() ) );
+    }
+
+    if ( graphic.width() > 0 && !text.isEmpty() )
+        w += d_data->itemSpacing;
+
+    size += QSize( w, h );
+    return size;
+}
+
+/*!
+  \return The preferred height, for a width.
+  \param data Attributes of the legend entry
+  \param width Width
+*/
+int QwtPlotLegendItem::heightForWidth( 
+    const QwtLegendData &data, int width ) const
+{
+    width -= 2 * d_data->itemMargin;
+
+    const QwtGraphic graphic = data.icon();
+    const QwtText text = data.title();
+
+    if ( text.isEmpty() )
+        return graphic.height();
+
+    if ( graphic.width() > 0 )
+        width -= graphic.width() + d_data->itemSpacing;
+
+    int h = text.heightForWidth( width, font() );
+    h += 2 * d_data->itemMargin;
+
+    return qMax( graphic.height(), h );
+}
+
+/*! 
+  \return All plot items with an entry on the legend
+  \note A plot item might have more than one entry on the legend
+ */
+QList< const QwtPlotItem * > QwtPlotLegendItem::plotItems() const
+{
+    return d_data->map.keys();
+}
+
+/*!
+  \return Geometries of the items of a plot item
+  \note Usually a plot item has only one entry on the legend
+*/
+QList< QRect > QwtPlotLegendItem::legendGeometries( 
+    const QwtPlotItem *plotItem ) const
+{
+    QList<QwtLegendLayoutItem *> layoutItems;
+
+    QMap<const QwtPlotItem *, QList<QwtLegendLayoutItem *> >::iterator it =
+        d_data->map.find( plotItem );
+    if ( it != d_data->map.end() )
+        layoutItems = it.value();
+
+    QList<QRect> geometries;
+    for ( int i = 0; i < layoutItems.size(); i++ )
+        geometries += layoutItems[i]->geometry();
+
+    return geometries;
+}
diff --git a/source/third_party/qwt/qwt_plot_magnifier.cpp b/source/third_party/qwt/qwt_plot_magnifier.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..de6984627783d0139a0d144c2fd7fa3e85a4fd6c
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_magnifier.cpp
@@ -0,0 +1,165 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_plot_magnifier.h"
+#include <qevent.h>
+
+class QwtPlotMagnifier::PrivateData
+{
+public:
+    PrivateData()
+    {
+        for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+            isAxisEnabled[axis] = true;
+    }
+
+    bool isAxisEnabled[QwtPlot::axisCnt];
+};
+
+/*!
+   Constructor
+   \param canvas Plot canvas to be magnified
+*/
+QwtPlotMagnifier::QwtPlotMagnifier( QWidget *canvas ):
+    QwtMagnifier( canvas )
+{
+    d_data = new PrivateData();
+}
+
+//! Destructor
+QwtPlotMagnifier::~QwtPlotMagnifier()
+{
+    delete d_data;
+}
+
+/*!
+   \brief En/Disable an axis
+
+   Only Axes that are enabled will be zoomed.
+   All other axes will remain unchanged.
+
+   \param axis Axis, see QwtPlot::Axis
+   \param on On/Off
+
+   \sa isAxisEnabled()
+*/
+void QwtPlotMagnifier::setAxisEnabled( int axis, bool on )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->isAxisEnabled[axis] = on;
+}
+
+/*!
+   Test if an axis is enabled
+
+   \param axis Axis, see QwtPlot::Axis
+   \return True, if the axis is enabled
+
+   \sa setAxisEnabled()
+*/
+bool QwtPlotMagnifier::isAxisEnabled( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->isAxisEnabled[axis];
+
+    return true;
+}
+
+//! Return observed plot canvas
+QWidget *QwtPlotMagnifier::canvas()
+{
+    return parentWidget();
+}
+
+//! Return Observed plot canvas
+const QWidget *QwtPlotMagnifier::canvas() const
+{
+    return parentWidget();
+}
+
+//! Return plot widget, containing the observed plot canvas
+QwtPlot *QwtPlotMagnifier::plot()
+{
+    QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<QwtPlot *>( w );
+}
+
+//! Return plot widget, containing the observed plot canvas
+const QwtPlot *QwtPlotMagnifier::plot() const
+{
+    const QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<const QwtPlot *>( w );
+}
+
+/*!
+   Zoom in/out the axes scales
+   \param factor A value < 1.0 zooms in, a value > 1.0 zooms out.
+*/
+void QwtPlotMagnifier::rescale( double factor )
+{
+    QwtPlot* plt = plot();
+    if ( plt == NULL )
+        return;
+
+    factor = qAbs( factor );
+    if ( factor == 1.0 || factor == 0.0 )
+        return;
+
+    bool doReplot = false;
+
+    const bool autoReplot = plt->autoReplot();
+    plt->setAutoReplot( false );
+
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        if ( isAxisEnabled( axisId ) )
+        {
+            const QwtScaleMap scaleMap = plt->canvasMap( axisId );
+
+            double v1 = scaleMap.s1();
+            double v2 = scaleMap.s2();
+
+            if ( scaleMap.transformation() )
+            {
+                // the coordinate system of the paint device is always linear
+
+                v1 = scaleMap.transform( v1 ); // scaleMap.p1()
+                v2 = scaleMap.transform( v2 ); // scaleMap.p2()
+            }
+
+            const double center = 0.5 * ( v1 + v2 );
+            const double width_2 = 0.5 * ( v2 - v1 ) * factor;
+
+            v1 = center - width_2;
+            v2 = center + width_2;
+
+            if ( scaleMap.transformation() )
+            {
+                v1 = scaleMap.invTransform( v1 );
+                v2 = scaleMap.invTransform( v2 );
+            }
+
+            plt->setAxisScale( axisId, v1, v2 );
+            doReplot = true;
+        }
+    }
+
+    plt->setAutoReplot( autoReplot );
+
+    if ( doReplot )
+        plt->replot();
+}
diff --git a/source/third_party/qwt/qwt_plot_marker.cpp b/source/third_party/qwt/qwt_plot_marker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a8ff342019cdac98a2530341ff16275b5dc68bd
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_marker.cpp
@@ -0,0 +1,610 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_marker.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_symbol.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_math.h"
+#include <qpainter.h>
+
+class QwtPlotMarker::PrivateData
+{
+public:
+    PrivateData():
+        labelAlignment( Qt::AlignCenter ),
+        labelOrientation( Qt::Horizontal ),
+        spacing( 2 ),
+        symbol( NULL ),
+        style( QwtPlotMarker::NoLine ),
+        xValue( 0.0 ),
+        yValue( 0.0 )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete symbol;
+    }
+
+    QwtText label;
+    Qt::Alignment labelAlignment;
+    Qt::Orientation labelOrientation;
+    int spacing;
+
+    QPen pen;
+    const QwtSymbol *symbol;
+    LineStyle style;
+
+    double xValue;
+    double yValue;
+};
+
+//! Sets alignment to Qt::AlignCenter, and style to QwtPlotMarker::NoLine
+QwtPlotMarker::QwtPlotMarker( const QString &title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    d_data = new PrivateData;
+    setZ( 30.0 );
+}
+
+//! Sets alignment to Qt::AlignCenter, and style to QwtPlotMarker::NoLine
+QwtPlotMarker::QwtPlotMarker( const QwtText &title ):
+    QwtPlotItem( title )
+{
+    d_data = new PrivateData;
+    setZ( 30.0 );
+}
+
+//! Destructor
+QwtPlotMarker::~QwtPlotMarker()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotMarker
+int QwtPlotMarker::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotMarker;
+}
+
+//! Return Value
+QPointF QwtPlotMarker::value() const
+{
+    return QPointF( d_data->xValue, d_data->yValue );
+}
+
+//! Return x Value
+double QwtPlotMarker::xValue() const
+{
+    return d_data->xValue;
+}
+
+//! Return y Value
+double QwtPlotMarker::yValue() const
+{
+    return d_data->yValue;
+}
+
+//! Set Value
+void QwtPlotMarker::setValue( const QPointF& pos )
+{
+    setValue( pos.x(), pos.y() );
+}
+
+//! Set Value
+void QwtPlotMarker::setValue( double x, double y )
+{
+    if ( x != d_data->xValue || y != d_data->yValue )
+    {
+        d_data->xValue = x;
+        d_data->yValue = y;
+        itemChanged();
+    }
+}
+
+//! Set X Value
+void QwtPlotMarker::setXValue( double x )
+{
+    setValue( x, d_data->yValue );
+}
+
+//! Set Y Value
+void QwtPlotMarker::setYValue( double y )
+{
+    setValue( d_data->xValue, y );
+}
+
+/*!
+  Draw the marker
+
+  \param painter Painter
+  \param xMap x Scale Map
+  \param yMap y Scale Map
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+*/
+void QwtPlotMarker::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    const QPointF pos( xMap.transform( d_data->xValue ), 
+        yMap.transform( d_data->yValue ) );
+
+    // draw lines
+
+    drawLines( painter, canvasRect, pos );
+
+    // draw symbol
+    if ( d_data->symbol &&
+        ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
+    {
+        const QSizeF sz = d_data->symbol->size();
+
+        const QRectF clipRect = canvasRect.adjusted( 
+            -sz.width(), -sz.height(), sz.width(), sz.height() );
+
+        if ( clipRect.contains( pos ) )
+            d_data->symbol->drawSymbol( painter, pos );
+    }
+
+    drawLabel( painter, canvasRect, pos );
+}
+
+/*!
+  Draw the lines marker
+
+  \param painter Painter
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+  \param pos Position of the marker, translated into widget coordinates
+
+  \sa drawLabel(), QwtSymbol::drawSymbol()
+*/
+void QwtPlotMarker::drawLines( QPainter *painter,
+    const QRectF &canvasRect, const QPointF &pos ) const
+{
+    if ( d_data->style == NoLine )
+        return;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    painter->setPen( d_data->pen );
+    if ( d_data->style == QwtPlotMarker::HLine ||
+        d_data->style == QwtPlotMarker::Cross )
+    {
+        double y = pos.y();
+        if ( doAlign )
+            y = qRound( y );
+
+        QwtPainter::drawLine( painter, canvasRect.left(),
+            y, canvasRect.right() - 1.0, y );
+    }
+    if ( d_data->style == QwtPlotMarker::VLine ||
+        d_data->style == QwtPlotMarker::Cross )
+    {
+        double x = pos.x();
+        if ( doAlign )
+            x = qRound( x );
+
+        QwtPainter::drawLine( painter, x,
+            canvasRect.top(), x, canvasRect.bottom() - 1.0 );
+    }
+}
+
+/*!
+  Align and draw the text label of the marker
+
+  \param painter Painter
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+  \param pos Position of the marker, translated into widget coordinates
+
+  \sa drawLabel(), QwtSymbol::drawSymbol()
+*/
+void QwtPlotMarker::drawLabel( QPainter *painter,
+    const QRectF &canvasRect, const QPointF &pos ) const
+{
+    if ( d_data->label.isEmpty() )
+        return;
+
+    Qt::Alignment align = d_data->labelAlignment;
+    QPointF alignPos = pos;
+
+    QSizeF symbolOff( 0, 0 );
+
+    switch ( d_data->style )
+    {
+        case QwtPlotMarker::VLine:
+        {
+            // In VLine-style the y-position is pointless and
+            // the alignment flags are relative to the canvas
+
+            if ( d_data->labelAlignment & Qt::AlignTop )
+            {
+                alignPos.setY( canvasRect.top() );
+                align &= ~Qt::AlignTop;
+                align |= Qt::AlignBottom;
+            }
+            else if ( d_data->labelAlignment & Qt::AlignBottom )
+            {
+                // In HLine-style the x-position is pointless and
+                // the alignment flags are relative to the canvas
+
+                alignPos.setY( canvasRect.bottom() - 1 );
+                align &= ~Qt::AlignBottom;
+                align |= Qt::AlignTop;
+            }
+            else
+            {
+                alignPos.setY( canvasRect.center().y() );
+            }
+            break;
+        }
+        case QwtPlotMarker::HLine:
+        {
+            if ( d_data->labelAlignment & Qt::AlignLeft )
+            {
+                alignPos.setX( canvasRect.left() );
+                align &= ~Qt::AlignLeft;
+                align |= Qt::AlignRight;
+            }
+            else if ( d_data->labelAlignment & Qt::AlignRight )
+            {
+                alignPos.setX( canvasRect.right() - 1 );
+                align &= ~Qt::AlignRight;
+                align |= Qt::AlignLeft;
+            }
+            else
+            {
+                alignPos.setX( canvasRect.center().x() );
+            }
+            break;
+        }
+        default:
+        {
+            if ( d_data->symbol &&
+                ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
+            {
+                symbolOff = d_data->symbol->size() + QSizeF( 1, 1 );
+                symbolOff /= 2;
+            }
+        }
+    }
+
+    qreal pw2 = d_data->pen.widthF() / 2.0;
+    if ( pw2 == 0.0 )
+        pw2 = 0.5;
+
+    const int spacing = d_data->spacing;
+
+    const qreal xOff = qMax( pw2, symbolOff.width() );
+    const qreal yOff = qMax( pw2, symbolOff.height() );
+
+    const QSizeF textSize = d_data->label.textSize( painter->font() );
+
+    if ( align & Qt::AlignLeft )
+    {
+        alignPos.rx() -= xOff + spacing;
+        if ( d_data->labelOrientation == Qt::Vertical )
+            alignPos.rx() -= textSize.height();
+        else
+            alignPos.rx() -= textSize.width();
+    }
+    else if ( align & Qt::AlignRight )
+    {
+        alignPos.rx() += xOff + spacing;
+    }
+    else
+    {
+        if ( d_data->labelOrientation == Qt::Vertical )
+            alignPos.rx() -= textSize.height() / 2;
+        else
+            alignPos.rx() -= textSize.width() / 2;
+    }
+
+    if ( align & Qt::AlignTop )
+    {
+        alignPos.ry() -= yOff + spacing;
+        if ( d_data->labelOrientation != Qt::Vertical )
+            alignPos.ry() -= textSize.height();
+    }
+    else if ( align & Qt::AlignBottom )
+    {
+        alignPos.ry() += yOff + spacing;
+        if ( d_data->labelOrientation == Qt::Vertical )
+            alignPos.ry() += textSize.width();
+    }
+    else
+    {
+        if ( d_data->labelOrientation == Qt::Vertical )
+            alignPos.ry() += textSize.width() / 2;
+        else
+            alignPos.ry() -= textSize.height() / 2;
+    }
+
+    painter->translate( alignPos.x(), alignPos.y() );
+    if ( d_data->labelOrientation == Qt::Vertical )
+        painter->rotate( -90.0 );
+
+    const QRectF textRect( 0, 0, textSize.width(), textSize.height() );
+    d_data->label.draw( painter, textRect );
+}
+
+/*!
+  \brief Set the line style
+  \param style Line style. 
+  \sa lineStyle()
+*/
+void QwtPlotMarker::setLineStyle( LineStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return the line style
+  \sa setLineStyle()
+*/
+QwtPlotMarker::LineStyle QwtPlotMarker::lineStyle() const
+{
+    return d_data->style;
+}
+
+/*!
+  \brief Assign a symbol
+  \param symbol New symbol
+  \sa symbol()
+*/
+void QwtPlotMarker::setSymbol( const QwtSymbol *symbol )
+{
+    if ( symbol != d_data->symbol )
+    {
+        delete d_data->symbol;
+        d_data->symbol = symbol;
+
+        if ( symbol )
+            setLegendIconSize( symbol->boundingRect().size() );
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return the symbol
+  \sa setSymbol(), QwtSymbol
+*/
+const QwtSymbol *QwtPlotMarker::symbol() const
+{
+    return d_data->symbol;
+}
+
+/*!
+  \brief Set the label
+  \param label Label text
+  \sa label()
+*/
+void QwtPlotMarker::setLabel( const QwtText& label )
+{
+    if ( label != d_data->label )
+    {
+        d_data->label = label;
+        itemChanged();
+    }
+}
+
+/*!
+  \return the label
+  \sa setLabel()
+*/
+QwtText QwtPlotMarker::label() const
+{
+    return d_data->label;
+}
+
+/*!
+  \brief Set the alignment of the label
+
+  In case of QwtPlotMarker::HLine the alignment is relative to the
+  y position of the marker, but the horizontal flags correspond to the
+  canvas rectangle. In case of QwtPlotMarker::VLine the alignment is
+  relative to the x position of the marker, but the vertical flags
+  correspond to the canvas rectangle.
+
+  In all other styles the alignment is relative to the marker's position.
+
+  \param align Alignment. 
+  \sa labelAlignment(), labelOrientation()
+*/
+void QwtPlotMarker::setLabelAlignment( Qt::Alignment align )
+{
+    if ( align != d_data->labelAlignment )
+    {
+        d_data->labelAlignment = align;
+        itemChanged();
+    }
+}
+
+/*!
+  \return the label alignment
+  \sa setLabelAlignment(), setLabelOrientation()
+*/
+Qt::Alignment QwtPlotMarker::labelAlignment() const
+{
+    return d_data->labelAlignment;
+}
+
+/*!
+  \brief Set the orientation of the label
+
+  When orientation is Qt::Vertical the label is rotated by 90.0 degrees
+  ( from bottom to top ).
+
+  \param orientation Orientation of the label
+
+  \sa labelOrientation(), setLabelAlignment()
+*/
+void QwtPlotMarker::setLabelOrientation( Qt::Orientation orientation )
+{
+    if ( orientation != d_data->labelOrientation )
+    {
+        d_data->labelOrientation = orientation;
+        itemChanged();
+    }
+}
+
+/*!
+  \return the label orientation
+  \sa setLabelOrientation(), labelAlignment()
+*/
+Qt::Orientation QwtPlotMarker::labelOrientation() const
+{
+    return d_data->labelOrientation;
+}
+
+/*!
+  \brief Set the spacing
+
+  When the label is not centered on the marker position, the spacing
+  is the distance between the position and the label.
+
+  \param spacing Spacing
+  \sa spacing(), setLabelAlignment()
+*/
+void QwtPlotMarker::setSpacing( int spacing )
+{
+    if ( spacing < 0 )
+        spacing = 0;
+
+    if ( spacing == d_data->spacing )
+        return;
+
+    d_data->spacing = spacing;
+    itemChanged();
+}
+
+/*!
+  \return the spacing
+  \sa setSpacing()
+*/
+int QwtPlotMarker::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*! 
+  Build and assign a line pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotMarker::setLinePen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setLinePen( QPen( color, width, style ) );
+}
+
+/*!
+  Specify a pen for the line.
+
+  \param pen New pen
+  \sa linePen()
+*/
+void QwtPlotMarker::setLinePen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return the line pen
+  \sa setLinePen()
+*/
+const QPen &QwtPlotMarker::linePen() const
+{
+    return d_data->pen;
+}
+
+QRectF QwtPlotMarker::boundingRect() const
+{
+    return QRectF( d_data->xValue, d_data->yValue, 0.0, 0.0 );
+}
+
+/*!
+   \return Icon representing the marker on the legend
+
+   \param index Index of the legend entry 
+                ( usually there is only one )
+   \param size Icon size
+
+   \sa setLegendIconSize(), legendData()
+*/
+QwtGraphic QwtPlotMarker::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+
+    if ( size.isEmpty() )
+        return QwtGraphic();
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+    icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &icon );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    if ( d_data->style != QwtPlotMarker::NoLine )
+    {
+        painter.setPen( d_data->pen );
+
+        if ( d_data->style == QwtPlotMarker::HLine ||
+            d_data->style == QwtPlotMarker::Cross )
+        {
+            const double y = 0.5 * size.height();
+
+            QwtPainter::drawLine( &painter, 
+                0.0, y, size.width(), y );
+        }
+
+        if ( d_data->style == QwtPlotMarker::VLine ||
+            d_data->style == QwtPlotMarker::Cross )
+        {
+            const double x = 0.5 * size.width();
+
+            QwtPainter::drawLine( &painter, 
+                x, 0.0, x, size.height() );
+        }
+    }
+
+    if ( d_data->symbol )
+    {
+        const QRect r( 0.0, 0.0, size.width(), size.height() );
+        d_data->symbol->drawSymbol( &painter, r );
+    }
+
+    return icon;
+}
+
diff --git a/source/third_party/qwt/qwt_plot_multi_barchart.cpp b/source/third_party/qwt/qwt_plot_multi_barchart.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3ee76f61824666637b0f8f564238844621b74c78
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_multi_barchart.cpp
@@ -0,0 +1,744 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_multi_barchart.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_column_symbol.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qmap.h>
+
+inline static bool qwtIsIncreasing(
+    const QwtScaleMap &map, const QVector<double> &values )
+{
+    bool isInverting = map.isInverting();
+
+    for ( int i = 0; i < values.size(); i++ )
+    {
+        const double y = values[ i ];
+        if ( y != 0.0 )
+            return ( map.isInverting() != ( y > 0.0 ) );
+    }
+
+    return !isInverting;
+}
+
+class QwtPlotMultiBarChart::PrivateData
+{
+public:
+    PrivateData():
+        style( QwtPlotMultiBarChart::Grouped )
+    {
+    }
+
+    QwtPlotMultiBarChart::ChartStyle style;
+    QList<QwtText> barTitles;
+    QMap<int, QwtColumnSymbol *> symbolMap;
+};
+
+/*!
+  Constructor
+  \param title Title of the chart
+*/
+QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QwtText &title ):
+    QwtPlotAbstractBarChart( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the chart
+*/
+QwtPlotMultiBarChart::QwtPlotMultiBarChart( const QString &title ):
+    QwtPlotAbstractBarChart( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotMultiBarChart::~QwtPlotMultiBarChart()
+{
+    resetSymbolMap();
+    delete d_data;
+}
+
+void QwtPlotMultiBarChart::init()
+{
+    d_data = new PrivateData;
+    setData( new QwtSetSeriesData() );
+}
+
+//! \return QwtPlotItem::Rtti_PlotBarChart
+int QwtPlotMultiBarChart::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotMultiBarChart;
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of points
+*/
+void QwtPlotMultiBarChart::setSamples(
+    const QVector<QwtSetSample> &samples )
+{
+    setData( new QwtSetSeriesData( samples ) );
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of points
+*/
+void QwtPlotMultiBarChart::setSamples(
+    const QVector< QVector<double> > &samples )
+{
+    QVector<QwtSetSample> s;
+    for ( int i = 0; i < samples.size(); i++ )
+        s += QwtSetSample( i, samples[ i ] );
+
+    setData( new QwtSetSeriesData( s ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore.
+*/  
+void QwtPlotMultiBarChart::setSamples( 
+    QwtSeriesData<QwtSetSample> *data )
+{       
+    setData( data );
+}       
+
+/*!
+  \brief Set the titles for the bars
+
+  The titles are used for the legend.
+
+  \param titles Bar titles
+
+  \sa barTitles(), legendData()
+ */
+void QwtPlotMultiBarChart::setBarTitles( const QList<QwtText> &titles )
+{
+    d_data->barTitles = titles;
+    itemChanged();
+}
+
+/*! 
+  \return Bar titles
+  \sa setBarTitles(), legendData()
+ */
+QList<QwtText> QwtPlotMultiBarChart::barTitles() const
+{
+    return d_data->barTitles;
+}
+
+/*!
+  \brief Add a symbol to the symbol map
+
+  Assign a default symbol for drawing the bar representing all values
+  with the same index in a set.
+
+  \param valueIndex Index of a value in a set
+  \param symbol Symbol used for drawing a bar
+
+  \sa symbol(), resetSymbolMap(), specialSymbol()
+*/
+void QwtPlotMultiBarChart::setSymbol( int valueIndex, QwtColumnSymbol *symbol )
+{
+    if ( valueIndex < 0 )
+        return;
+
+    QMap<int, QwtColumnSymbol *>::iterator it = 
+        d_data->symbolMap.find(valueIndex);
+    if ( it == d_data->symbolMap.end() )
+    {
+        if ( symbol != NULL )
+        {
+            d_data->symbolMap.insert( valueIndex, symbol );
+
+            legendChanged();
+            itemChanged();
+        }
+    }
+    else
+    {
+        if ( symbol != it.value() )
+        {
+            delete it.value();
+
+            if ( symbol == NULL )
+            {
+                d_data->symbolMap.remove( valueIndex );
+            }
+            else
+            {
+                it.value() = symbol;
+            }
+
+            legendChanged();
+            itemChanged();
+        }
+    }
+}
+
+/*!
+  Find a symbol in the symbol map
+
+  \param valueIndex Index of a value in a set
+  \return The symbol, that had been set by setSymbol() or NULL.
+
+  \sa setSymbol(), specialSymbol(), drawBar()
+*/
+const QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex ) const
+{
+    QMap<int, QwtColumnSymbol *>::const_iterator it =
+        d_data->symbolMap.find( valueIndex );
+
+    return ( it == d_data->symbolMap.end() ) ? NULL : it.value();
+}
+
+/*!
+  Find a symbol in the symbol map
+
+  \param valueIndex Index of a value in a set
+  \return The symbol, that had been set by setSymbol() or NULL.
+
+  \sa setSymbol(), specialSymbol(), drawBar()
+*/
+QwtColumnSymbol *QwtPlotMultiBarChart::symbol( int valueIndex ) 
+{
+    QMap<int, QwtColumnSymbol *>::iterator it =
+        d_data->symbolMap.find( valueIndex );
+
+    return ( it == d_data->symbolMap.end() ) ? NULL : it.value();
+}
+
+/*!
+  Remove all symbols from the symbol map
+ */
+void QwtPlotMultiBarChart::resetSymbolMap()
+{
+    for ( QMap<int, QwtColumnSymbol *>::iterator it 
+        = d_data->symbolMap.begin(); it != d_data->symbolMap.end(); ++it )
+    {
+        delete it.value();
+    }
+
+    d_data->symbolMap.clear();
+}
+
+/*!
+  \brief Create a symbol for special values
+
+  Usually the symbols for displaying a bar are set by setSymbols() and
+  common for all sets. By overloading specialSymbol() it is possible to
+  create a temporary symbol() for displaying a special value.
+
+  The symbol has to be created by new each time specialSymbol() is
+  called. As soon as the symbol is painted this symbol gets deleted.
+
+  When no symbol ( NULL ) is returned, the value will be displayed
+  with the standard symbol that is used for all symbols with the same 
+  valueIndex.
+
+  \param sampleIndex Index of the sample
+  \param valueIndex Index of the value in the set
+
+  \return NULL, meaning that the value is not special
+    
+ */
+QwtColumnSymbol *QwtPlotMultiBarChart::specialSymbol( 
+    int sampleIndex, int valueIndex ) const
+{
+    Q_UNUSED( sampleIndex );
+    Q_UNUSED( valueIndex );
+
+    return NULL;
+}
+
+/*!
+  Set the style of the chart
+
+  \param style Chart style
+  \sa style()
+ */
+void QwtPlotMultiBarChart::setStyle( ChartStyle style )
+{
+    if ( style != d_data->style )
+    {
+        d_data->style = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Style of the chart
+  \sa setStyle()
+ */
+QwtPlotMultiBarChart::ChartStyle QwtPlotMultiBarChart::style() const
+{
+    return d_data->style;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotMultiBarChart::boundingRect() const
+{
+    const size_t numSamples = dataSize();
+
+    if ( numSamples == 0 )
+        return QwtPlotSeriesItem::boundingRect();
+
+    const double baseLine = baseline();
+
+    QRectF rect;
+
+    if ( d_data->style != QwtPlotMultiBarChart::Stacked )
+    {
+        rect = QwtPlotSeriesItem::boundingRect();
+
+        if ( rect.height() >= 0 )
+        {
+            if ( rect.bottom() < baseLine )
+                rect.setBottom( baseLine );
+            if ( rect.top() > baseLine )
+                rect.setTop( baseLine );
+        }
+    }
+    else
+    {
+        double xMin, xMax, yMin, yMax;
+
+        xMin = xMax = 0.0;
+        yMin = yMax = baseLine;
+
+        const QwtSeriesData<QwtSetSample> *series = data();
+
+        for ( size_t i = 0; i < numSamples; i++ )
+        {
+            const QwtSetSample sample = series->sample( i );
+            if ( i == 0 )
+            {
+                xMin = xMax = sample.value;
+            }
+            else
+            {
+                xMin = qMin( xMin, sample.value );
+                xMax = qMax( xMax, sample.value );
+            }
+
+            const double y = baseLine + sample.added();
+
+            yMin = qMin( yMin, y );
+            yMax = qMax( yMax, y );
+        }
+        rect.setRect( xMin, yMin, xMax - xMin, yMax - yMin );
+    }
+
+    if ( orientation() == Qt::Horizontal )
+        rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
+
+    return rect;
+}
+
+/*!
+  Draw an interval of the bar chart
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         curve will be painted to its last point.
+
+  \sa drawSymbols()
+*/
+void QwtPlotMultiBarChart::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+
+    const QRectF br = data()->boundingRect();
+    const QwtInterval interval( br.left(), br.right() );
+
+    painter->save();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        drawSample( painter, xMap, yMap,
+            canvasRect, interval, i, sample( i ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+  Draw a sample
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param boundingInterval Bounding interval of sample values
+  \param index Index of the sample to be painted
+  \param sample Sample value
+
+  \sa drawSeries()
+*/
+void QwtPlotMultiBarChart::drawSample( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, const QwtInterval &boundingInterval,
+    int index, const QwtSetSample& sample ) const
+{
+    if ( sample.set.size() <= 0 )
+        return;
+
+    double sampleW;
+
+    if ( orientation() == Qt::Horizontal )
+    {
+        sampleW = sampleWidth( yMap, canvasRect.height(),
+            boundingInterval.width(), sample.value );
+    }
+    else
+    {
+        sampleW = sampleWidth( xMap, canvasRect.width(),
+            boundingInterval.width(), sample.value );
+    }
+
+    if ( d_data->style == Stacked )
+    {
+        drawStackedBars( painter, xMap, yMap,
+            canvasRect, index, sampleW, sample );
+    }
+    else
+    {
+        drawGroupedBars( painter, xMap, yMap,
+            canvasRect, index, sampleW, sample );
+    }
+}
+
+/*!
+  Draw a grouped sample
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param index Index of the sample to be painted
+  \param sampleWidth Boundng width for all bars of the smaple
+  \param sample Sample 
+
+  \sa drawSeries(), sampleWidth()
+*/
+void QwtPlotMultiBarChart::drawGroupedBars( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int index, double sampleWidth,
+    const QwtSetSample& sample ) const
+{
+    Q_UNUSED( canvasRect );
+
+    const int numBars = sample.set.size();
+    if ( numBars == 0 )
+        return;
+
+    if ( orientation() == Qt::Vertical )
+    {
+        const double barWidth = sampleWidth / numBars;
+
+        const double y1 = yMap.transform( baseline() );
+        const double x0 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
+
+        for ( int i = 0; i < numBars; i++ )
+        {
+            const double x1 = x0 + i * barWidth;
+            const double x2 = x1 + barWidth;
+
+            const double y2 = yMap.transform( sample.set[i] );
+
+            QwtColumnRect barRect;
+            barRect.direction = ( y1 < y2 ) ?
+                QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
+
+            barRect.hInterval = QwtInterval( x1, x2 ).normalized();
+            if ( i != 0 )
+                barRect.hInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
+
+            barRect.vInterval = QwtInterval( y1, y2 ).normalized();
+
+            drawBar( painter, index, i, barRect );
+        }
+    }
+    else
+    {
+        const double barHeight = sampleWidth / numBars;
+
+        const double x1 = xMap.transform( baseline() );
+        const double y0 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
+
+        for ( int i = 0; i < numBars; i++ )
+        {
+            double y1 = y0 + i * barHeight;
+            double y2 = y1 + barHeight;
+
+            double x2 = xMap.transform( sample.set[i] );
+
+            QwtColumnRect barRect;
+            barRect.direction = x1 < x2 ?
+                QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
+
+            barRect.hInterval = QwtInterval( x1, x2 ).normalized();
+
+            barRect.vInterval = QwtInterval( y1, y2 );
+            if ( i != 0 )
+                barRect.vInterval.setBorderFlags( QwtInterval::ExcludeMinimum );
+
+            drawBar( painter, index, i, barRect );
+        }
+    }
+}
+
+/*!
+  Draw a stacked sample
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param index Index of the sample to be painted
+  \param sampleWidth Width of the bars
+  \param sample Sample 
+
+  \sa drawSeries(), sampleWidth()
+*/
+void QwtPlotMultiBarChart::drawStackedBars( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int index, 
+    double sampleWidth, const QwtSetSample& sample ) const
+{
+    Q_UNUSED( canvasRect ); // clipping the bars ?
+
+    const int numBars = sample.set.size();
+    if ( numBars == 0 )
+        return;
+
+    QwtInterval::BorderFlag borderFlags = QwtInterval::IncludeBorders;
+
+    if ( orientation() == Qt::Vertical )
+    {
+        const double x1 = xMap.transform( sample.value ) - 0.5 * sampleWidth;
+        const double x2 = x1 + sampleWidth;
+
+        const bool increasing = qwtIsIncreasing( yMap, sample.set );
+
+        QwtColumnRect bar;
+        bar.direction = increasing ?
+            QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop;
+
+        bar.hInterval = QwtInterval( x1, x2 ).normalized();
+
+        double sum = baseline();
+
+        const int numBars = sample.set.size();
+        for ( int i = 0; i < numBars; i++ )
+        {
+            const double si = sample.set[ i ];
+            if ( si == 0.0 )
+                continue;
+
+            const double y1 = yMap.transform( sum );
+            const double y2 = yMap.transform( sum + si );
+
+            if ( ( y2 > y1 ) != increasing )
+            {
+                // stacked bars need to be in the same direction
+                continue;
+            }
+
+            bar.vInterval = QwtInterval( y1, y2 ).normalized();
+            bar.vInterval.setBorderFlags( borderFlags );
+
+            drawBar( painter, index, i, bar );
+
+            sum += si;
+
+            if ( increasing )
+                borderFlags = QwtInterval::ExcludeMinimum;
+            else
+                borderFlags = QwtInterval::ExcludeMaximum;
+        }
+    }
+    else
+    {
+        const double y1 = yMap.transform( sample.value ) - 0.5 * sampleWidth;
+        const double y2 = y1 + sampleWidth;
+
+        const bool increasing = qwtIsIncreasing( xMap, sample.set );
+
+        QwtColumnRect bar;
+        bar.direction = increasing ?
+            QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft;
+        bar.vInterval = QwtInterval( y1, y2 ).normalized();
+
+        double sum = baseline();
+
+        for ( int i = 0; i < sample.set.size(); i++ )
+        {
+            const double si = sample.set[ i ];
+            if ( si == 0.0 )
+                continue;
+
+            const double x1 = xMap.transform( sum );
+            const double x2 = xMap.transform( sum + si );
+
+            if ( ( x2 > x1 ) != increasing )
+            {
+                // stacked bars need to be in the same direction
+                continue;
+            }
+
+            bar.hInterval = QwtInterval( x1, x2 ).normalized();
+            bar.hInterval.setBorderFlags( borderFlags );
+
+            drawBar( painter, index, i, bar );
+
+            sum += si;
+
+            if ( increasing )
+                borderFlags = QwtInterval::ExcludeMinimum;
+            else
+                borderFlags = QwtInterval::ExcludeMaximum;
+        }
+    }
+}
+
+/*!
+  Draw a bar
+
+  \param painter Painter
+  \param sampleIndex Index of the sample - might be -1 when the
+                     bar is painted for the legend
+  \param valueIndex Index of a value in a set
+  \param rect Directed target rectangle for the bar
+
+  \sa drawSeries()
+*/
+void QwtPlotMultiBarChart::drawBar( QPainter *painter,
+    int sampleIndex, int valueIndex, const QwtColumnRect &rect ) const
+{
+    const QwtColumnSymbol *specialSym = NULL;
+    if ( sampleIndex >= 0 )
+        specialSym = specialSymbol( sampleIndex, valueIndex );
+
+    const QwtColumnSymbol *sym = specialSym;
+    if ( sym == NULL )
+        sym = symbol( valueIndex );
+
+    if ( sym )
+    {
+        sym->draw( painter, rect );
+    }
+    else
+    {
+        // we build a temporary default symbol
+        QwtColumnSymbol sym( QwtColumnSymbol::Box );
+        sym.setLineWidth( 1 );
+        sym.setFrameStyle( QwtColumnSymbol::Plain );
+        sym.draw( painter, rect );
+    }
+
+    delete specialSym;
+}
+
+/*!
+  \return Information to be displayed on the legend
+
+  The chart is represented by a list of entries - one for each bar title.
+  Each element contains a bar title and an icon showing its corresponding bar.
+
+  \sa barTitles(), legendIcon(), legendIconSize()
+*/
+QList<QwtLegendData> QwtPlotMultiBarChart::legendData() const
+{
+    QList<QwtLegendData> list;
+
+    for ( int i = 0; i < d_data->barTitles.size(); i++ )
+    {
+        QwtLegendData data;
+
+        QVariant titleValue;
+        qVariantSetValue( titleValue, d_data->barTitles[i] );
+        data.setValue( QwtLegendData::TitleRole, titleValue );
+
+        if ( !legendIconSize().isEmpty() )
+        {
+            QVariant iconValue;
+            qVariantSetValue( iconValue, 
+                legendIcon( i, legendIconSize() ) );
+
+            data.setValue( QwtLegendData::IconRole, iconValue );
+        }
+
+        list += data;
+    }
+
+    return list;
+}
+
+/*!
+  \return Icon for representing a bar on the legend
+
+  \param index Index of the bar
+  \param size Icon size
+  
+  \return An icon showing a bar
+  \sa drawBar(), legendData()
+ */
+QwtGraphic QwtPlotMultiBarChart::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    QwtColumnRect column;
+    column.hInterval = QwtInterval( 0.0, size.width() - 1.0 );
+    column.vInterval = QwtInterval( 0.0, size.height() - 1.0 );
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+    icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true );
+
+    QPainter painter( &icon );
+    painter.setRenderHint( QPainter::Antialiasing,
+        testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+    drawBar( &painter, -1, index, column );
+
+    return icon;
+}
+
diff --git a/source/third_party/qwt/qwt_plot_panner.cpp b/source/third_party/qwt/qwt_plot_panner.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6b8cbe7719724f7014992e71c10607e4834862db
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_panner.cpp
@@ -0,0 +1,275 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_panner.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_painter.h"
+#include <qbitmap.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+
+static QBitmap qwtBorderMask( const QWidget *canvas, const QSize &size )
+{
+    const QRect r( 0, 0, size.width(), size.height() );
+
+    QPainterPath borderPath;
+
+    ( void )QMetaObject::invokeMethod( 
+        const_cast< QWidget *>( canvas ), "borderPath", Qt::DirectConnection,
+        Q_RETURN_ARG( QPainterPath, borderPath ), Q_ARG( QRect, r ) );
+
+    if ( borderPath.isEmpty() )
+    {
+        if ( canvas->contentsRect() == canvas->rect() )
+            return QBitmap();
+
+        QBitmap mask( size );
+        mask.fill( Qt::color0 );
+
+        QPainter painter( &mask );
+        painter.fillRect( canvas->contentsRect(), Qt::color1 );
+
+        return mask;
+    }
+
+    QImage image( size, QImage::Format_ARGB32_Premultiplied );
+    image.fill( Qt::color0 );
+
+    QPainter painter( &image );
+    painter.setClipPath( borderPath );
+    painter.fillRect( r, Qt::color1 );
+
+    // now erase the frame
+
+    painter.setCompositionMode( QPainter::CompositionMode_DestinationOut );
+
+    if ( canvas->testAttribute(Qt::WA_StyledBackground ) )
+    {
+        QStyleOptionFrame opt;
+        opt.initFrom(canvas);
+        opt.rect = r;
+        canvas->style()->drawPrimitive( QStyle::PE_Frame, &opt, &painter, canvas );
+    }
+    else
+    {
+        const QVariant borderRadius = canvas->property( "borderRadius" );
+        const QVariant frameWidth = canvas->property( "frameWidth" );
+
+        if ( borderRadius.type() == QVariant::Double 
+            && frameWidth.type() == QVariant::Int )
+        {
+            const double br = borderRadius.toDouble();
+            const int fw = frameWidth.toInt();
+        
+            if ( br > 0.0 && fw > 0 )
+            {
+                painter.setPen( QPen( Qt::color1, fw ) );
+                painter.setBrush( Qt::NoBrush );
+                painter.setRenderHint( QPainter::Antialiasing, true );
+
+                painter.drawPath( borderPath );
+            }
+        }
+    }
+
+    painter.end();
+
+    const QImage mask = image.createMaskFromColor(
+        QColor( Qt::color1 ).rgb(), Qt::MaskOutColor );
+
+    return QBitmap::fromImage( mask );
+}
+
+class QwtPlotPanner::PrivateData
+{
+public:
+    PrivateData()
+    {
+        for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+            isAxisEnabled[axis] = true;
+    }
+
+    bool isAxisEnabled[QwtPlot::axisCnt];
+};
+
+/*!
+  \brief A panner for the canvas of a QwtPlot
+
+  The panner is enabled for all axes
+
+  \param canvas Plot canvas to pan, also the parent object
+
+  \sa setAxisEnabled()
+*/
+QwtPlotPanner::QwtPlotPanner( QWidget *canvas ):
+    QwtPanner( canvas )
+{
+    d_data = new PrivateData();
+
+    connect( this, SIGNAL( panned( int, int ) ),
+        SLOT( moveCanvas( int, int ) ) );
+}
+
+//! Destructor
+QwtPlotPanner::~QwtPlotPanner()
+{
+    delete d_data;
+}
+
+/*!
+   \brief En/Disable an axis
+
+   Axes that are enabled will be synchronized to the
+   result of panning. All other axes will remain unchanged.
+
+   \param axis Axis, see QwtPlot::Axis
+   \param on On/Off
+
+   \sa isAxisEnabled(), moveCanvas()
+*/
+void QwtPlotPanner::setAxisEnabled( int axis, bool on )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->isAxisEnabled[axis] = on;
+}
+
+/*!
+   Test if an axis is enabled
+
+   \param axis Axis, see QwtPlot::Axis
+   \return True, if the axis is enabled
+
+   \sa setAxisEnabled(), moveCanvas()
+*/
+bool QwtPlotPanner::isAxisEnabled( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->isAxisEnabled[axis];
+
+    return true;
+}
+
+//! Return observed plot canvas
+QWidget *QwtPlotPanner::canvas()
+{
+    return parentWidget();
+}
+
+//! Return Observed plot canvas
+const QWidget *QwtPlotPanner::canvas() const
+{
+    return parentWidget();
+}
+
+//! Return plot widget, containing the observed plot canvas
+QwtPlot *QwtPlotPanner::plot()
+{
+    QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<QwtPlot *>( w );
+}
+
+//! Return plot widget, containing the observed plot canvas
+const QwtPlot *QwtPlotPanner::plot() const
+{
+    const QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<const QwtPlot *>( w );
+}
+
+/*!
+   Adjust the enabled axes according to dx/dy
+
+   \param dx Pixel offset in x direction
+   \param dy Pixel offset in y direction
+
+   \sa QwtPanner::panned()
+*/
+void QwtPlotPanner::moveCanvas( int dx, int dy )
+{
+    if ( dx == 0 && dy == 0 )
+        return;
+
+    QwtPlot *plot = this->plot();
+    if ( plot == NULL )
+        return;
+
+    const bool doAutoReplot = plot->autoReplot();
+    plot->setAutoReplot( false );
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( !d_data->isAxisEnabled[axis] )
+            continue;
+
+        const QwtScaleMap map = plot->canvasMap( axis );
+
+        const double p1 = map.transform( plot->axisScaleDiv( axis ).lowerBound() );
+        const double p2 = map.transform( plot->axisScaleDiv( axis ).upperBound() );
+
+        double d1, d2;
+        if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
+        {
+            d1 = map.invTransform( p1 - dx );
+            d2 = map.invTransform( p2 - dx );
+        }
+        else
+        {
+            d1 = map.invTransform( p1 - dy );
+            d2 = map.invTransform( p2 - dy );
+        }
+
+        plot->setAxisScale( axis, d1, d2 );
+    }
+
+    plot->setAutoReplot( doAutoReplot );
+    plot->replot();
+}
+
+/*!
+   Calculate a mask from the border path of the canvas
+
+   \return Mask as bitmap
+   \sa QwtPlotCanvas::borderPath()
+*/
+QBitmap QwtPlotPanner::contentsMask() const
+{
+    if ( canvas() )
+        return qwtBorderMask( canvas(), size() );
+
+    return QwtPanner::contentsMask();
+}
+
+/*!
+   \return Pixmap with the content of the canvas
+ */
+QPixmap QwtPlotPanner::grab() const
+{   
+    const QWidget *cv = canvas();
+    if ( cv && cv->inherits( "QGLWidget" ) )
+    {
+        // we can't grab from a QGLWidget
+
+        QPixmap pm( cv->size() );
+        QwtPainter::fillPixmap( cv, pm );
+
+        QPainter painter( &pm );
+        const_cast<QwtPlot *>( plot() )->drawCanvas( &painter );
+
+        return pm;
+    }
+
+    return QwtPanner::grab();
+}   
+
diff --git a/source/third_party/qwt/qwt_plot_picker.cpp b/source/third_party/qwt/qwt_plot_picker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..14bccb53305667a7113cd85a0d701c6cb2c54b5e
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_picker.cpp
@@ -0,0 +1,378 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_picker.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_picker_machine.h"
+
+/*!
+  \brief Create a plot picker
+
+  The picker is set to those x- and y-axis of the plot
+  that are enabled. If both or no x-axis are enabled, the picker
+  is set to QwtPlot::xBottom. If both or no y-axis are
+  enabled, it is set to QwtPlot::yLeft.
+
+  \param canvas Plot canvas to observe, also the parent object
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect()
+*/
+
+QwtPlotPicker::QwtPlotPicker( QWidget *canvas ):
+    QwtPicker( canvas ),
+    d_xAxis( -1 ),
+    d_yAxis( -1 )
+{
+    if ( !canvas )
+        return;
+
+    // attach axes
+
+    int xAxis = QwtPlot::xBottom;
+
+    const QwtPlot *plot = QwtPlotPicker::plot();
+    if ( !plot->axisEnabled( QwtPlot::xBottom ) &&
+        plot->axisEnabled( QwtPlot::xTop ) )
+    {
+        xAxis = QwtPlot::xTop;
+    }
+
+    int yAxis = QwtPlot::yLeft;
+    if ( !plot->axisEnabled( QwtPlot::yLeft ) &&
+        plot->axisEnabled( QwtPlot::yRight ) )
+    {
+        yAxis = QwtPlot::yRight;
+    }
+
+    setAxis( xAxis, yAxis );
+}
+
+/*!
+  Create a plot picker
+
+  \param xAxis Set the x axis of the picker
+  \param yAxis Set the y axis of the picker
+  \param canvas Plot canvas to observe, also the parent object
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect()
+*/
+QwtPlotPicker::QwtPlotPicker( int xAxis, int yAxis, QWidget *canvas ):
+    QwtPicker( canvas ),
+    d_xAxis( xAxis ),
+    d_yAxis( yAxis )
+{
+}
+
+/*!
+  Create a plot picker
+
+  \param xAxis X axis of the picker
+  \param yAxis Y axis of the picker
+  \param rubberBand Rubber band style
+  \param trackerMode Tracker mode
+  \param canvas Plot canvas to observe, also the parent object
+
+  \sa QwtPicker, QwtPicker::setSelectionFlags(), QwtPicker::setRubberBand(),
+      QwtPicker::setTrackerMode
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect()
+*/
+QwtPlotPicker::QwtPlotPicker( int xAxis, int yAxis,
+        RubberBand rubberBand, DisplayMode trackerMode,
+        QWidget *canvas ):
+    QwtPicker( rubberBand, trackerMode, canvas ),
+    d_xAxis( xAxis ),
+    d_yAxis( yAxis )
+{
+}
+
+//! Destructor
+QwtPlotPicker::~QwtPlotPicker()
+{
+}
+
+//! \return Observed plot canvas
+QWidget *QwtPlotPicker::canvas()
+{
+    return parentWidget();
+}
+
+//! \return Observed plot canvas
+const QWidget *QwtPlotPicker::canvas() const
+{
+    return parentWidget();
+}
+
+//! \return Plot widget, containing the observed plot canvas
+QwtPlot *QwtPlotPicker::plot()
+{
+    QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<QwtPlot *>( w );
+}
+
+//! \return Plot widget, containing the observed plot canvas
+const QwtPlot *QwtPlotPicker::plot() const
+{
+    const QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<const QwtPlot *>( w );
+}
+
+/*!
+  \return Normalized bounding rectangle of the axes
+  \sa QwtPlot::autoReplot(), QwtPlot::replot().
+*/
+QRectF QwtPlotPicker::scaleRect() const
+{
+    QRectF rect;
+
+    if ( plot() )
+    {
+        const QwtScaleDiv &xs = plot()->axisScaleDiv( xAxis() );
+        const QwtScaleDiv &ys = plot()->axisScaleDiv( yAxis() );
+
+        rect = QRectF( xs.lowerBound(), ys.lowerBound(),
+            xs.range(), ys.range() );
+        rect = rect.normalized();
+    }
+
+    return rect;
+}
+
+/*!
+  Set the x and y axes of the picker
+
+  \param xAxis X axis
+  \param yAxis Y axis
+*/
+void QwtPlotPicker::setAxis( int xAxis, int yAxis )
+{
+    const QwtPlot *plt = plot();
+    if ( !plt )
+        return;
+
+    if ( xAxis != d_xAxis || yAxis != d_yAxis )
+    {
+        d_xAxis = xAxis;
+        d_yAxis = yAxis;
+    }
+}
+
+//! Return x axis
+int QwtPlotPicker::xAxis() const
+{
+    return d_xAxis;
+}
+
+//! Return y axis
+int QwtPlotPicker::yAxis() const
+{
+    return d_yAxis;
+}
+
+/*!
+  Translate a pixel position into a position string
+
+  \param pos Position in pixel coordinates
+  \return Position string
+*/
+QwtText QwtPlotPicker::trackerText( const QPoint &pos ) const
+{
+    return trackerTextF( invTransform( pos ) );
+}
+
+/*!
+  \brief Translate a position into a position string
+
+  In case of HLineRubberBand the label is the value of the
+  y position, in case of VLineRubberBand the value of the x position.
+  Otherwise the label contains x and y position separated by a ',' .
+
+  The format for the double to string conversion is "%.4f".
+
+  \param pos Position
+  \return Position string
+*/
+QwtText QwtPlotPicker::trackerTextF( const QPointF &pos ) const
+{
+    QString text;
+
+    switch ( rubberBand() )
+    {
+        case HLineRubberBand:
+            text.sprintf( "%.4f", pos.y() );
+            break;
+        case VLineRubberBand:
+            text.sprintf( "%.4f", pos.x() );
+            break;
+        default:
+            text.sprintf( "%.4f, %.4f", pos.x(), pos.y() );
+    }
+    return QwtText( text );
+}
+
+/*!
+  Append a point to the selection and update rubber band and tracker.
+
+  \param pos Additional point
+  \sa isActive, begin(), end(), move(), appended()
+
+  \note The appended(const QPoint &), appended(const QDoublePoint &)
+        signals are emitted.
+*/
+void QwtPlotPicker::append( const QPoint &pos )
+{
+    QwtPicker::append( pos );
+    Q_EMIT appended( invTransform( pos ) );
+}
+
+/*!
+  Move the last point of the selection
+
+  \param pos New position
+  \sa isActive, begin(), end(), append()
+
+  \note The moved(const QPoint &), moved(const QDoublePoint &)
+        signals are emitted.
+*/
+void QwtPlotPicker::move( const QPoint &pos )
+{
+    QwtPicker::move( pos );
+    Q_EMIT moved( invTransform( pos ) );
+}
+
+/*!
+  Close a selection setting the state to inactive.
+
+  \param ok If true, complete the selection and emit selected signals
+            otherwise discard the selection.
+  \return True if the selection has been accepted, false otherwise
+*/
+
+bool QwtPlotPicker::end( bool ok )
+{
+    ok = QwtPicker::end( ok );
+    if ( !ok )
+        return false;
+
+    QwtPlot *plot = QwtPlotPicker::plot();
+    if ( !plot )
+        return false;
+
+    const QPolygon points = selection();
+    if ( points.count() == 0 )
+        return false;
+
+    QwtPickerMachine::SelectionType selectionType =
+        QwtPickerMachine::NoSelection;
+
+    if ( stateMachine() )
+        selectionType = stateMachine()->selectionType();
+
+    switch ( selectionType )
+    {
+        case QwtPickerMachine::PointSelection:
+        {
+            const QPointF pos = invTransform( points.first() );
+            Q_EMIT selected( pos );
+            break;
+        }
+        case QwtPickerMachine::RectSelection:
+        {
+            if ( points.count() >= 2 )
+            {
+                const QPoint p1 = points.first();
+                const QPoint p2 = points.last();
+
+                const QRect rect = QRect( p1, p2 ).normalized();
+                Q_EMIT selected( invTransform( rect ) );
+            }
+            break;
+        }
+        case QwtPickerMachine::PolygonSelection:
+        {
+            QVector<QPointF> dpa( points.count() );
+            for ( int i = 0; i < points.count(); i++ )
+                dpa[i] = invTransform( points[i] );
+
+            Q_EMIT selected( dpa );
+        }
+        default:
+            break;
+    }
+
+    return true;
+}
+
+/*!
+    Translate a rectangle from pixel into plot coordinates
+
+    \return Rectangle in plot coordinates
+    \sa transform()
+*/
+QRectF QwtPlotPicker::invTransform( const QRect &rect ) const
+{
+    const QwtScaleMap xMap = plot()->canvasMap( d_xAxis );
+    const QwtScaleMap yMap = plot()->canvasMap( d_yAxis );
+
+    return QwtScaleMap::invTransform( xMap, yMap, rect );
+}
+
+/*!
+    Translate a rectangle from plot into pixel coordinates
+    \return Rectangle in pixel coordinates
+    \sa invTransform()
+*/
+QRect QwtPlotPicker::transform( const QRectF &rect ) const
+{
+    const QwtScaleMap xMap = plot()->canvasMap( d_xAxis );
+    const QwtScaleMap yMap = plot()->canvasMap( d_yAxis );
+
+    return QwtScaleMap::transform( xMap, yMap, rect ).toRect();
+}
+
+/*!
+    Translate a point from pixel into plot coordinates
+    \return Point in plot coordinates
+    \sa transform()
+*/
+QPointF QwtPlotPicker::invTransform( const QPoint &pos ) const
+{
+    QwtScaleMap xMap = plot()->canvasMap( d_xAxis );
+    QwtScaleMap yMap = plot()->canvasMap( d_yAxis );
+
+    return QPointF(
+        xMap.invTransform( pos.x() ),
+        yMap.invTransform( pos.y() )
+    );
+}
+
+/*!
+    Translate a point from plot into pixel coordinates
+    \return Point in pixel coordinates
+    \sa invTransform()
+*/
+QPoint QwtPlotPicker::transform( const QPointF &pos ) const
+{
+    QwtScaleMap xMap = plot()->canvasMap( d_xAxis );
+    QwtScaleMap yMap = plot()->canvasMap( d_yAxis );
+
+    const QPointF p( xMap.transform( pos.x() ),
+        yMap.transform( pos.y() ) );
+
+    return p.toPoint();
+}
diff --git a/source/third_party/qwt/qwt_plot_rasteritem.cpp b/source/third_party/qwt/qwt_plot_rasteritem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eeebc85310f450ca7b4633e5b5acca95092ad34b
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_rasteritem.cpp
@@ -0,0 +1,961 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_rasteritem.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_painter.h"
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qmath.h>
+#if QT_VERSION >= 0x040400
+#include <qthread.h>
+#include <qfuture.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+#endif
+#include <float.h>
+
+class QwtPlotRasterItem::PrivateData
+{
+public:
+    PrivateData():
+        alpha( -1 ),
+        paintAttributes( QwtPlotRasterItem::PaintInDeviceResolution )
+    {
+        cache.policy = QwtPlotRasterItem::NoCache;
+    }
+
+    int alpha;
+
+    QwtPlotRasterItem::PaintAttributes paintAttributes;
+
+    struct ImageCache
+    {
+        QwtPlotRasterItem::CachePolicy policy;
+        QRectF area;
+        QSizeF size;
+        QImage image;
+    } cache;
+};
+
+
+static QRectF qwtAlignRect(const QRectF &rect)
+{
+    QRectF r;
+    r.setLeft( qRound( rect.left() ) );
+    r.setRight( qRound( rect.right() ) );
+    r.setTop( qRound( rect.top() ) );
+    r.setBottom( qRound( rect.bottom() ) );
+
+    return r;
+}
+
+static QRectF qwtStripRect(const QRectF &rect, const QRectF &area,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtInterval &xInterval, const QwtInterval &yInterval)
+{
+    QRectF r = rect;
+    if ( xInterval.borderFlags() & QwtInterval::ExcludeMinimum )
+    {
+        if ( area.left() <= xInterval.minValue() )
+        {
+            if ( xMap.isInverting() )
+                r.adjust(0, 0, -1, 0);
+            else
+                r.adjust(1, 0, 0, 0);
+        }
+    }
+
+    if ( xInterval.borderFlags() & QwtInterval::ExcludeMaximum )
+    {
+        if ( area.right() >= xInterval.maxValue() )
+        {
+            if ( xMap.isInverting() )
+                r.adjust(1, 0, 0, 0);
+            else
+                r.adjust(0, 0, -1, 0);
+        }
+    }
+
+    if ( yInterval.borderFlags() & QwtInterval::ExcludeMinimum )
+    {
+        if ( area.top() <= yInterval.minValue() )
+        {
+            if ( yMap.isInverting() )
+                r.adjust(0, 0, 0, -1);
+            else
+                r.adjust(0, 1, 0, 0);
+        }
+    }
+
+    if ( yInterval.borderFlags() & QwtInterval::ExcludeMaximum )
+    {
+        if ( area.bottom() >= yInterval.maxValue() )
+        {
+            if ( yMap.isInverting() )
+                r.adjust(0, 1, 0, 0);
+            else
+                r.adjust(0, 0, 0, -1);
+        }
+    }
+
+    return r;
+}
+
+static QImage qwtExpandImage(const QImage &image,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &area, const QRectF &area2, const QRectF &paintRect,
+    const QwtInterval &xInterval, const QwtInterval &yInterval )
+{
+    const QRectF strippedRect = qwtStripRect(paintRect, area2,
+        xMap, yMap, xInterval, yInterval);
+    const QSize sz = strippedRect.toRect().size();
+
+    const int w = image.width();
+    const int h = image.height();
+
+    const QRectF r = QwtScaleMap::transform(xMap, yMap, area).normalized();
+    const double pw = ( r.width() - 1) / w;
+    const double ph = ( r.height() - 1) / h;
+
+    double px0, py0;
+    if ( !xMap.isInverting() )
+    {
+        px0 = xMap.transform( area2.left() );
+        px0 = qRound( px0 );
+        px0 = px0 - xMap.transform( area.left() );
+    }
+    else
+    {
+        px0 = xMap.transform( area2.right() );
+        px0 = qRound( px0 );
+        px0 -= xMap.transform( area.right() );
+
+        px0 -= 1.0;
+    }
+    px0 += strippedRect.left() - paintRect.left();
+
+    if ( !yMap.isInverting() )
+    {
+        py0 = yMap.transform( area2.top() );
+        py0 = qRound( py0 );
+        py0 -= yMap.transform( area.top() );
+    }
+    else
+    {
+        py0 = yMap.transform( area2.bottom() );
+        py0 = qRound( py0 );
+        py0 -= yMap.transform( area.bottom() );
+
+        py0 -= 1.0;
+    }
+    py0 += strippedRect.top() - paintRect.top();
+
+    QImage expanded(sz, image.format());
+
+    switch( image.depth() )
+    {
+        case 32:
+        {
+            for ( int y1 = 0; y1 < h; y1++ )
+            {
+                int yy1;
+                if ( y1 == 0 )
+                {
+                    yy1 = 0;
+                }
+                else
+                {
+                    yy1 = qRound( y1 * ph - py0 );
+                    if ( yy1 < 0 )
+                        yy1 = 0;
+                }
+
+                int yy2;
+                if ( y1 == h - 1 )
+                {
+                    yy2 = sz.height();
+                }
+                else
+                {
+                    yy2 = qRound( ( y1 + 1 ) * ph - py0 );
+                    if ( yy2 > sz.height() )
+                        yy2 = sz.height();
+                }
+
+                const quint32 *line1 = 
+                    reinterpret_cast<const quint32 *>( image.scanLine( y1 ) );
+
+                for ( int x1 = 0; x1 < w; x1++ )
+                {
+                    int xx1;
+                    if ( x1 == 0 )
+                    {
+                        xx1 = 0;
+                    }
+                    else
+                    {
+                        xx1 = qRound( x1 * pw - px0 );
+                        if ( xx1 < 0 )
+                            xx1 = 0;
+                    }
+
+                    int xx2;
+                    if ( x1 == w - 1 )
+                    {
+                        xx2 = sz.width();
+                    }
+                    else
+                    {
+                        xx2 = qRound( ( x1 + 1 ) * pw - px0 );
+                        if ( xx2 > sz.width() )
+                            xx2 = sz.width();
+                    }
+
+                    const quint32 rgb( line1[x1] );
+                    for ( int y2 = yy1; y2 < yy2; y2++ )
+                    {
+                        quint32 *line2 = reinterpret_cast<quint32 *>( 
+                            expanded.scanLine( y2 ) );
+
+                        for ( int x2 = xx1; x2 < xx2; x2++ ) 
+                            line2[x2] = rgb;
+                    }       
+                }   
+            }   
+            break;
+        }
+        case 8:
+        {
+            for ( int y1 = 0; y1 < h; y1++ )
+            {
+                int yy1;
+                if ( y1 == 0 )
+                {
+                    yy1 = 0;
+                }   
+                else
+                {
+                    yy1 = qRound( y1 * ph - py0 );
+                    if ( yy1 < 0 )
+                        yy1 = 0; 
+                }       
+                
+                int yy2;
+                if ( y1 == h - 1 )
+                {
+                    yy2 = sz.height();
+                }   
+                else
+                {
+                    yy2 = qRound( ( y1 + 1 ) * ph - py0 );
+                    if ( yy2 > sz.height() )
+                        yy2 = sz.height();
+                }
+    
+                const uchar *line1 = image.scanLine( y1 );
+
+                for ( int x1 = 0; x1 < w; x1++ )
+                {
+                    int xx1;
+                    if ( x1 == 0 )
+                    {
+                        xx1 = 0;
+                    }
+                    else
+                    {
+                        xx1 = qRound( x1 * pw - px0 );
+                        if ( xx1 < 0 )
+                            xx1 = 0;
+                    }
+
+                    int xx2;
+                    if ( x1 == w - 1 )
+                    {
+                        xx2 = sz.width();
+                    }
+                    else
+                    {
+                        xx2 = qRound( ( x1 + 1 ) * pw - px0 );
+                        if ( xx2 > sz.width() )
+                            xx2 = sz.width();
+                    }
+
+                    for ( int y2 = yy1; y2 < yy2; y2++ )
+                    {
+                        uchar *line2 = expanded.scanLine( y2 );
+                        memset( line2 + xx1, line1[x1], xx2 - xx1 );
+                    }       
+                }   
+            }
+            break;
+        }
+        default:
+            expanded = image;
+    }
+    
+    return expanded;
+}   
+
+static QRectF qwtExpandToPixels(const QRectF &rect, const QRectF &pixelRect)
+{
+    const double pw = pixelRect.width();
+    const double ph = pixelRect.height();
+
+    const double dx1 = pixelRect.left() - rect.left();
+    const double dx2 = pixelRect.right() - rect.right();
+    const double dy1 = pixelRect.top() - rect.top();
+    const double dy2 = pixelRect.bottom() - rect.bottom();
+
+    QRectF r;
+    r.setLeft( pixelRect.left() - qCeil( dx1 / pw ) * pw );
+    r.setTop( pixelRect.top() - qCeil( dy1 / ph ) * ph );
+    r.setRight( pixelRect.right() - qFloor( dx2 / pw ) * pw );
+    r.setBottom( pixelRect.bottom() - qFloor( dy2 / ph ) * ph );
+
+    return r;
+}
+
+static void qwtTransformMaps( const QTransform &tr,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    QwtScaleMap &xxMap, QwtScaleMap &yyMap )
+{
+    const QPointF p1 = tr.map( QPointF( xMap.p1(), yMap.p1() ) );
+    const QPointF p2 = tr.map( QPointF( xMap.p2(), yMap.p2() ) );
+
+    xxMap = xMap;
+    xxMap.setPaintInterval( p1.x(), p2.x() );
+
+    yyMap = yMap;
+    yyMap.setPaintInterval( p1.y(), p2.y() );
+}
+
+static void qwtAdjustMaps( QwtScaleMap &xMap, QwtScaleMap &yMap,
+    const QRectF &area, const QRectF &paintRect)
+{
+    double sx1 = area.left();
+    double sx2 = area.right();
+    if ( xMap.isInverting() )
+        qSwap(sx1, sx2);
+
+    double sy1 = area.top();
+    double sy2 = area.bottom();
+
+    if ( yMap.isInverting() )
+        qSwap(sy1, sy2);
+
+    xMap.setPaintInterval(paintRect.left(), paintRect.right());
+    xMap.setScaleInterval(sx1, sx2);
+
+    yMap.setPaintInterval(paintRect.top(), paintRect.bottom());
+    yMap.setScaleInterval(sy1, sy2);
+}
+
+static bool qwtUseCache( QwtPlotRasterItem::CachePolicy policy,
+    const QPainter *painter )
+{
+    bool doCache = false;
+
+    if ( policy == QwtPlotRasterItem::PaintCache )
+    {
+        // Caching doesn't make sense, when the item is
+        // not painted to screen
+
+        switch ( painter->paintEngine()->type() )
+        {
+            case QPaintEngine::SVG:
+            case QPaintEngine::Pdf:
+            case QPaintEngine::PostScript:
+            case QPaintEngine::MacPrinter:
+            case QPaintEngine::Picture:
+                break;
+            default:;
+                doCache = true;
+        }
+    }
+
+    return doCache;
+}
+
+static void qwtToRgba( const QImage* from, QImage* to,  
+    const QRect& tile, int alpha )
+{
+    const QRgb mask1 = qRgba( 0, 0, 0, alpha );
+    const QRgb mask2 = qRgba( 255, 255, 255, 0 );
+    const QRgb mask3 = qRgba( 0, 0, 0, 255 );
+
+    const int y0 = tile.top();
+    const int y1 = tile.bottom();
+    const int x0 = tile.left();
+    const int x1 = tile.right();
+
+    if ( from->depth() == 8 )
+    {
+        for ( int y = y0; y <= y1; y++ )
+        {
+            QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
+            const unsigned char *line = from->scanLine( y );
+
+            for ( int x = x0; x <= x1; x++ )
+                *alphaLine++ = ( from->color( *line++ ) & mask2 ) | mask1;
+        }
+    }
+    else if ( from->depth() == 32 )
+    {
+        for ( int y = y0; y <= y1; y++ )
+        {
+            QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
+            const QRgb *line = reinterpret_cast<const QRgb *>( from->scanLine( y ) );
+
+            for ( int x = x0; x <= x1; x++ )
+            {
+                const QRgb rgb = *line++;
+                if ( rgb & mask3 ) // alpha != 0
+                    *alphaLine++ = ( rgb & mask2 ) | mask1;
+                else
+                    *alphaLine++ = rgb;
+            }
+        }
+    }
+}
+
+//! Constructor
+QwtPlotRasterItem::QwtPlotRasterItem( const QString& title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Constructor
+QwtPlotRasterItem::QwtPlotRasterItem( const QwtText& title ):
+    QwtPlotItem( title )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotRasterItem::~QwtPlotRasterItem()
+{
+    delete d_data;
+}
+
+void QwtPlotRasterItem::init()
+{
+    d_data = new PrivateData();
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 8.0 );
+}
+
+/*!
+  Specify an attribute how to draw the raster item
+
+  \param attribute Paint attribute
+  \param on On/Off
+  /sa PaintAttribute, testPaintAttribute()
+*/
+void QwtPlotRasterItem::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa PaintAttribute, setPaintAttribute()
+*/
+bool QwtPlotRasterItem::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+   \brief Set an alpha value for the raster data
+
+   Often a plot has several types of raster data organized in layers.
+   ( f.e a geographical map, with weather statistics ).
+   Using setAlpha() raster items can be stacked easily.
+
+   The alpha value is a value [0, 255] to
+   control the transparency of the image. 0 represents a fully
+   transparent color, while 255 represents a fully opaque color.
+
+   \param alpha Alpha value
+
+   - alpha >= 0\n
+     All alpha values of the pixels returned by renderImage() will be set to
+     alpha, beside those with an alpha value of 0 (invalid pixels).
+   - alpha < 0
+     The alpha values returned by renderImage() are not changed.
+
+   The default alpha value is -1.
+
+   \sa alpha()
+*/
+void QwtPlotRasterItem::setAlpha( int alpha )
+{
+    if ( alpha < 0 )
+        alpha = -1;
+
+    if ( alpha > 255 )
+        alpha = 255;
+
+    if ( alpha != d_data->alpha )
+    {
+        d_data->alpha = alpha;
+
+        itemChanged();
+    }
+}
+
+/*!
+  \return Alpha value of the raster item
+  \sa setAlpha()
+*/
+int QwtPlotRasterItem::alpha() const
+{
+    return d_data->alpha;
+}
+
+/*!
+  Change the cache policy
+
+  The default policy is NoCache
+
+  \param policy Cache policy
+  \sa CachePolicy, cachePolicy()
+*/
+void QwtPlotRasterItem::setCachePolicy(
+    QwtPlotRasterItem::CachePolicy policy )
+{
+    if ( d_data->cache.policy != policy )
+    {
+        d_data->cache.policy = policy;
+
+        invalidateCache();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Cache policy
+  \sa CachePolicy, setCachePolicy()
+*/
+QwtPlotRasterItem::CachePolicy QwtPlotRasterItem::cachePolicy() const
+{
+    return d_data->cache.policy;
+}
+
+/*!
+   Invalidate the paint cache
+   \sa setCachePolicy()
+*/
+void QwtPlotRasterItem::invalidateCache()
+{
+    d_data->cache.image = QImage();
+    d_data->cache.area = QRect();
+    d_data->cache.size = QSize();
+}
+
+/*!
+   \brief Pixel hint
+
+   The geometry of a pixel is used to calculated the resolution and
+   alignment of the rendered image. 
+
+   Width and height of the hint need to be the horizontal  
+   and vertical distances between 2 neighbored points. 
+   The center of the hint has to be the position of any point 
+   ( it doesn't matter which one ).
+
+   Limiting the resolution of the image might significantly improve
+   the performance and heavily reduce the amount of memory when rendering
+   a QImage from the raster data. 
+
+   The default implementation returns an empty rectangle (QRectF()),
+   meaning, that the image will be rendered in target device ( f.e screen )
+   resolution.
+
+   \param area In most implementations the resolution of the data doesn't
+               depend on the requested area.
+
+   \return Bounding rectangle of a pixel
+
+   \sa render(), renderImage()
+*/
+QRectF QwtPlotRasterItem::pixelHint( const QRectF &area ) const
+{
+    Q_UNUSED( area );
+    return QRectF();
+}
+
+/*!
+  \brief Draw the raster data
+  \param painter Painter
+  \param xMap X-Scale Map
+  \param yMap Y-Scale Map
+  \param canvasRect Contents rectangle of the plot canvas
+*/
+void QwtPlotRasterItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( canvasRect.isEmpty() || d_data->alpha == 0 )
+        return;
+
+    const bool doCache = qwtUseCache( d_data->cache.policy, painter );
+
+    const QwtInterval xInterval = interval( Qt::XAxis );
+    const QwtInterval yInterval = interval( Qt::YAxis );
+
+    /*
+        Scaling an image always results in a loss of
+        precision/quality. So we always render the image in
+        paint device resolution.
+    */
+
+    QwtScaleMap xxMap, yyMap;
+    qwtTransformMaps( painter->transform(), xMap, yMap, xxMap, yyMap );
+
+    QRectF paintRect = painter->transform().mapRect( canvasRect );
+    QRectF area = QwtScaleMap::invTransform( xxMap, yyMap, paintRect );
+
+    const QRectF br = boundingRect();
+    if ( br.isValid() && !br.contains( area ) )
+    {
+        area &= br;
+        if ( !area.isValid() )
+            return;
+
+        paintRect = QwtScaleMap::transform( xxMap, yyMap, area );
+    }
+
+    QRectF imageRect;
+    QImage image;
+
+    QRectF pixelRect = pixelHint(area);
+    if ( !pixelRect.isEmpty() )
+    {
+        // one pixel of the target device in plot coordinates
+        const double dx = qAbs( xxMap.invTransform( 1 ) - xxMap.invTransform( 0 ) );
+        const double dy = qAbs( yyMap.invTransform( 1 ) - yyMap.invTransform( 0 ) );
+
+        if ( dx > pixelRect.width() && dy > pixelRect.height() )
+        {
+            /*
+              When the resolution of the data pixels is higher than
+              the resolution of the target device we render in
+              target device resolution.
+             */
+            pixelRect = QRectF();
+        }
+        else
+        {
+            /*
+              If only one dimension is of the data pixel is higher 
+              we expand the pixel rect to the resolution of the target device.
+             */
+
+            if ( dx > pixelRect.width() )
+                pixelRect.setWidth( dx );
+
+            if ( dy > pixelRect.height() )
+                pixelRect.setHeight( dy );
+        }
+    }
+
+    if ( pixelRect.isEmpty() )
+    {
+        if ( QwtPainter::roundingAlignment( painter ) )
+        {
+            // we want to have maps, where the boundaries of
+            // the aligned paint rectangle exactly match the area
+
+            paintRect = qwtAlignRect(paintRect);
+            qwtAdjustMaps(xxMap, yyMap, area, paintRect);
+        }
+
+        // When we have no information about position and size of
+        // data pixels we render in resolution of the paint device.
+
+        image = compose(xxMap, yyMap, 
+            area, paintRect, paintRect.size().toSize(), doCache);
+        if ( image.isNull() )
+            return;
+
+        // Remove pixels at the boundaries, when explicitly
+        // excluded in the intervals
+
+        imageRect = qwtStripRect(paintRect, area, 
+            xxMap, yyMap, xInterval, yInterval);
+
+        if ( imageRect != paintRect )
+        {
+            const QRect r( 
+                qRound( imageRect.x() - paintRect.x()),
+                qRound( imageRect.y() - paintRect.y() ),
+                qRound( imageRect.width() ),
+                qRound( imageRect.height() ) );
+                
+            image = image.copy(r);
+        }   
+    }
+    else
+    {
+        if ( QwtPainter::roundingAlignment( painter ) )
+            paintRect = qwtAlignRect(paintRect);
+
+        // align the area to the data pixels
+        QRectF imageArea = qwtExpandToPixels(area, pixelRect);
+
+        if ( imageArea.right() == xInterval.maxValue() &&
+            !( xInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
+        {
+            imageArea.adjust(0, 0, pixelRect.width(), 0);
+        }
+        if ( imageArea.bottom() == yInterval.maxValue() &&
+            !( yInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
+        {
+            imageArea.adjust(0, 0, 0, pixelRect.height() );
+        }
+
+        QSize imageSize;
+        imageSize.setWidth( qRound( imageArea.width() / pixelRect.width() ) );
+        imageSize.setHeight( qRound( imageArea.height() / pixelRect.height() ) );
+
+        image = compose(xxMap, yyMap, 
+            imageArea, paintRect, imageSize, doCache );
+
+        if ( image.isNull() )
+            return;
+
+        imageRect = qwtStripRect(paintRect, area, 
+            xxMap, yyMap, xInterval, yInterval);
+
+        if ( ( image.width() > 1 || image.height() > 1 ) &&
+            testPaintAttribute( PaintInDeviceResolution ) )
+        {
+            // Because of rounding errors the pixels 
+            // need to be expanded manually to rectangles of 
+            // different sizes
+
+            image = qwtExpandImage(image, xxMap, yyMap, 
+                imageArea, area, paintRect, xInterval, yInterval );
+        }
+    }
+
+    painter->save();
+    painter->setWorldTransform( QTransform() );
+    
+    QwtPainter::drawImage( painter, imageRect, image );
+
+    painter->restore();
+}
+
+/*!
+   \return Bounding interval for an axis
+
+   This method is intended to be reimplemented by derived classes.
+   The default implementation returns an invalid interval.
+   
+   \param axis X, Y, or Z axis
+*/
+QwtInterval QwtPlotRasterItem::interval(Qt::Axis axis) const
+{
+    Q_UNUSED( axis );
+    return QwtInterval();
+}
+
+/*!
+   \return Bounding rectangle of the data
+   \sa QwtPlotRasterItem::interval()
+*/
+QRectF QwtPlotRasterItem::boundingRect() const
+{
+    const QwtInterval intervalX = interval( Qt::XAxis );
+    const QwtInterval intervalY = interval( Qt::YAxis );
+
+    if ( !intervalX.isValid() && !intervalY.isValid() )
+        return QRectF(); // no bounding rect
+
+    QRectF r;
+
+    if ( intervalX.isValid() )
+    {
+        r.setLeft( intervalX.minValue() );
+        r.setRight( intervalX.maxValue() );
+    }
+    else
+    {
+        r.setLeft(-0.5 * FLT_MAX);
+        r.setWidth(FLT_MAX);
+    }
+
+    if ( intervalY.isValid() )
+    {
+        r.setTop( intervalY.minValue() );
+        r.setBottom( intervalY.maxValue() );
+    }
+    else
+    {
+        r.setTop(-0.5 * FLT_MAX);
+        r.setHeight(FLT_MAX);
+    }
+
+    return r.normalized();
+}
+
+QImage QwtPlotRasterItem::compose( 
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &imageArea, const QRectF &paintRect, 
+    const QSize &imageSize, bool doCache) const
+{
+    QImage image;
+    if ( imageArea.isEmpty() || paintRect.isEmpty() || imageSize.isEmpty() )
+        return image;
+
+    if ( doCache )
+    {
+        if ( !d_data->cache.image.isNull()
+            && d_data->cache.area == imageArea
+            && d_data->cache.size == paintRect.size() )
+        {
+            image = d_data->cache.image;
+        }
+    }
+
+    if ( image.isNull() )
+    {
+        double dx = 0.0;
+        if ( paintRect.toRect().width() > imageSize.width() )
+            dx = imageArea.width() / imageSize.width();
+
+        const QwtScaleMap xxMap = 
+            imageMap(Qt::Horizontal, xMap, imageArea, imageSize, dx);
+        
+        double dy = 0.0;
+        if ( paintRect.toRect().height() > imageSize.height() )
+            dy = imageArea.height() / imageSize.height();
+
+        const QwtScaleMap yyMap = 
+            imageMap(Qt::Vertical, yMap, imageArea, imageSize, dy);
+
+        image = renderImage( xxMap, yyMap, imageArea, imageSize );
+
+        if ( doCache )
+        {
+            d_data->cache.area = imageArea;
+            d_data->cache.size = paintRect.size();
+            d_data->cache.image = image;
+        }
+    }
+
+    if ( d_data->alpha >= 0 && d_data->alpha < 255 )
+    {
+        QImage alphaImage( image.size(), QImage::Format_ARGB32 );
+
+#if QT_VERSION >= 0x040400 && !defined(QT_NO_QFUTURE)
+        uint numThreads = renderThreadCount();
+
+        if ( numThreads <= 0 )
+            numThreads = QThread::idealThreadCount();
+
+        if ( numThreads <= 0 )
+            numThreads = 1;
+
+        const int numRows = image.height() / numThreads;
+
+        QList< QFuture<void> > futures;
+        for ( uint i = 0; i < numThreads; i++ )
+        {
+            QRect tile( 0, i * numRows, image.width(), numRows );
+            if ( i == numThreads - 1 )
+            {
+                tile.setHeight( image.height() - i * numRows );
+                qwtToRgba( &image, &alphaImage, tile, d_data->alpha );
+            }
+            else
+            {
+                futures += QtConcurrent::run(
+                    &qwtToRgba, &image, &alphaImage, tile, d_data->alpha );
+            }
+        }
+        for ( int i = 0; i < futures.size(); i++ )
+            futures[i].waitForFinished();
+#else
+        const QRect tile( 0, 0, image.width(), image.height() );
+        qwtToRgba( &image, &alphaImage, tile, d_data->alpha );
+#endif
+        image = alphaImage;
+    }
+
+    return image;
+}
+
+/*!
+   \brief Calculate a scale map for painting to an image
+
+   \param orientation Orientation, Qt::Horizontal means a X axis
+   \param map Scale map for rendering the plot item
+   \param area Area to be painted on the image
+   \param imageSize Image size
+   \param pixelSize Width/Height of a data pixel
+
+   \return Calculated scale map
+*/
+QwtScaleMap QwtPlotRasterItem::imageMap(
+    Qt::Orientation orientation,
+    const QwtScaleMap &map, const QRectF &area,
+    const QSize &imageSize, double pixelSize) const
+{
+    double p1, p2, s1, s2;
+
+    if ( orientation == Qt::Horizontal )
+    {
+        p1 = 0.0;
+        p2 = imageSize.width();
+        s1 = area.left();
+        s2 = area.right();
+    }
+    else
+    {
+        p1 = 0.0;
+        p2 = imageSize.height();
+        s1 = area.top();
+        s2 = area.bottom();
+    }
+
+    if ( pixelSize > 0.0 )
+    {
+        double off = 0.5 * pixelSize;
+        if ( map.isInverting() )
+            off = -off;
+
+        s1 += off;
+        s2 += off;
+    }
+    else
+    {
+        p2--;
+    }
+
+    if ( map.isInverting() && ( s1 < s2 ) )
+        qSwap( s1, s2 );
+
+    QwtScaleMap newMap = map;
+    newMap.setPaintInterval( p1, p2 );
+    newMap.setScaleInterval( s1, s2 );
+
+    return newMap;
+}
diff --git a/source/third_party/qwt/qwt_plot_renderer.cpp b/source/third_party/qwt/qwt_plot_renderer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ae59625df0150ec0b215a18d7e14deb87889527f
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_renderer.cpp
@@ -0,0 +1,1014 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_renderer.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_plot_layout.h"
+#include "qwt/qwt_abstract_legend.h"
+#include "qwt/qwt_scale_widget.h"
+#include "qwt/qwt_scale_engine.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_text_label.h"
+#include "qwt/qwt_math.h"
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qtransform.h>
+#include <qfiledialog.h>
+#include <qfileinfo.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qimagewriter.h>
+#include <QtPrintSupport/qprinter.h>
+#include <QtPrintSupport/qprintdialog.h>
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+#include <qsvggenerator.h>
+#endif
+#endif
+
+static QPainterPath qwtCanvasClip( 
+    const QWidget* canvas, const QRectF &canvasRect )
+{
+    // The clip region is calculated in integers
+    // To avoid too much rounding errors better
+    // calculate it in target device resolution
+
+    int x1 = qCeil( canvasRect.left() );
+    int x2 = qFloor( canvasRect.right() );
+    int y1 = qCeil( canvasRect.top() );
+    int y2 = qFloor( canvasRect.bottom() );
+
+    const QRect r( x1, y1, x2 - x1 - 1, y2 - y1 - 1 );
+
+    QPainterPath clipPath;
+
+    ( void ) QMetaObject::invokeMethod(
+        const_cast< QWidget *>( canvas ), "borderPath",
+        Qt::DirectConnection,
+        Q_RETURN_ARG( QPainterPath, clipPath ), Q_ARG( QRect, r ) );
+
+    return clipPath;
+}
+
+class QwtPlotRenderer::PrivateData
+{
+public:
+    PrivateData():
+        discardFlags( QwtPlotRenderer::DiscardNone ),
+        layoutFlags( QwtPlotRenderer::DefaultLayout )
+    {
+    }
+
+    QwtPlotRenderer::DiscardFlags discardFlags;
+    QwtPlotRenderer::LayoutFlags layoutFlags;
+};
+
+/*! 
+   Constructor
+   \param parent Parent object
+*/
+QwtPlotRenderer::QwtPlotRenderer( QObject *parent ):
+    QObject( parent )
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtPlotRenderer::~QwtPlotRenderer()
+{
+    delete d_data;
+}
+
+/*!
+  Change a flag, indicating what to discard from rendering
+
+  \param flag Flag to change
+  \param on On/Off
+
+  \sa DiscardFlag, testDiscardFlag(), setDiscardFlags(), discardFlags()
+*/
+void QwtPlotRenderer::setDiscardFlag( DiscardFlag flag, bool on )
+{
+    if ( on )
+        d_data->discardFlags |= flag;
+    else
+        d_data->discardFlags &= ~flag;
+}
+
+/*!
+  \return True, if flag is enabled.
+  \param flag Flag to be tested
+  \sa DiscardFlag, setDiscardFlag(), setDiscardFlags(), discardFlags()
+*/
+bool QwtPlotRenderer::testDiscardFlag( DiscardFlag flag ) const
+{
+    return d_data->discardFlags & flag;
+}
+
+/*!
+  Set the flags, indicating what to discard from rendering
+
+  \param flags Flags
+  \sa DiscardFlag, setDiscardFlag(), testDiscardFlag(), discardFlags()
+*/
+void QwtPlotRenderer::setDiscardFlags( DiscardFlags flags )
+{
+    d_data->discardFlags = flags;
+}
+
+/*!
+  \return Flags, indicating what to discard from rendering
+  \sa DiscardFlag, setDiscardFlags(), setDiscardFlag(), testDiscardFlag()
+*/
+QwtPlotRenderer::DiscardFlags QwtPlotRenderer::discardFlags() const
+{
+    return d_data->discardFlags;
+}
+
+/*!
+  Change a layout flag
+
+  \param flag Flag to change
+  \param on On/Off
+
+  \sa LayoutFlag, testLayoutFlag(), setLayoutFlags(), layoutFlags()
+*/
+void QwtPlotRenderer::setLayoutFlag( LayoutFlag flag, bool on )
+{
+    if ( on )
+        d_data->layoutFlags |= flag;
+    else
+        d_data->layoutFlags &= ~flag;
+}
+
+/*!
+  \return True, if flag is enabled.
+  \param flag Flag to be tested
+  \sa LayoutFlag, setLayoutFlag(), setLayoutFlags(), layoutFlags()
+*/
+bool QwtPlotRenderer::testLayoutFlag( LayoutFlag flag ) const
+{
+    return d_data->layoutFlags & flag;
+}
+
+/*!
+  Set the layout flags
+
+  \param flags Flags
+  \sa LayoutFlag, setLayoutFlag(), testLayoutFlag(), layoutFlags()
+*/
+void QwtPlotRenderer::setLayoutFlags( LayoutFlags flags )
+{
+    d_data->layoutFlags = flags;
+}
+
+/*!
+  \return Layout flags
+  \sa LayoutFlag, setLayoutFlags(), setLayoutFlag(), testLayoutFlag()
+*/
+QwtPlotRenderer::LayoutFlags QwtPlotRenderer::layoutFlags() const
+{
+    return d_data->layoutFlags;
+}
+
+/*!
+  Render a plot to a file
+
+  The format of the document will be auto-detected from the
+  suffix of the file name.
+
+  \param plot Plot widget
+  \param fileName Path of the file, where the document will be stored
+  \param sizeMM Size for the document in millimeters.
+  \param resolution Resolution in dots per Inch (dpi)
+*/
+void QwtPlotRenderer::renderDocument( QwtPlot *plot,
+    const QString &fileName, const QSizeF &sizeMM, int resolution )
+{
+    renderDocument( plot, fileName,
+        QFileInfo( fileName ).suffix(), sizeMM, resolution );
+}
+
+/*!
+  Render a plot to a file
+
+  Supported formats are:
+
+  - pdf\n
+    Portable Document Format PDF
+  - ps\n
+    Postcript
+  - svg\n
+    Scalable Vector Graphics SVG
+  - all image formats supported by Qt\n
+    see QImageWriter::supportedImageFormats()
+
+  Scalable vector graphic formats like PDF or SVG are superior to
+  raster graphics formats.
+
+  \param plot Plot widget
+  \param fileName Path of the file, where the document will be stored
+  \param format Format for the document
+  \param sizeMM Size for the document in millimeters.
+  \param resolution Resolution in dots per Inch (dpi)
+
+  \sa renderTo(), render(), QwtPainter::setRoundingAlignment()
+*/
+void QwtPlotRenderer::renderDocument( QwtPlot *plot,
+    const QString &fileName, const QString &format,
+    const QSizeF &sizeMM, int resolution )
+{
+    if ( plot == NULL || sizeMM.isEmpty() || resolution <= 0 )
+        return;
+
+    QString title = plot->title().text();
+    if ( title.isEmpty() )
+        title = "Plot Document";
+
+    const double mmToInch = 1.0 / 25.4;
+    const QSizeF size = sizeMM * mmToInch * resolution;
+
+    const QRectF documentRect( 0.0, 0.0, size.width(), size.height() );
+
+    const QString fmt = format.toLower();
+    if ( fmt == "pdf" )
+    {
+#ifndef QT_NO_PRINTER
+        QPrinter printer;
+        printer.setOutputFormat( QPrinter::PdfFormat );
+        printer.setColorMode( QPrinter::Color );
+        printer.setFullPage( true );
+        printer.setPaperSize( sizeMM, QPrinter::Millimeter );
+        printer.setDocName( title );
+        printer.setOutputFileName( fileName );
+        printer.setResolution( resolution );
+
+        QPainter painter( &printer );
+        render( plot, &painter, documentRect );
+#endif
+    }
+    else if ( fmt == "ps" )
+    {
+#if QT_VERSION < 0x050000
+#ifndef QT_NO_PRINTER
+        QPrinter printer;
+        printer.setOutputFormat( QPrinter::PostScriptFormat );
+        printer.setColorMode( QPrinter::Color );
+        printer.setFullPage( true );
+        printer.setPaperSize( sizeMM, QPrinter::Millimeter );
+        printer.setDocName( title );
+        printer.setOutputFileName( fileName );
+        printer.setResolution( resolution );
+
+        QPainter painter( &printer );
+        render( plot, &painter, documentRect );
+#endif
+#endif
+    }
+    else if ( fmt == "svg" )
+    {
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+#if QT_VERSION >= 0x040500
+        QSvgGenerator generator;
+        generator.setTitle( title );
+        generator.setFileName( fileName );
+        generator.setResolution( resolution );
+        generator.setViewBox( documentRect );
+
+        QPainter painter( &generator );
+        render( plot, &painter, documentRect );
+#endif
+#endif
+#endif
+    }
+    else
+    {
+        if ( QImageWriter::supportedImageFormats().indexOf(
+            format.toLatin1() ) >= 0 )
+        {
+            const QRect imageRect = documentRect.toRect();
+            const int dotsPerMeter = qRound( resolution * mmToInch * 1000.0 );
+
+            QImage image( imageRect.size(), QImage::Format_ARGB32 );
+            image.setDotsPerMeterX( dotsPerMeter );
+            image.setDotsPerMeterY( dotsPerMeter );
+            image.fill( QColor( Qt::white ).rgb() );
+
+            QPainter painter( &image );
+            render( plot, &painter, imageRect );
+            painter.end();
+
+            image.save( fileName, format.toLatin1() );
+        }
+    }
+}
+
+/*!
+  \brief Render the plot to a \c QPaintDevice
+
+  This function renders the contents of a QwtPlot instance to
+  \c QPaintDevice object. The target rectangle is derived from
+  its device metrics.
+
+  \param plot Plot to be rendered
+  \param paintDevice device to paint on, f.e a QImage
+
+  \sa renderDocument(), render(), QwtPainter::setRoundingAlignment()
+*/
+
+void QwtPlotRenderer::renderTo(
+    QwtPlot *plot, QPaintDevice &paintDevice ) const
+{
+    int w = paintDevice.width();
+    int h = paintDevice.height();
+
+    QPainter p( &paintDevice );
+    render( plot, &p, QRectF( 0, 0, w, h ) );
+}
+
+/*!
+  \brief Render the plot to a QPrinter
+
+  This function renders the contents of a QwtPlot instance to
+  \c QPaintDevice object. The size is derived from the printer
+  metrics.
+
+  \param plot Plot to be rendered
+  \param printer Printer to paint on
+
+  \sa renderDocument(), render(), QwtPainter::setRoundingAlignment()
+*/
+
+#ifndef QT_NO_PRINTER
+
+void QwtPlotRenderer::renderTo(
+    QwtPlot *plot, QPrinter &printer ) const
+{
+    int w = printer.width();
+    int h = printer.height();
+
+    QRectF rect( 0, 0, w, h );
+    double aspect = rect.width() / rect.height();
+    if ( ( aspect < 1.0 ) )
+        rect.setHeight( aspect * rect.width() );
+
+    QPainter p( &printer );
+    render( plot, &p, rect );
+}
+
+#endif
+
+#ifndef QWT_NO_SVG
+#ifdef QT_SVG_LIB
+#if QT_VERSION >= 0x040500
+
+/*!
+  \brief Render the plot to a QSvgGenerator
+
+  If the generator has a view box, the plot will be rendered into it.
+  If it has no viewBox but a valid size the target coordinates
+  will be (0, 0, generator.width(), generator.height()). Otherwise
+  the target rectangle will be QRectF(0, 0, 800, 600);
+
+  \param plot Plot to be rendered
+  \param generator SVG generator
+*/
+void QwtPlotRenderer::renderTo(
+    QwtPlot *plot, QSvgGenerator &generator ) const
+{
+    QRectF rect = generator.viewBoxF();
+    if ( rect.isEmpty() )
+        rect.setRect( 0, 0, generator.width(), generator.height() );
+
+    if ( rect.isEmpty() )
+        rect.setRect( 0, 0, 800, 600 ); // something
+
+    QPainter p( &generator );
+    render( plot, &p, rect );
+}
+#endif
+#endif
+#endif
+
+/*!
+  Paint the contents of a QwtPlot instance into a given rectangle.
+
+  \param plot Plot to be rendered
+  \param painter Painter
+  \param plotRect Bounding rectangle
+
+  \sa renderDocument(), renderTo(), QwtPainter::setRoundingAlignment()
+*/
+void QwtPlotRenderer::render( QwtPlot *plot,
+    QPainter *painter, const QRectF &plotRect ) const
+{
+    if ( painter == 0 || !painter->isActive() ||
+            !plotRect.isValid() || plot->size().isNull() )
+    {
+        return;
+    }
+
+    if ( !( d_data->discardFlags & DiscardBackground ) )
+        QwtPainter::drawBackgound( painter, plotRect, plot );
+
+    /*
+      The layout engine uses the same methods as they are used
+      by the Qt layout system. Therefore we need to calculate the
+      layout in screen coordinates and paint with a scaled painter.
+     */
+    QTransform transform;
+    transform.scale(
+        double( painter->device()->logicalDpiX() ) / plot->logicalDpiX(),
+        double( painter->device()->logicalDpiY() ) / plot->logicalDpiY() );
+
+    QRectF layoutRect = transform.inverted().mapRect( plotRect );
+
+    if ( !( d_data->discardFlags & DiscardBackground ) )
+    {
+        // subtract the contents margins
+
+        int left, top, right, bottom;
+        plot->getContentsMargins( &left, &top, &right, &bottom );
+        layoutRect.adjust( left, top, -right, -bottom );
+    }
+
+    QwtPlotLayout *layout = plot->plotLayout();
+
+    int baseLineDists[QwtPlot::axisCnt];
+    int canvasMargins[QwtPlot::axisCnt];
+
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        canvasMargins[ axisId ] = layout->canvasMargin( axisId );
+
+        if ( d_data->layoutFlags & FrameWithScales )
+        {
+            QwtScaleWidget *scaleWidget = plot->axisWidget( axisId );
+            if ( scaleWidget )
+            {
+                baseLineDists[axisId] = scaleWidget->margin();
+                scaleWidget->setMargin( 0 );
+            }
+
+            if ( !plot->axisEnabled( axisId ) )
+            {
+                int left = 0;
+                int right = 0;
+                int top = 0;
+                int bottom = 0;
+
+                // When we have a scale the frame is painted on
+                // the position of the backbone - otherwise we
+                // need to introduce a margin around the canvas
+
+                switch( axisId )
+                {
+                    case QwtPlot::yLeft:
+                        layoutRect.adjust( 1, 0, 0, 0 );
+                        break;
+                    case QwtPlot::yRight:
+                        layoutRect.adjust( 0, 0, -1, 0 );
+                        break;
+                    case QwtPlot::xTop:
+                        layoutRect.adjust( 0, 1, 0, 0 );
+                        break;
+                    case QwtPlot::xBottom:
+                        layoutRect.adjust( 0, 0, 0, -1 );
+                        break;
+                    default:
+                        break;
+                }
+                layoutRect.adjust( left, top, right, bottom );
+            }
+        }
+    }
+
+    // Calculate the layout for the document.
+
+    QwtPlotLayout::Options layoutOptions = QwtPlotLayout::IgnoreScrollbars;
+
+    if ( ( d_data->layoutFlags & FrameWithScales ) ||
+        ( d_data->discardFlags & DiscardCanvasFrame ) )
+    {
+        layoutOptions |= QwtPlotLayout::IgnoreFrames;
+    } 
+
+
+    if ( d_data->discardFlags & DiscardLegend )
+        layoutOptions |= QwtPlotLayout::IgnoreLegend;
+
+    if ( d_data->discardFlags & DiscardTitle )
+        layoutOptions |= QwtPlotLayout::IgnoreTitle;
+
+    if ( d_data->discardFlags & DiscardFooter )
+        layoutOptions |= QwtPlotLayout::IgnoreFooter;
+
+    layout->activate( plot, layoutRect, layoutOptions );
+
+    // canvas
+
+    QwtScaleMap maps[QwtPlot::axisCnt];
+    buildCanvasMaps( plot, layout->canvasRect(), maps );
+    if ( updateCanvasMargins( plot, layout->canvasRect(), maps ) )
+    {
+        // recalculate maps and layout, when the margins
+        // have been changed
+
+        layout->activate( plot, layoutRect, layoutOptions );
+        buildCanvasMaps( plot, layout->canvasRect(), maps );
+    }
+
+    // now start painting
+
+    painter->save();
+    painter->setWorldTransform( transform, true );
+
+    renderCanvas( plot, painter, layout->canvasRect(), maps );
+
+    if ( !( d_data->discardFlags & DiscardTitle )
+        && ( !plot->titleLabel()->text().isEmpty() ) )
+    {
+        renderTitle( plot, painter, layout->titleRect() );
+    }
+
+    if ( !( d_data->discardFlags & DiscardFooter )
+        && ( !plot->footerLabel()->text().isEmpty() ) )
+    {
+        renderFooter( plot, painter, layout->footerRect() );
+    }
+
+    if ( !( d_data->discardFlags & DiscardLegend )
+        && plot->legend() && !plot->legend()->isEmpty() )
+    {
+        renderLegend( plot, painter, layout->legendRect() );
+    }
+
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        QwtScaleWidget *scaleWidget = plot->axisWidget( axisId );
+        if ( scaleWidget )
+        {
+            int baseDist = scaleWidget->margin();
+
+            int startDist, endDist;
+            scaleWidget->getBorderDistHint( startDist, endDist );
+
+            renderScale( plot, painter, axisId, startDist, endDist,
+                baseDist, layout->scaleRect( axisId ) );
+        }
+    }
+
+    painter->restore();
+
+    // restore all setting to their original attributes.
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        if ( d_data->layoutFlags & FrameWithScales )
+        {
+            QwtScaleWidget *scaleWidget = plot->axisWidget( axisId );
+            if ( scaleWidget  )
+                scaleWidget->setMargin( baseLineDists[axisId] );
+        }
+
+        layout->setCanvasMargin( canvasMargins[axisId] );
+    }
+
+    layout->invalidate();
+
+}
+
+/*!
+  Render the title into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param rect Bounding rectangle
+*/
+void QwtPlotRenderer::renderTitle( const QwtPlot *plot,
+    QPainter *painter, const QRectF &rect ) const
+{
+    painter->setFont( plot->titleLabel()->font() );
+
+    const QColor color = plot->titleLabel()->palette().color(
+            QPalette::Active, QPalette::Text );
+
+    painter->setPen( color );
+    plot->titleLabel()->text().draw( painter, rect );
+}
+
+/*!
+  Render the footer into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param rect Bounding rectangle
+*/
+void QwtPlotRenderer::renderFooter( const QwtPlot *plot,
+    QPainter *painter, const QRectF &rect ) const
+{
+    painter->setFont( plot->footerLabel()->font() );
+
+    const QColor color = plot->footerLabel()->palette().color(
+            QPalette::Active, QPalette::Text );
+
+    painter->setPen( color );
+    plot->footerLabel()->text().draw( painter, rect );
+}
+
+
+/*!
+  Render the legend into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param rect Bounding rectangle
+*/
+void QwtPlotRenderer::renderLegend( const QwtPlot *plot,
+    QPainter *painter, const QRectF &rect ) const
+{
+    if ( plot->legend() )
+    {
+        bool fillBackground = !( d_data->discardFlags & DiscardBackground );
+        plot->legend()->renderLegend( painter, rect, fillBackground );
+    }
+}
+
+/*!
+  \brief Paint a scale into a given rectangle.
+  Paint the scale into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param axisId Axis
+  \param startDist Start border distance
+  \param endDist End border distance
+  \param baseDist Base distance
+  \param rect Bounding rectangle
+*/
+void QwtPlotRenderer::renderScale( const QwtPlot *plot,
+    QPainter *painter,
+    int axisId, int startDist, int endDist, int baseDist,
+    const QRectF &rect ) const
+{
+    if ( !plot->axisEnabled( axisId ) )
+        return;
+
+    const QwtScaleWidget *scaleWidget = plot->axisWidget( axisId );
+    if ( scaleWidget->isColorBarEnabled()
+        && scaleWidget->colorBarWidth() > 0 )
+    {
+        scaleWidget->drawColorBar( painter, scaleWidget->colorBarRect( rect ) );
+        baseDist += scaleWidget->colorBarWidth() + scaleWidget->spacing();
+    }
+
+    painter->save();
+
+    QwtScaleDraw::Alignment align;
+    double x, y, w;
+
+    switch ( axisId )
+    {
+        case QwtPlot::yLeft:
+        {
+            x = rect.right() - 1.0 - baseDist;
+            y = rect.y() + startDist;
+            w = rect.height() - startDist - endDist;
+            align = QwtScaleDraw::LeftScale;
+            break;
+        }
+        case QwtPlot::yRight:
+        {
+            x = rect.left() + baseDist;
+            y = rect.y() + startDist;
+            w = rect.height() - startDist - endDist;
+            align = QwtScaleDraw::RightScale;
+            break;
+        }
+        case QwtPlot::xTop:
+        {
+            x = rect.left() + startDist;
+            y = rect.bottom() - 1.0 - baseDist;
+            w = rect.width() - startDist - endDist;
+            align = QwtScaleDraw::TopScale;
+            break;
+        }
+        case QwtPlot::xBottom:
+        {
+            x = rect.left() + startDist;
+            y = rect.top() + baseDist;
+            w = rect.width() - startDist - endDist;
+            align = QwtScaleDraw::BottomScale;
+            break;
+        }
+        default:
+            return;
+    }
+
+    scaleWidget->drawTitle( painter, align, rect );
+
+    painter->setFont( scaleWidget->font() );
+
+    QwtScaleDraw *sd = const_cast<QwtScaleDraw *>( scaleWidget->scaleDraw() );
+    const QPointF sdPos = sd->pos();
+    const double sdLength = sd->length();
+
+    sd->move( x, y );
+    sd->setLength( w );
+
+    QPalette palette = scaleWidget->palette();
+    palette.setCurrentColorGroup( QPalette::Active );
+    sd->draw( painter, palette );
+
+    // reset previous values
+    sd->move( sdPos );
+    sd->setLength( sdLength );
+
+    painter->restore();
+}
+
+/*!
+  Render the canvas into a given rectangle.
+
+  \param plot Plot widget
+  \param painter Painter
+  \param map Maps mapping between plot and paint device coordinates
+  \param canvasRect Canvas rectangle
+*/
+void QwtPlotRenderer::renderCanvas( const QwtPlot *plot,
+    QPainter *painter, const QRectF &canvasRect, 
+    const QwtScaleMap *map ) const
+{
+    const QWidget *canvas = plot->canvas();
+
+    QRectF r = canvasRect.adjusted( 0.0, 0.0, -1.0, -1.0 );
+
+    if ( d_data->layoutFlags & FrameWithScales )
+    {
+        painter->save();
+
+        r.adjust( -1.0, -1.0, 1.0, 1.0 );
+        painter->setPen( QPen( Qt::black ) );
+
+        if ( !( d_data->discardFlags & DiscardCanvasBackground ) )
+        {
+            const QBrush bgBrush =
+                canvas->palette().brush( plot->backgroundRole() );
+            painter->setBrush( bgBrush );
+        }
+
+        QwtPainter::drawRect( painter, r );
+
+        painter->restore();
+        painter->save();
+
+        painter->setClipRect( canvasRect );
+        plot->drawItems( painter, canvasRect, map );
+
+        painter->restore();
+    }
+    else if ( canvas->testAttribute( Qt::WA_StyledBackground ) )
+    {
+        QPainterPath clipPath;
+
+        painter->save();
+
+        if ( !( d_data->discardFlags & DiscardCanvasBackground ) )
+        {
+            QwtPainter::drawBackgound( painter, r, canvas );
+            clipPath = qwtCanvasClip( canvas, canvasRect );
+        }
+
+        painter->restore();
+        painter->save();
+
+        if ( clipPath.isEmpty() )
+            painter->setClipRect( canvasRect );
+        else
+            painter->setClipPath( clipPath );
+
+        plot->drawItems( painter, canvasRect, map );
+
+        painter->restore();
+    }
+    else
+    {
+        QPainterPath clipPath;
+
+        int frameWidth = 0;
+
+        if ( !( d_data->discardFlags & DiscardCanvasFrame ) )
+        {
+            const QVariant fw = canvas->property( "frameWidth" );
+            if ( fw.type() == QVariant::Int )
+                frameWidth = fw.toInt();
+
+            clipPath = qwtCanvasClip( canvas, canvasRect );
+        }
+
+        QRectF innerRect = canvasRect.adjusted( 
+            frameWidth, frameWidth, -frameWidth, -frameWidth );
+
+        painter->save();
+
+        if ( clipPath.isEmpty() )
+        {
+            painter->setClipRect( innerRect );
+        }
+        else
+        {
+            painter->setClipPath( clipPath );
+        }
+
+        if ( !( d_data->discardFlags & DiscardCanvasBackground ) )
+        {
+            QwtPainter::drawBackgound( painter, innerRect, canvas );
+        }
+
+        plot->drawItems( painter, innerRect, map );
+
+        painter->restore();
+
+        if ( frameWidth > 0 )
+        {
+            painter->save();
+
+            const int frameStyle =
+                canvas->property( "frameShadow" ).toInt() |
+                canvas->property( "frameShape" ).toInt();
+
+            const int frameWidth = canvas->property( "frameWidth" ).toInt();
+
+
+            const QVariant borderRadius = canvas->property( "borderRadius" );
+            if ( borderRadius.type() == QVariant::Double 
+                && borderRadius.toDouble() > 0.0 )
+            {
+                const double r = borderRadius.toDouble();
+
+                QwtPainter::drawRoundedFrame( painter, canvasRect,
+                    r, r, canvas->palette(), frameWidth, frameStyle );
+            }
+            else
+            {
+                const int midLineWidth = canvas->property( "midLineWidth" ).toInt();
+
+                QwtPainter::drawFrame( painter, canvasRect,
+                    canvas->palette(), canvas->foregroundRole(),
+                    frameWidth, midLineWidth, frameStyle );
+            }
+            painter->restore();
+        }
+    }
+}
+
+/*!
+   Calculated the scale maps for rendering the canvas
+
+   \param plot Plot widget
+   \param canvasRect Target rectangle
+   \param maps Scale maps to be calculated
+*/
+void QwtPlotRenderer::buildCanvasMaps( const QwtPlot *plot,
+    const QRectF &canvasRect, QwtScaleMap maps[] ) const
+{
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        maps[axisId].setTransformation(
+            plot->axisScaleEngine( axisId )->transformation() );
+
+        const QwtScaleDiv &scaleDiv = plot->axisScaleDiv( axisId );
+        maps[axisId].setScaleInterval(
+            scaleDiv.lowerBound(), scaleDiv.upperBound() );
+
+        double from, to;
+        if ( plot->axisEnabled( axisId ) )
+        {
+            const int sDist = plot->axisWidget( axisId )->startBorderDist();
+            const int eDist = plot->axisWidget( axisId )->endBorderDist();
+            const QRectF scaleRect = plot->plotLayout()->scaleRect( axisId );
+
+            if ( axisId == QwtPlot::xTop || axisId == QwtPlot::xBottom )
+            {
+                from = scaleRect.left() + sDist;
+                to = scaleRect.right() - eDist;
+            }
+            else
+            {
+                from = scaleRect.bottom() - eDist;
+                to = scaleRect.top() + sDist;
+            }
+        }
+        else
+        {
+            int margin = 0;
+            if ( !plot->plotLayout()->alignCanvasToScale( axisId ) )
+                margin = plot->plotLayout()->canvasMargin( axisId );
+
+            if ( axisId == QwtPlot::yLeft || axisId == QwtPlot::yRight )
+            {
+                from = canvasRect.bottom() - margin;
+                to = canvasRect.top() + margin;
+            }
+            else
+            {
+                from = canvasRect.left() + margin;
+                to = canvasRect.right() - margin;
+            }
+        }
+        maps[axisId].setPaintInterval( from, to );
+    }
+}
+
+bool QwtPlotRenderer::updateCanvasMargins( QwtPlot *plot,
+    const QRectF &canvasRect, const QwtScaleMap maps[] ) const
+{
+    double margins[QwtPlot::axisCnt];
+    plot->getCanvasMarginsHint( maps, canvasRect,
+        margins[QwtPlot::yLeft], margins[QwtPlot::xTop], 
+        margins[QwtPlot::yRight], margins[QwtPlot::xBottom] );
+
+    bool marginsChanged = false;
+    for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ )
+    {
+        if ( margins[axisId] >= 0.0 )
+        {
+            const int m = qCeil( margins[axisId] );
+            plot->plotLayout()->setCanvasMargin( m, axisId);
+            marginsChanged = true;
+        }
+    }
+
+    return marginsChanged;
+}
+
+/*!
+   \brief Execute a file dialog and render the plot to the selected file
+
+   \param plot Plot widget
+   \param documentName Default document name
+   \param sizeMM Size for the document in millimeters.
+   \param resolution Resolution in dots per Inch (dpi)
+
+   \return True, when exporting was successful
+   \sa renderDocument()
+*/
+bool QwtPlotRenderer::exportTo( QwtPlot *plot, const QString &documentName,
+     const QSizeF &sizeMM, int resolution )
+{       
+    if ( plot == NULL )
+        return false;
+    
+    QString fileName = documentName;
+
+    // What about translation 
+
+#ifndef QT_NO_FILEDIALOG
+    const QList<QByteArray> imageFormats =
+        QImageWriter::supportedImageFormats();
+        
+    QStringList filter;
+#ifndef QT_NO_PRINTER
+    filter += QString( "PDF " ) + tr( "Documents" ) + " (*.pdf)";
+#endif
+#ifndef QWT_NO_SVG 
+    filter += QString( "SVG " ) + tr( "Documents" ) + " (*.svg)";
+#endif
+#ifndef QT_NO_PRINTER
+    filter += QString( "Postscript " ) + tr( "Documents" ) + " (*.ps)";
+#endif
+    
+    if ( imageFormats.size() > 0 )
+    {
+        QString imageFilter( tr( "Images" ) );
+        imageFilter += " (";
+        for ( int i = 0; i < imageFormats.size(); i++ )
+        {
+            if ( i > 0 )
+                imageFilter += " ";
+            imageFilter += "*."; 
+            imageFilter += imageFormats[i];
+        }   
+        imageFilter += ")";
+        
+        filter += imageFilter;
+    }   
+    
+    fileName = QFileDialog::getSaveFileName(
+        NULL, tr( "Export File Name" ), fileName,
+        filter.join( ";;" ), NULL, QFileDialog::DontConfirmOverwrite );
+#endif  
+    if ( fileName.isEmpty() )
+        return false;
+
+    renderDocument( plot, fileName, sizeMM, resolution );
+
+    return true;
+}   
diff --git a/source/third_party/qwt/qwt_plot_rescaler.cpp b/source/third_party/qwt/qwt_plot_rescaler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d0d7ff15ab56cac690a8b8c4e8ab4e9934d4ccf6
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_rescaler.cpp
@@ -0,0 +1,631 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_rescaler.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_interval.h"
+#include "qwt/qwt_plot_canvas.h"
+#include <qevent.h>
+#include <qalgorithms.h>
+
+class QwtPlotRescaler::AxisData
+{
+public:
+    AxisData():
+        aspectRatio( 1.0 ),
+        expandingDirection( QwtPlotRescaler::ExpandUp )
+    {
+    }
+
+    double aspectRatio;
+    QwtInterval intervalHint;
+    QwtPlotRescaler::ExpandingDirection expandingDirection;
+    mutable QwtScaleDiv scaleDiv;
+};
+
+class QwtPlotRescaler::PrivateData
+{
+public:
+    PrivateData():
+        referenceAxis( QwtPlot::xBottom ),
+        rescalePolicy( QwtPlotRescaler::Expanding ),
+        isEnabled( false ),
+        inReplot( 0 )
+    {
+    }
+
+    int referenceAxis;
+    RescalePolicy rescalePolicy;
+    QwtPlotRescaler::AxisData axisData[QwtPlot::axisCnt];
+    bool isEnabled;
+
+    mutable int inReplot;
+};
+
+/*!
+   Constructor
+
+   \param canvas Canvas
+   \param referenceAxis Reference axis, see RescalePolicy
+   \param policy Rescale policy
+
+   \sa setRescalePolicy(), setReferenceAxis()
+*/
+QwtPlotRescaler::QwtPlotRescaler( QWidget *canvas,
+        int referenceAxis, RescalePolicy policy ):
+    QObject( canvas )
+{
+    d_data = new PrivateData;
+    d_data->referenceAxis = referenceAxis;
+    d_data->rescalePolicy = policy;
+
+    setEnabled( true );
+}
+
+//! Destructor
+QwtPlotRescaler::~QwtPlotRescaler()
+{
+    delete d_data;
+}
+
+/*!
+  \brief En/disable the rescaler
+
+  When enabled is true an event filter is installed for
+  the canvas, otherwise the event filter is removed.
+
+  \param on true or false
+  \sa isEnabled(), eventFilter()
+*/
+void QwtPlotRescaler::setEnabled( bool on )
+{
+    if ( d_data->isEnabled != on )
+    {
+        d_data->isEnabled = on;
+
+        QWidget *w = canvas();
+        if ( w )
+        {
+            if ( d_data->isEnabled )
+                w->installEventFilter( this );
+            else
+                w->removeEventFilter( this );
+        }
+    }
+}
+
+/*!
+  \return true when enabled, false otherwise
+  \sa setEnabled, eventFilter()
+*/
+bool QwtPlotRescaler::isEnabled() const
+{
+    return d_data->isEnabled;
+}
+
+/*!
+  Change the rescale policy
+
+  \param policy Rescale policy
+  \sa rescalePolicy()
+*/
+void QwtPlotRescaler::setRescalePolicy( RescalePolicy policy )
+{
+    d_data->rescalePolicy = policy;
+}
+
+/*!
+  \return Rescale policy
+  \sa setRescalePolicy()
+*/
+QwtPlotRescaler::RescalePolicy QwtPlotRescaler::rescalePolicy() const
+{
+    return d_data->rescalePolicy;
+}
+
+/*!
+  Set the reference axis ( see RescalePolicy )
+
+  \param axis Axis index ( QwtPlot::Axis )
+  \sa referenceAxis()
+*/
+void QwtPlotRescaler::setReferenceAxis( int axis )
+{
+    d_data->referenceAxis = axis;
+}
+
+/*!
+  \return Reference axis ( see RescalePolicy )
+  \sa setReferenceAxis()
+*/
+int QwtPlotRescaler::referenceAxis() const
+{
+    return d_data->referenceAxis;
+}
+
+/*!
+  Set the direction in which all axis should be expanded
+
+  \param direction Direction
+  \sa expandingDirection()
+*/
+void QwtPlotRescaler::setExpandingDirection(
+    ExpandingDirection direction )
+{
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        setExpandingDirection( axis, direction );
+}
+
+/*!
+  Set the direction in which an axis should be expanded
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \param direction Direction
+  \sa expandingDirection()
+*/
+void QwtPlotRescaler::setExpandingDirection(
+    int axis, ExpandingDirection direction )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->axisData[axis].expandingDirection = direction;
+}
+
+/*!
+  \return Direction in which an axis should be expanded
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \sa setExpandingDirection()
+*/
+QwtPlotRescaler::ExpandingDirection
+QwtPlotRescaler::expandingDirection( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->axisData[axis].expandingDirection;
+
+    return ExpandBoth;
+}
+
+/*!
+  Set the aspect ratio between the scale of the reference axis
+  and the other scales. The default ratio is 1.0
+
+  \param ratio Aspect ratio
+  \sa aspectRatio()
+*/
+void QwtPlotRescaler::setAspectRatio( double ratio )
+{
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        setAspectRatio( axis, ratio );
+}
+
+/*!
+  Set the aspect ratio between the scale of the reference axis
+  and another scale. The default ratio is 1.0
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \param ratio Aspect ratio
+  \sa aspectRatio()
+*/
+void QwtPlotRescaler::setAspectRatio( int axis, double ratio )
+{
+    if ( ratio < 0.0 )
+        ratio = 0.0;
+
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->axisData[axis].aspectRatio = ratio;
+}
+
+/*!
+  \return Aspect ratio between an axis and the reference axis.
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \sa setAspectRatio()
+*/
+double QwtPlotRescaler::aspectRatio( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->axisData[axis].aspectRatio;
+
+    return 0.0;
+}
+
+/*!
+  Set an interval hint for an axis
+
+  In Fitting mode, the hint is used as minimal interval
+  that always needs to be displayed.
+
+  \param axis Axis, see QwtPlot::Axis
+  \param interval Axis
+  \sa intervalHint(), RescalePolicy
+*/
+void QwtPlotRescaler::setIntervalHint( int axis,
+    const QwtInterval &interval )
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        d_data->axisData[axis].intervalHint = interval;
+}
+
+/*!
+  \param axis Axis, see QwtPlot::Axis
+  \return Interval hint
+  \sa setIntervalHint(), RescalePolicy
+*/
+QwtInterval QwtPlotRescaler::intervalHint( int axis ) const
+{
+    if ( axis >= 0 && axis < QwtPlot::axisCnt )
+        return d_data->axisData[axis].intervalHint;
+
+    return QwtInterval();
+}
+
+//! \return plot canvas
+QWidget *QwtPlotRescaler::canvas()
+{
+    return qobject_cast<QWidget *>( parent() );
+}
+
+//! \return plot canvas
+const QWidget *QwtPlotRescaler::canvas() const
+{
+    return qobject_cast<const QWidget *>( parent() );
+}
+
+//! \return plot widget
+QwtPlot *QwtPlotRescaler::plot()
+{
+    QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<QwtPlot *>( w );
+}
+
+//! \return plot widget
+const QwtPlot *QwtPlotRescaler::plot() const
+{
+    const QWidget *w = canvas();
+    if ( w )
+        w = w->parentWidget();
+
+    return qobject_cast<const QwtPlot *>( w );
+}
+
+//!  Event filter for the plot canvas
+bool QwtPlotRescaler::eventFilter( QObject *object, QEvent *event )
+{
+    if ( object && object == canvas() )
+    {
+        switch ( event->type() )
+        {
+            case QEvent::Resize:
+            {
+                canvasResizeEvent( static_cast<QResizeEvent *>( event ) );
+                break;
+            }
+            case QEvent::PolishRequest:
+            {
+                rescale();
+                break;
+            }
+            default:;
+        }
+    }
+
+    return false;
+}
+
+/*!
+  Event handler for resize events of the plot canvas
+
+  \param event Resize event
+  \sa rescale()
+*/
+void QwtPlotRescaler::canvasResizeEvent( QResizeEvent* event )
+{
+    int left, top, right, bottom;
+    canvas()->getContentsMargins( &left, &top, &right, &bottom );
+
+    const QSize marginSize( left + right, top + bottom );
+
+    const QSize newSize = event->size() - marginSize;
+    const QSize oldSize = event->oldSize() - marginSize;
+
+    rescale( oldSize, newSize );
+}
+
+//! Adjust the plot axes scales
+void QwtPlotRescaler::rescale() const
+{
+    const QSize size = canvas()->contentsRect().size();
+    rescale( size, size );
+}
+
+/*!
+   Adjust the plot axes scales
+
+   \param oldSize Previous size of the canvas
+   \param newSize New size of the canvas
+*/
+void QwtPlotRescaler::rescale(
+    const QSize &oldSize, const QSize &newSize ) const
+{
+    if ( newSize.isEmpty() )
+        return;
+
+    QwtInterval intervals[QwtPlot::axisCnt];
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+        intervals[axis] = interval( axis );
+
+    const int refAxis = referenceAxis();
+    intervals[refAxis] = expandScale( refAxis, oldSize, newSize );
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( aspectRatio( axis ) > 0.0 && axis != refAxis )
+            intervals[axis] = syncScale( axis, intervals[refAxis], newSize );
+    }
+
+    updateScales( intervals );
+}
+
+/*!
+  Calculate the new scale interval of a plot axis
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \param oldSize Previous size of the canvas
+  \param newSize New size of the canvas
+
+  \return Calculated new interval for the axis
+*/
+QwtInterval QwtPlotRescaler::expandScale( int axis,
+        const QSize &oldSize, const QSize &newSize ) const
+{
+    const QwtInterval oldInterval = interval( axis );
+
+    QwtInterval expanded = oldInterval;
+    switch ( rescalePolicy() )
+    {
+        case Fixed:
+        {
+            break; // do nothing
+        }
+        case Expanding:
+        {
+            if ( !oldSize.isEmpty() )
+            {
+                double width = oldInterval.width();
+                if ( orientation( axis ) == Qt::Horizontal )
+                    width *= double( newSize.width() ) / oldSize.width();
+                else
+                    width *= double( newSize.height() ) / oldSize.height();
+
+                expanded = expandInterval( oldInterval,
+                    width, expandingDirection( axis ) );
+            }
+            break;
+        }
+        case Fitting:
+        {
+            double dist = 0.0;
+            for ( int ax = 0; ax < QwtPlot::axisCnt; ax++ )
+            {
+                const double d = pixelDist( ax, newSize );
+                if ( d > dist )
+                    dist = d;
+            }
+            if ( dist > 0.0 )
+            {
+                double width;
+                if ( orientation( axis ) == Qt::Horizontal )
+                    width = newSize.width() * dist;
+                else
+                    width = newSize.height() * dist;
+
+                expanded = expandInterval( intervalHint( axis ),
+                    width, expandingDirection( axis ) );
+            }
+            break;
+        }
+    }
+
+    return expanded;
+}
+
+/*!
+  Synchronize an axis scale according to the scale of the reference axis
+
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \param reference Interval of the reference axis
+  \param size Size of the canvas
+
+  \return New interval for axis
+*/
+QwtInterval QwtPlotRescaler::syncScale( int axis,
+    const QwtInterval& reference, const QSize &size ) const
+{
+    double dist;
+    if ( orientation( referenceAxis() ) == Qt::Horizontal )
+        dist = reference.width() / size.width();
+    else
+        dist = reference.width() / size.height();
+
+    if ( orientation( axis ) == Qt::Horizontal )
+        dist *= size.width();
+    else
+        dist *= size.height();
+
+    dist /= aspectRatio( axis );
+
+    QwtInterval intv;
+    if ( rescalePolicy() == Fitting )
+        intv = intervalHint( axis );
+    else
+        intv = interval( axis );
+
+    intv = expandInterval( intv, dist, expandingDirection( axis ) );
+
+    return intv;
+}
+
+/*!
+  \return Orientation of an axis
+  \param axis Axis index ( see QwtPlot::AxisId )
+*/
+Qt::Orientation QwtPlotRescaler::orientation( int axis ) const
+{
+    if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight )
+        return Qt::Vertical;
+
+    return Qt::Horizontal;
+}
+
+/*!
+  \param axis Axis index ( see QwtPlot::AxisId )
+  \return Normalized interval of an axis
+*/
+QwtInterval QwtPlotRescaler::interval( int axis ) const
+{
+    if ( axis < 0 || axis >= QwtPlot::axisCnt )
+        return QwtInterval();
+
+    return plot()->axisScaleDiv( axis ).interval().normalized();
+}
+
+/*!
+  Expand the interval
+
+  \param interval Interval to be expanded
+  \param width Distance to be added to the interval
+  \param direction Direction of the expand operation
+
+  \return Expanded interval
+*/
+QwtInterval QwtPlotRescaler::expandInterval(
+    const QwtInterval &interval, double width,
+    ExpandingDirection direction ) const
+{
+    QwtInterval expanded = interval;
+
+    switch ( direction )
+    {
+        case ExpandUp:
+            expanded.setMinValue( interval.minValue() );
+            expanded.setMaxValue( interval.minValue() + width );
+            break;
+
+        case ExpandDown:
+            expanded.setMaxValue( interval.maxValue() );
+            expanded.setMinValue( interval.maxValue() - width );
+            break;
+
+        case ExpandBoth:
+        default:
+            expanded.setMinValue( interval.minValue() +
+                interval.width() / 2.0 - width / 2.0 );
+            expanded.setMaxValue( expanded.minValue() + width );
+    }
+    return expanded;
+}
+
+double QwtPlotRescaler::pixelDist( int axis, const QSize &size ) const
+{
+    const QwtInterval intv = intervalHint( axis );
+
+    double dist = 0.0;
+    if ( !intv.isNull() )
+    {
+        if ( axis == referenceAxis() )
+            dist = intv.width();
+        else
+        {
+            const double r = aspectRatio( axis );
+            if ( r > 0.0 )
+                dist = intv.width() * r;
+        }
+    }
+
+    if ( dist > 0.0 )
+    {
+        if ( orientation( axis ) == Qt::Horizontal )
+            dist /= size.width();
+        else
+            dist /= size.height();
+    }
+
+    return dist;
+}
+
+/*!
+   Update the axes scales
+
+   \param intervals Scale intervals
+*/
+void QwtPlotRescaler::updateScales(
+    QwtInterval intervals[QwtPlot::axisCnt] ) const
+{
+    if ( d_data->inReplot >= 5 )
+    {
+        return;
+    }
+
+    QwtPlot *plt = const_cast<QwtPlot *>( plot() );
+
+    const bool doReplot = plt->autoReplot();
+    plt->setAutoReplot( false );
+
+    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
+    {
+        if ( axis == referenceAxis() || aspectRatio( axis ) > 0.0 )
+        {
+            double v1 = intervals[axis].minValue();
+            double v2 = intervals[axis].maxValue();
+
+            if ( !plt->axisScaleDiv( axis ).isIncreasing() )
+                qSwap( v1, v2 );
+
+            if ( d_data->inReplot >= 1 )
+                d_data->axisData[axis].scaleDiv = plt->axisScaleDiv( axis );
+
+            if ( d_data->inReplot >= 2 )
+            {
+                QList<double> ticks[QwtScaleDiv::NTickTypes];
+                for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+                    ticks[i] = d_data->axisData[axis].scaleDiv.ticks( i );
+
+                plt->setAxisScaleDiv( axis, QwtScaleDiv( v1, v2, ticks ) );
+            }
+            else
+            {
+                plt->setAxisScale( axis, v1, v2 );
+            }
+        }
+    }
+
+    QwtPlotCanvas *canvas = qobject_cast<QwtPlotCanvas *>( plt->canvas() );
+
+    bool immediatePaint = false;
+    if ( canvas )
+    {
+        immediatePaint = canvas->testPaintAttribute( QwtPlotCanvas::ImmediatePaint );
+        canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false );
+    }
+
+    plt->setAutoReplot( doReplot );
+
+    d_data->inReplot++;
+    plt->replot();
+    d_data->inReplot--;
+
+    if ( canvas && immediatePaint )
+    {
+        canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, true );
+    }
+}
diff --git a/source/third_party/qwt/qwt_plot_scaleitem.cpp b/source/third_party/qwt/qwt_plot_scaleitem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3c691887121c6732b25e2ea0ed847e690bd921c7
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_scaleitem.cpp
@@ -0,0 +1,478 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_scaleitem.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_interval.h"
+#include <qpalette.h>
+#include <qpainter.h>
+
+class QwtPlotScaleItem::PrivateData
+{
+public:
+    PrivateData():
+        position( 0.0 ),
+        borderDistance( -1 ),
+        scaleDivFromAxis( true ),
+        scaleDraw( new QwtScaleDraw() )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete scaleDraw;
+    }
+
+    QwtInterval scaleInterval( const QRectF &,
+        const QwtScaleMap &, const QwtScaleMap & ) const;
+
+    QPalette palette;
+    QFont font;
+    double position;
+    int borderDistance;
+    bool scaleDivFromAxis;
+    QwtScaleDraw *scaleDraw;
+};
+
+QwtInterval QwtPlotScaleItem::PrivateData::scaleInterval( const QRectF &canvasRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const
+{
+    QwtInterval interval;
+    if ( scaleDraw->orientation() == Qt::Horizontal )
+    {
+        interval.setMinValue( xMap.invTransform( canvasRect.left() ) );
+        interval.setMaxValue( xMap.invTransform( canvasRect.right() - 1 ) );
+    }
+    else
+    {
+        interval.setMinValue( yMap.invTransform( canvasRect.bottom() - 1 ) );
+        interval.setMaxValue( yMap.invTransform( canvasRect.top() ) );
+    }
+
+    return interval;
+}
+
+/*!
+   \brief Constructor for scale item at the position pos.
+
+   \param alignment In case of QwtScaleDraw::BottomScale or QwtScaleDraw::TopScale
+                    the scale item is corresponding to the xAxis(),
+                    otherwise it corresponds to the yAxis().
+
+   \param pos x or y position, depending on the corresponding axis.
+
+   \sa setPosition(), setAlignment()
+*/
+QwtPlotScaleItem::QwtPlotScaleItem(
+        QwtScaleDraw::Alignment alignment, const double pos ):
+    QwtPlotItem( QwtText( "Scale" ) )
+{
+    d_data = new PrivateData;
+    d_data->position = pos;
+    d_data->scaleDraw->setAlignment( alignment );
+
+    setItemInterest( QwtPlotItem::ScaleInterest, true );
+    setZ( 11.0 );
+}
+
+//! Destructor
+QwtPlotScaleItem::~QwtPlotScaleItem()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotScale
+int QwtPlotScaleItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotScale;
+}
+
+/*!
+   \brief Assign a scale division
+
+   When assigning a scaleDiv the scale division won't be synchronized
+   with the corresponding axis anymore.
+
+   \param scaleDiv Scale division
+   \sa scaleDiv(), setScaleDivFromAxis(), isScaleDivFromAxis()
+*/
+void QwtPlotScaleItem::setScaleDiv( const QwtScaleDiv& scaleDiv )
+{
+    d_data->scaleDivFromAxis = false;
+    d_data->scaleDraw->setScaleDiv( scaleDiv );
+}
+
+//! \return Scale division
+const QwtScaleDiv& QwtPlotScaleItem::scaleDiv() const
+{
+    return d_data->scaleDraw->scaleDiv();
+}
+
+/*!
+   Enable/Disable the synchronization of the scale division with
+   the corresponding axis.
+
+   \param on true/false
+   \sa isScaleDivFromAxis()
+*/
+void QwtPlotScaleItem::setScaleDivFromAxis( bool on )
+{
+    if ( on != d_data->scaleDivFromAxis )
+    {
+        d_data->scaleDivFromAxis = on;
+        if ( on )
+        {
+            const QwtPlot *plt = plot();
+            if ( plt )
+            {
+                updateScaleDiv( plt->axisScaleDiv( xAxis() ),
+                    plt->axisScaleDiv( yAxis() ) );
+                itemChanged();
+            }
+        }
+    }
+}
+
+/*!
+   \return True, if the synchronization of the scale division with
+           the corresponding axis is enabled.
+   \sa setScaleDiv(), setScaleDivFromAxis()
+*/
+bool QwtPlotScaleItem::isScaleDivFromAxis() const
+{
+    return d_data->scaleDivFromAxis;
+}
+
+/*!
+   Set the palette
+   \sa QwtAbstractScaleDraw::draw(), palette()
+*/
+void QwtPlotScaleItem::setPalette( const QPalette &palette )
+{
+    if ( palette != d_data->palette )
+    {
+        d_data->palette = palette;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+   \return palette
+   \sa setPalette()
+*/
+QPalette QwtPlotScaleItem::palette() const
+{
+    return d_data->palette;
+}
+
+/*!
+   Change the tick label font
+   \sa font()
+*/
+void QwtPlotScaleItem::setFont( const QFont &font )
+{
+    if ( font != d_data->font )
+    {
+        d_data->font = font;
+        itemChanged();
+    }
+}
+
+/*!
+   \return tick label font
+   \sa setFont()
+*/
+QFont QwtPlotScaleItem::font() const
+{
+    return d_data->font;
+}
+
+/*!
+  \brief Set a scale draw
+
+  \param scaleDraw object responsible for drawing scales.
+
+  The main use case for replacing the default QwtScaleDraw is
+  to overload QwtAbstractScaleDraw::label, to replace or swallow
+  tick labels.
+
+  \sa scaleDraw()
+*/
+void QwtPlotScaleItem::setScaleDraw( QwtScaleDraw *scaleDraw )
+{
+    if ( scaleDraw == NULL )
+        return;
+
+    if ( scaleDraw != d_data->scaleDraw )
+        delete d_data->scaleDraw;
+
+    d_data->scaleDraw = scaleDraw;
+
+    const QwtPlot *plt = plot();
+    if ( plt )
+    {
+        updateScaleDiv( plt->axisScaleDiv( xAxis() ),
+            plt->axisScaleDiv( yAxis() ) );
+    }
+
+    itemChanged();
+}
+
+/*!
+   \return Scale draw
+   \sa setScaleDraw()
+*/
+const QwtScaleDraw *QwtPlotScaleItem::scaleDraw() const
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+   \return Scale draw
+   \sa setScaleDraw()
+*/
+QwtScaleDraw *QwtPlotScaleItem::scaleDraw()
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+   Change the position of the scale
+
+   The position is interpreted as y value for horizontal axes
+   and as x value for vertical axes.
+
+   The border distance is set to -1.
+
+   \param pos New position
+   \sa position(), setAlignment()
+*/
+void QwtPlotScaleItem::setPosition( double pos )
+{
+    if ( d_data->position != pos )
+    {
+        d_data->position = pos;
+        d_data->borderDistance = -1;
+        itemChanged();
+    }
+}
+
+/*!
+   \return Position of the scale
+   \sa setPosition(), setAlignment()
+*/
+double QwtPlotScaleItem::position() const
+{
+    return d_data->position;
+}
+
+/*!
+   \brief Align the scale to the canvas
+
+   If distance is >= 0 the scale will be aligned to a
+   border of the contents rectangle of the canvas. If
+   alignment() is QwtScaleDraw::LeftScale, the scale will
+   be aligned to the right border, if it is QwtScaleDraw::TopScale
+   it will be aligned to the bottom (and vice versa),
+
+   If distance is < 0 the scale will be at the position().
+
+   \param distance Number of pixels between the canvas border and the
+                   backbone of the scale.
+
+   \sa setPosition(), borderDistance()
+*/
+void QwtPlotScaleItem::setBorderDistance( int distance )
+{
+    if ( distance < 0 )
+        distance = -1;
+
+    if ( distance != d_data->borderDistance )
+    {
+        d_data->borderDistance = distance;
+        itemChanged();
+    }
+}
+
+/*!
+   \return Distance from a canvas border
+   \sa setBorderDistance(), setPosition()
+*/
+int QwtPlotScaleItem::borderDistance() const
+{
+    return d_data->borderDistance;
+}
+
+/*!
+   Change the alignment of the scale
+
+   The alignment sets the orientation of the scale and the position of
+   the ticks:
+
+   - QwtScaleDraw::BottomScale: horizontal, ticks below
+   - QwtScaleDraw::TopScale: horizontal, ticks above
+   - QwtScaleDraw::LeftScale: vertical, ticks left
+   - QwtScaleDraw::RightScale: vertical, ticks right
+
+   For horizontal scales the position corresponds to QwtPlotItem::yAxis(),
+   otherwise to QwtPlotItem::xAxis().
+
+   \sa scaleDraw(), QwtScaleDraw::alignment(), setPosition()
+*/
+void QwtPlotScaleItem::setAlignment( QwtScaleDraw::Alignment alignment )
+{
+    QwtScaleDraw *sd = d_data->scaleDraw;
+    if ( sd->alignment() != alignment )
+    {
+        sd->setAlignment( alignment );
+        itemChanged();
+    }
+}
+
+/*!
+  \brief Draw the scale
+*/
+void QwtPlotScaleItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    QwtScaleDraw *sd = d_data->scaleDraw;
+
+    if ( d_data->scaleDivFromAxis )
+    {
+        const QwtInterval interval = 
+            d_data->scaleInterval( canvasRect, xMap, yMap );
+
+        if ( interval != sd->scaleDiv().interval() )
+        {
+            QwtScaleDiv scaleDiv = sd->scaleDiv();
+            scaleDiv.setInterval( interval );
+            sd->setScaleDiv( scaleDiv );
+        }
+    }
+
+    QPen pen = painter->pen();
+    pen.setStyle( Qt::SolidLine );
+    painter->setPen( pen );
+
+    if ( sd->orientation() == Qt::Horizontal )
+    {
+        double y;
+        if ( d_data->borderDistance >= 0 )
+        {
+            if ( sd->alignment() == QwtScaleDraw::BottomScale )
+                y = canvasRect.top() + d_data->borderDistance;
+            else
+            {
+                y = canvasRect.bottom() - d_data->borderDistance;
+            }
+
+        }
+        else
+        {
+            y = yMap.transform( d_data->position );
+        }
+
+        if ( y < canvasRect.top() || y > canvasRect.bottom() )
+            return;
+
+        sd->move( canvasRect.left(), y );
+        sd->setLength( canvasRect.width() - 1 );
+
+        QwtTransform *transform = NULL;
+        if ( xMap.transformation() )
+            transform = xMap.transformation()->copy();
+
+        sd->setTransformation( transform );
+    }
+    else // == Qt::Vertical
+    {
+        double x;
+        if ( d_data->borderDistance >= 0 )
+        {
+            if ( sd->alignment() == QwtScaleDraw::RightScale )
+                x = canvasRect.left() + d_data->borderDistance;
+            else
+            {
+                x = canvasRect.right() - d_data->borderDistance;
+            }
+        }
+        else
+        {
+            x = xMap.transform( d_data->position );
+        }
+        if ( x < canvasRect.left() || x > canvasRect.right() )
+            return;
+
+        sd->move( x, canvasRect.top() );
+        sd->setLength( canvasRect.height() - 1 );
+
+        QwtTransform *transform = NULL;
+        if ( yMap.transformation() )
+            transform = yMap.transformation()->copy();
+
+        sd->setTransformation( transform );
+    }
+
+    painter->setFont( d_data->font );
+
+    sd->draw( painter, d_data->palette );
+}
+
+/*!
+   \brief Update the item to changes of the axes scale division
+
+   In case of isScaleDivFromAxis(), the scale draw is synchronized
+   to the correspond axis.
+
+   \param xScaleDiv Scale division of the x-axis
+   \param yScaleDiv Scale division of the y-axis
+
+   \sa QwtPlot::updateAxes()
+*/
+
+void QwtPlotScaleItem::updateScaleDiv( const QwtScaleDiv& xScaleDiv,
+    const QwtScaleDiv& yScaleDiv )
+{
+    QwtScaleDraw *scaleDraw = d_data->scaleDraw;
+
+    if ( d_data->scaleDivFromAxis && scaleDraw )
+    {
+        const QwtScaleDiv &scaleDiv = 
+            scaleDraw->orientation() == Qt::Horizontal ? xScaleDiv : yScaleDiv;
+
+        const QwtPlot *plt = plot();
+        if ( plt != NULL )
+        {
+            const QRectF canvasRect = plt->canvas()->contentsRect();
+
+            const QwtInterval interval = d_data->scaleInterval( 
+                canvasRect, plt->canvasMap( xAxis() ), plt->canvasMap( yAxis() ) );
+
+            QwtScaleDiv sd = scaleDiv;
+            sd.setInterval( interval );
+
+            if ( sd != scaleDraw->scaleDiv() )
+            {
+                // the internal label cache of QwtScaleDraw
+                // is cleared here, so better avoid pointless
+                // assignments.
+
+                scaleDraw->setScaleDiv( sd );
+            }
+        }
+        else
+        {
+            scaleDraw->setScaleDiv( scaleDiv );
+        }
+    }
+}
diff --git a/source/third_party/qwt/qwt_plot_seriesitem.cpp b/source/third_party/qwt/qwt_plot_seriesitem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4376b067457e826d9bba966ee1c85c328bc63923
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_seriesitem.cpp
@@ -0,0 +1,112 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_seriesitem.h"
+
+class QwtPlotSeriesItem::PrivateData
+{
+public:
+    PrivateData():
+        orientation( Qt::Vertical )
+    {
+    }
+
+    Qt::Orientation orientation;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotSeriesItem::QwtPlotSeriesItem( const QwtText &title ):
+    QwtPlotItem( title )
+{
+    d_data = new PrivateData();
+    setItemInterest( QwtPlotItem::ScaleInterest, true );
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotSeriesItem::QwtPlotSeriesItem( const QString &title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    d_data = new PrivateData();
+}
+
+//! Destructor
+QwtPlotSeriesItem::~QwtPlotSeriesItem()
+{
+    delete d_data;
+}
+
+/*!
+  Set the orientation of the item.
+
+  The orientation() might be used in specific way by a plot item.
+  F.e. a QwtPlotCurve uses it to identify how to display the curve
+  int QwtPlotCurve::Steps or QwtPlotCurve::Sticks style.
+
+  \sa orientation()
+*/
+void QwtPlotSeriesItem::setOrientation( Qt::Orientation orientation )
+{
+    if ( d_data->orientation != orientation )
+    {
+        d_data->orientation = orientation;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Orientation of the plot item
+  \sa setOrientation()
+*/
+Qt::Orientation QwtPlotSeriesItem::orientation() const
+{
+    return d_data->orientation;
+}
+
+/*!
+  \brief Draw the complete series
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+*/
+void QwtPlotSeriesItem::draw( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QRectF &canvasRect ) const
+{
+    drawSeries( painter, xMap, yMap, canvasRect, 0, -1 );
+}
+
+QRectF QwtPlotSeriesItem::boundingRect() const
+{
+    return dataRect();
+}
+
+void QwtPlotSeriesItem::updateScaleDiv(
+    const QwtScaleDiv &xScaleDiv, const QwtScaleDiv &yScaleDiv )
+{   
+    const QRectF rect = QRectF(
+        xScaleDiv.lowerBound(), yScaleDiv.lowerBound(),
+        xScaleDiv.range(), yScaleDiv.range() );
+        
+    setRectOfInterest( rect );
+}   
+
+void QwtPlotSeriesItem::dataChanged()
+{
+    itemChanged();
+}
diff --git a/source/third_party/qwt/qwt_plot_shapeitem.cpp b/source/third_party/qwt/qwt_plot_shapeitem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba422bf2d4c37420dade1d7a19e1c0b0bd81eece
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_shapeitem.cpp
@@ -0,0 +1,497 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_shapeitem.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_curve_fitter.h"
+#include "qwt/qwt_clipper.h"
+
+static QPainterPath qwtTransformPath( const QwtScaleMap &xMap,
+        const QwtScaleMap &yMap, const QPainterPath &path, bool doAlign )
+{
+    QPainterPath shape;
+    shape.setFillRule( path.fillRule() );
+
+    for ( int i = 0; i < path.elementCount(); i++ )
+    {
+        const QPainterPath::Element &element = path.elementAt( i );
+
+        double x = xMap.transform( element.x );
+        double y = yMap.transform( element.y );
+
+        switch( element.type )
+        {
+            case QPainterPath::MoveToElement:
+            {
+                if ( doAlign )
+                {
+                    x = qRound( x );
+                    y = qRound( y );
+                }
+
+                shape.moveTo( x, y );
+                break;
+            }
+            case QPainterPath::LineToElement:
+            {
+                if ( doAlign )
+                {
+                    x = qRound( x );
+                    y = qRound( y );
+                }
+
+                shape.lineTo( x, y );
+                break;
+            }
+            case QPainterPath::CurveToElement:
+            {
+                const QPainterPath::Element& element1 = path.elementAt( ++i );
+                const double x1 = xMap.transform( element1.x );
+                const double y1 = yMap.transform( element1.y );
+
+                const QPainterPath::Element& element2 = path.elementAt( ++i );
+                const double x2 = xMap.transform( element2.x );
+                const double y2 = yMap.transform( element2.y );
+
+                shape.cubicTo( x, y, x1, y1, x2, y2 );
+                break;
+            }
+            case QPainterPath::CurveToDataElement:
+            {
+                break;
+            }
+        }
+    }
+
+    return shape;
+}
+
+
+class QwtPlotShapeItem::PrivateData
+{
+public:
+    PrivateData():
+        legendMode( QwtPlotShapeItem::LegendColor ),
+        renderTolerance( 0.0 )
+    {
+    }
+
+    QwtPlotShapeItem::PaintAttributes paintAttributes;
+    QwtPlotShapeItem::LegendMode legendMode;
+
+    double renderTolerance;
+    QRectF boundingRect;
+
+    QPen pen;
+    QBrush brush;
+    QPainterPath shape;
+};
+
+/*!
+   \brief Constructor
+
+   Sets the following item attributes:
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   \param title Title
+*/
+QwtPlotShapeItem::QwtPlotShapeItem( const QString& title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    init();
+}
+
+/*!
+   \brief Constructor
+
+   Sets the following item attributes:
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   \param title Title
+*/
+QwtPlotShapeItem::QwtPlotShapeItem( const QwtText& title ):
+    QwtPlotItem( title )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotShapeItem::~QwtPlotShapeItem()
+{
+    delete d_data;
+}
+
+void QwtPlotShapeItem::init()
+{
+    d_data = new PrivateData();
+    d_data->boundingRect = QwtPlotItem::boundingRect();
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 8.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotShape
+int QwtPlotShapeItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotShape;
+}
+
+/*!
+  Specify an attribute how to draw the shape
+
+  \param attribute Paint attribute
+  \param on On/Off
+  \sa testPaintAttribute()
+*/
+void QwtPlotShapeItem::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+  \return True, when attribute is enabled
+  \sa setPaintAttribute()
+*/
+bool QwtPlotShapeItem::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Set the mode how to represent the item on the legend
+
+  \param mode Mode
+  \sa legendMode()
+ */
+void QwtPlotShapeItem::setLegendMode( LegendMode mode )
+{
+    if ( mode != d_data->legendMode )
+    {
+        d_data->legendMode = mode;
+        legendChanged();
+    }
+}
+
+/*!
+  \return Mode how to represent the item on the legend
+  \sa legendMode()
+ */
+QwtPlotShapeItem::LegendMode QwtPlotShapeItem::legendMode() const
+{
+    return d_data->legendMode;
+}
+
+//! Bounding rectangle of the shape
+QRectF QwtPlotShapeItem::boundingRect() const
+{
+    return d_data->boundingRect;
+}
+
+/*!
+  \brief Set a path built from a rectangle
+
+  \param rect Rectangle
+  \sa setShape(), setPolygon(), shape()
+ */
+void QwtPlotShapeItem::setRect( const QRectF &rect ) 
+{
+    QPainterPath path;
+    path.addRect( rect );
+
+    setShape( path );
+}
+
+/*!
+  \brief Set a path built from a polygon
+
+  \param polygon Polygon
+  \sa setShape(), setRect(), shape()
+ */
+void QwtPlotShapeItem::setPolygon( const QPolygonF &polygon )
+{
+    QPainterPath shape;
+    shape.addPolygon( polygon );
+
+    setShape( shape );
+}
+
+/*!
+  \brief Set the shape to be displayed
+
+  \param shape Shape
+  \sa setShape(), shape()
+ */
+void QwtPlotShapeItem::setShape( const QPainterPath &shape )
+{
+    if ( shape != d_data->shape )
+    {
+        d_data->shape = shape;
+        if ( shape.isEmpty() )
+        {
+            d_data->boundingRect = QwtPlotItem::boundingRect();
+        }
+        else
+        {
+            d_data->boundingRect = shape.boundingRect();
+        }
+
+        itemChanged();
+    }
+}
+
+/*!
+  \return Shape to be displayed
+  \sa setShape()
+ */
+QPainterPath QwtPlotShapeItem::shape() const
+{
+    return d_data->shape;
+}
+
+/*! 
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotShapeItem::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  \brief Assign a pen
+
+  The pen is used to draw the outline of the shape
+
+  \param pen Pen
+  \sa pen(), brush()
+*/
+void QwtPlotShapeItem::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+        itemChanged();
+    }
+}
+
+/*!
+    \return Pen used to draw the outline of the shape
+    \sa setPen(), brush()
+*/
+QPen QwtPlotShapeItem::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  Assign a brush.
+
+  The brush is used to fill the path
+
+  \param brush Brush
+  \sa brush(), pen()
+*/
+void QwtPlotShapeItem::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used to fill the shape
+  \sa setBrush(), pen()
+*/
+QBrush QwtPlotShapeItem::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  \brief Set the tolerance for the weeding optimization
+
+  After translating the shape into target device coordinate 
+  ( usually widget geometries ) the painter path can be simplified
+  by a point weeding algorithm ( Douglas-Peucker ).
+
+  For shapes built from curves and ellipses weeding might
+  have the opposite effect because they have to be expanded
+  to polygons.
+
+  \param tolerance Accepted error when reducing the number of points
+                   A value <= 0.0 disables weeding.
+
+  \sa renderTolerance(), QwtWeedingCurveFitter
+ */
+void QwtPlotShapeItem::setRenderTolerance( double tolerance )
+{
+    tolerance = qMax( tolerance, 0.0 );
+
+    if ( tolerance != d_data->renderTolerance )
+    {
+        d_data->renderTolerance = tolerance;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Tolerance for the weeding optimization
+  \sa setRenderTolerance()
+ */
+double QwtPlotShapeItem::renderTolerance() const
+{
+    return d_data->renderTolerance;
+}
+
+/*!
+  Draw the shape item
+
+  \param painter Painter
+  \param xMap X-Scale Map
+  \param yMap Y-Scale Map
+  \param canvasRect Contents rect of the plot canvas
+*/
+void QwtPlotShapeItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( d_data->shape.isEmpty() )
+        return;
+
+    if ( d_data->pen.style() == Qt::NoPen 
+        && d_data->brush.style() == Qt::NoBrush )
+    {
+        return;
+    }
+
+    const QRectF cr = QwtScaleMap::invTransform(
+        xMap, yMap, canvasRect.toRect() );
+
+    const QRectF &br = d_data->boundingRect;
+
+    if ( ( br.left() > cr.right() ) || ( br.right() < cr.left() )
+        || ( br.top() > cr.bottom() ) || ( br.bottom() < cr.top() ) )
+    {
+        // outside the visisble area
+        return;
+    }
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    QPainterPath path = qwtTransformPath( xMap, yMap, 
+        d_data->shape, doAlign );
+
+    if ( testPaintAttribute( QwtPlotShapeItem::ClipPolygons ) )
+    {
+        qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
+        QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw );
+
+        QPainterPath clippedPath;
+        clippedPath.setFillRule( path.fillRule() );
+
+        const QList<QPolygonF> polygons = path.toSubpathPolygons();
+        for ( int i = 0; i < polygons.size(); i++ )
+        {
+            const QPolygonF p = QwtClipper::clipPolygonF(
+                clipRect, polygons[i], true );
+
+            clippedPath.addPolygon( p );
+
+        }
+
+        path = clippedPath;
+    }
+
+    if ( d_data->renderTolerance > 0.0 )
+    {
+        QwtWeedingCurveFitter fitter( d_data->renderTolerance );
+
+        QPainterPath fittedPath;
+        fittedPath.setFillRule( path.fillRule() );
+
+        const QList<QPolygonF> polygons = path.toSubpathPolygons();
+        for ( int i = 0; i < polygons.size(); i++ )
+            fittedPath.addPolygon( fitter.fitCurve( polygons[ i ] ) );
+
+        path = fittedPath;
+    }
+
+    painter->setPen( d_data->pen );
+    painter->setBrush( d_data->brush );
+
+    painter->drawPath( path );
+}
+
+/*!
+  \return A rectangle filled with the color of the brush ( or the pen )
+
+  \param index Index of the legend entry 
+                ( usually there is only one )
+  \param size Icon size
+
+  \sa setLegendIconSize(), legendData()
+*/
+QwtGraphic QwtPlotShapeItem::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+
+    QwtGraphic icon;
+    icon.setDefaultSize( size );
+
+    if ( size.isEmpty() )
+        return icon;
+
+    if ( d_data->legendMode == QwtPlotShapeItem::LegendShape )
+    {
+        const QRectF &br = d_data->boundingRect;
+
+        QPainter painter( &icon );
+        painter.setRenderHint( QPainter::Antialiasing,
+            testRenderHint( QwtPlotItem::RenderAntialiased ) );
+
+        painter.translate( -br.topLeft() );
+
+        painter.setPen( d_data->pen );
+        painter.setBrush( d_data->brush );
+        painter.drawPath( d_data->shape );
+    }
+    else
+    {
+        QColor iconColor;
+        if ( d_data->brush.style() != Qt::NoBrush )
+            iconColor = d_data->brush.color();
+        else
+            iconColor = d_data->pen.color();
+
+        icon = defaultIcon( iconColor, size );
+    }
+
+    return icon;
+}
+
diff --git a/source/third_party/qwt/qwt_plot_spectrocurve.cpp b/source/third_party/qwt/qwt_plot_spectrocurve.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb54a80a5cf14f68744e0bc6e9ed4c423de1bdef
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_spectrocurve.cpp
@@ -0,0 +1,321 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_spectrocurve.h"
+#include "qwt/qwt_color_map.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+
+class QwtPlotSpectroCurve::PrivateData
+{
+public:
+    PrivateData():
+        colorRange( 0.0, 1000.0 ),
+        penWidth(0.0),
+        paintAttributes( QwtPlotSpectroCurve::ClipPoints )
+    {
+        colorMap = new QwtLinearColorMap();
+    }
+
+    ~PrivateData()
+    {
+        delete colorMap;
+    }
+
+    QwtColorMap *colorMap;
+    QwtInterval colorRange;
+    QVector<QRgb> colorTable;
+    double penWidth;
+    QwtPlotSpectroCurve::PaintAttributes paintAttributes;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotSpectroCurve::QwtPlotSpectroCurve( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotSpectroCurve::QwtPlotSpectroCurve( const QString &title ):
+    QwtPlotSeriesItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotSpectroCurve::~QwtPlotSpectroCurve()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Initialize data members
+*/
+void QwtPlotSpectroCurve::init()
+{
+    setItemAttribute( QwtPlotItem::Legend );
+    setItemAttribute( QwtPlotItem::AutoScale );
+
+    d_data = new PrivateData;
+    setData( new QwtPoint3DSeriesData() );
+
+    setZ( 20.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotSpectroCurve
+int QwtPlotSpectroCurve::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotSpectroCurve;
+}
+
+/*!
+  Specify an attribute how to draw the curve
+
+  \param attribute Paint attribute
+  \param on On/Off
+  /sa PaintAttribute, testPaintAttribute()
+*/
+void QwtPlotSpectroCurve::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa PaintAttribute, setPaintAttribute()
+*/
+bool QwtPlotSpectroCurve::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of points
+*/
+void QwtPlotSpectroCurve::setSamples( const QVector<QwtPoint3D> &samples )
+{
+    setData( new QwtPoint3DSeriesData( samples ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore. 
+*/
+void QwtPlotSpectroCurve::setSamples(
+    QwtSeriesData<QwtPoint3D> *data )
+{
+    setData( data );
+}  
+
+/*!
+  Change the color map
+
+  Often it is useful to display the mapping between intensities and
+  colors as an additional plot axis, showing a color bar.
+
+  \param colorMap Color Map
+
+  \sa colorMap(), setColorRange(), QwtColorMap::color(),
+      QwtScaleWidget::setColorBarEnabled(), QwtScaleWidget::setColorMap()
+*/
+void QwtPlotSpectroCurve::setColorMap( QwtColorMap *colorMap )
+{
+    if ( colorMap != d_data->colorMap )
+    {
+        delete d_data->colorMap;
+        d_data->colorMap = colorMap;
+    }
+
+    legendChanged();
+    itemChanged();
+}
+
+/*!
+   \return Color Map used for mapping the intensity values to colors
+   \sa setColorMap(), setColorRange(), QwtColorMap::color()
+*/
+const QwtColorMap *QwtPlotSpectroCurve::colorMap() const
+{
+    return d_data->colorMap;
+}
+
+/*!
+   Set the value interval, that corresponds to the color map
+
+   \param interval interval.minValue() corresponds to 0.0,
+                   interval.maxValue() to 1.0 on the color map.
+
+   \sa colorRange(), setColorMap(), QwtColorMap::color()
+*/
+void QwtPlotSpectroCurve::setColorRange( const QwtInterval &interval )
+{
+    if ( interval != d_data->colorRange )
+    {
+        d_data->colorRange = interval;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Value interval, that corresponds to the color map
+  \sa setColorRange(), setColorMap(), QwtColorMap::color()
+*/
+QwtInterval &QwtPlotSpectroCurve::colorRange() const
+{
+    return d_data->colorRange;
+}
+
+/*!
+  Assign a pen width
+
+  \param penWidth New pen width
+  \sa penWidth()
+*/
+void QwtPlotSpectroCurve::setPenWidth(double penWidth)
+{
+    if ( penWidth < 0.0 )
+        penWidth = 0.0;
+
+    if ( d_data->penWidth != penWidth )
+    {
+        d_data->penWidth = penWidth;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen width used to draw a dot
+  \sa setPenWidth()
+*/
+double QwtPlotSpectroCurve::penWidth() const
+{
+    return d_data->penWidth;
+}
+
+/*!
+  Draw a subset of the points
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawDots()
+*/
+void QwtPlotSpectroCurve::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( !painter || dataSize() <= 0 )
+        return;
+
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+    drawDots( painter, xMap, yMap, canvasRect, from, to );
+}
+
+/*!
+  Draw a subset of the points
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first sample to be painted
+  \param to Index of the last sample to be painted. If to < 0 the
+         series will be painted to its last sample.
+
+  \sa drawSeries()
+*/
+void QwtPlotSpectroCurve::drawDots( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( !d_data->colorRange.isValid() )
+        return;
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    const QwtColorMap::Format format = d_data->colorMap->format();
+    if ( format == QwtColorMap::Indexed )
+        d_data->colorTable = d_data->colorMap->colorTable( d_data->colorRange );
+
+    const QwtSeriesData<QwtPoint3D> *series = data();
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtPoint3D sample = series->sample( i );
+
+        double xi = xMap.transform( sample.x() );
+        double yi = yMap.transform( sample.y() );
+        if ( doAlign )
+        {
+            xi = qRound( xi );
+            yi = qRound( yi );
+        }
+
+        if ( d_data->paintAttributes & QwtPlotSpectroCurve::ClipPoints )
+        {
+            if ( !canvasRect.contains( xi, yi ) )
+                continue;
+        }
+
+        if ( format == QwtColorMap::RGB )
+        {
+            const QRgb rgb = d_data->colorMap->rgb(
+                d_data->colorRange, sample.z() );
+
+            painter->setPen( QPen( QColor::fromRgba( rgb ), d_data->penWidth ) );
+        }
+        else
+        {
+            const unsigned char index = d_data->colorMap->colorIndex(
+                d_data->colorRange, sample.z() );
+
+            painter->setPen( QPen( QColor::fromRgba( d_data->colorTable[index] ), 
+                d_data->penWidth ) );
+        }
+
+        QwtPainter::drawPoint( painter, QPointF( xi, yi ) );
+    }
+
+    d_data->colorTable.clear();
+}
diff --git a/source/third_party/qwt/qwt_plot_spectrogram.cpp b/source/third_party/qwt/qwt_plot_spectrogram.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..deed41d609fc6b26e5b43f2bbcc2360bed4457af
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_spectrogram.cpp
@@ -0,0 +1,676 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_spectrogram.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_interval.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_color_map.h"
+#include <qimage.h>
+#include <qpen.h>
+#include <qpainter.h>
+#include <qmath.h>
+#include <qalgorithms.h>
+#if QT_VERSION >= 0x040400
+#include <qthread.h>
+#include <qfuture.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+#endif
+
+#define DEBUG_RENDER 0
+
+#if DEBUG_RENDER
+#include <QElapsedTimer>
+#endif
+
+class QwtPlotSpectrogram::PrivateData
+{
+public:
+    PrivateData():
+        data( NULL )
+    {
+        colorMap = new QwtLinearColorMap();
+        displayMode = ImageMode;
+
+        conrecFlags = QwtRasterData::IgnoreAllVerticesOnLevel;
+#if 0
+        conrecFlags |= QwtRasterData::IgnoreOutOfRange;
+#endif
+    }
+    ~PrivateData()
+    {
+        delete data;
+        delete colorMap;
+    }
+
+    QwtRasterData *data;
+    QwtColorMap *colorMap;
+    DisplayModes displayMode;
+
+    QList<double> contourLevels;
+    QPen defaultContourPen;
+    QwtRasterData::ConrecFlags conrecFlags;
+};
+
+/*!
+   Sets the following item attributes:
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   The z value is initialized by 8.0.
+
+   \param title Title
+
+   \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
+*/
+QwtPlotSpectrogram::QwtPlotSpectrogram( const QString &title ):
+    QwtPlotRasterItem( title )
+{
+    d_data = new PrivateData();
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 8.0 );
+}
+
+//! Destructor
+QwtPlotSpectrogram::~QwtPlotSpectrogram()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotSpectrogram
+int QwtPlotSpectrogram::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotSpectrogram;
+}
+
+/*!
+   The display mode controls how the raster data will be represented.
+
+   \param mode Display mode
+   \param on On/Off
+
+   The default setting enables ImageMode.
+
+   \sa DisplayMode, displayMode()
+*/
+void QwtPlotSpectrogram::setDisplayMode( DisplayMode mode, bool on )
+{
+    if ( on != bool( mode & d_data->displayMode ) )
+    {
+        if ( on )
+            d_data->displayMode |= mode;
+        else
+            d_data->displayMode &= ~mode;
+    }
+
+    legendChanged();
+    itemChanged();
+}
+
+/*!
+   The display mode controls how the raster data will be represented.
+
+   \param mode Display mode
+   \return true if mode is enabled
+*/
+bool QwtPlotSpectrogram::testDisplayMode( DisplayMode mode ) const
+{
+    return ( d_data->displayMode & mode );
+}
+
+/*!
+  Change the color map
+
+  Often it is useful to display the mapping between intensities and
+  colors as an additional plot axis, showing a color bar.
+
+  \param colorMap Color Map
+
+  \sa colorMap(), QwtScaleWidget::setColorBarEnabled(),
+      QwtScaleWidget::setColorMap()
+*/
+void QwtPlotSpectrogram::setColorMap( QwtColorMap *colorMap )
+{
+    if ( d_data->colorMap != colorMap )
+    {
+        delete d_data->colorMap;
+        d_data->colorMap = colorMap;
+    }
+
+    invalidateCache();
+
+    legendChanged();
+    itemChanged();
+}
+
+/*!
+   \return Color Map used for mapping the intensity values to colors
+   \sa setColorMap()
+*/
+const QwtColorMap *QwtPlotSpectrogram::colorMap() const
+{
+    return d_data->colorMap;
+}
+
+/*! 
+  Build and assign the default pen for the contour lines
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotSpectrogram::setDefaultContourPen( 
+    const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setDefaultContourPen( QPen( color, width, style ) );
+}
+
+/*!
+   \brief Set the default pen for the contour lines
+
+   If the spectrogram has a valid default contour pen
+   a contour line is painted using the default contour pen.
+   Otherwise (pen.style() == Qt::NoPen) the pen is calculated
+   for each contour level using contourPen().
+
+   \sa defaultContourPen(), contourPen()
+*/
+void QwtPlotSpectrogram::setDefaultContourPen( const QPen &pen )
+{
+    if ( pen != d_data->defaultContourPen )
+    {
+        d_data->defaultContourPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+   \return Default contour pen
+   \sa setDefaultContourPen()
+*/
+QPen QwtPlotSpectrogram::defaultContourPen() const
+{
+    return d_data->defaultContourPen;
+}
+
+/*!
+   \brief Calculate the pen for a contour line
+
+   The color of the pen is the color for level calculated by the color map
+
+   \param level Contour level
+   \return Pen for the contour line
+   \note contourPen is only used if defaultContourPen().style() == Qt::NoPen
+
+   \sa setDefaultContourPen(), setColorMap(), setContourLevels()
+*/
+QPen QwtPlotSpectrogram::contourPen( double level ) const
+{
+    if ( d_data->data == NULL || d_data->colorMap == NULL )
+        return QPen();
+
+    const QwtInterval intensityRange = d_data->data->interval(Qt::ZAxis);
+    const QColor c( d_data->colorMap->rgb( intensityRange, level ) );
+
+    return QPen( c );
+}
+
+/*!
+   Modify an attribute of the CONREC algorithm, used to calculate
+   the contour lines.
+
+   \param flag CONREC flag
+   \param on On/Off
+
+   \sa testConrecFlag(), renderContourLines(),
+       QwtRasterData::contourLines()
+*/
+void QwtPlotSpectrogram::setConrecFlag(
+    QwtRasterData::ConrecFlag flag, bool on )
+{
+    if ( bool( d_data->conrecFlags & flag ) == on )
+        return;
+
+    if ( on )
+        d_data->conrecFlags |= flag;
+    else
+        d_data->conrecFlags &= ~flag;
+
+    itemChanged();
+}
+
+/*!
+   Test an attribute of the CONREC algorithm, used to calculate
+   the contour lines.
+
+   \param flag CONREC flag
+   \return true, is enabled
+
+   The default setting enables QwtRasterData::IgnoreAllVerticesOnLevel
+
+   \sa setConrecClag(), renderContourLines(),
+       QwtRasterData::contourLines()
+*/
+bool QwtPlotSpectrogram::testConrecFlag(
+    QwtRasterData::ConrecFlag flag ) const
+{
+    return d_data->conrecFlags & flag;
+}
+
+/*!
+   Set the levels of the contour lines
+
+   \param levels Values of the contour levels
+   \sa contourLevels(), renderContourLines(),
+       QwtRasterData::contourLines()
+
+   \note contourLevels returns the same levels but sorted.
+*/
+void QwtPlotSpectrogram::setContourLevels( const QList<double> &levels )
+{
+    d_data->contourLevels = levels;
+    qSort( d_data->contourLevels );
+
+    legendChanged();
+    itemChanged();
+}
+
+/*!
+   \return Levels of the contour lines.
+
+   The levels are sorted in increasing order.
+
+   \sa contourLevels(), renderContourLines(),
+       QwtRasterData::contourLines()
+*/
+QList<double> QwtPlotSpectrogram::contourLevels() const
+{
+    return d_data->contourLevels;
+}
+
+/*!
+  Set the data to be displayed
+
+  \param data Spectrogram Data
+  \sa data()
+*/
+void QwtPlotSpectrogram::setData( QwtRasterData *data )
+{
+    if ( data != d_data->data )
+    {
+        delete d_data->data;
+        d_data->data = data;
+
+        invalidateCache();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Spectrogram data
+  \sa setData()
+*/
+const QwtRasterData *QwtPlotSpectrogram::data() const
+{
+    return d_data->data;
+}
+
+/*!
+  \return Spectrogram data
+  \sa setData()
+*/
+QwtRasterData *QwtPlotSpectrogram::data()
+{
+    return d_data->data;
+}
+
+/*!
+   \return Bounding interval for an axis
+
+   The default implementation returns the interval of the
+   associated raster data object.
+
+   \param axis X, Y, or Z axis
+   \sa QwtRasterData::interval()
+*/
+QwtInterval QwtPlotSpectrogram::interval(Qt::Axis axis) const
+{
+    if ( d_data->data == NULL )
+        return QwtInterval();
+
+    return d_data->data->interval( axis );
+}
+
+/*!
+   \brief Pixel hint
+
+   The geometry of a pixel is used to calculated the resolution and
+   alignment of the rendered image. 
+
+   The default implementation returns data()->pixelHint( rect );
+
+   \param area In most implementations the resolution of the data doesn't
+               depend on the requested area.
+
+   \return Bounding rectangle of a pixel
+
+   \sa QwtPlotRasterItem::pixelHint(), QwtRasterData::pixelHint(), 
+       render(), renderImage()
+*/
+QRectF QwtPlotSpectrogram::pixelHint( const QRectF &area ) const
+{
+    if ( d_data->data == NULL )
+        return QRectF();
+
+    return d_data->data->pixelHint( area );
+}
+
+/*!
+   \brief Render an image from data and color map.
+
+   For each pixel of area the value is mapped into a color.
+
+  \param xMap X-Scale Map
+  \param yMap Y-Scale Map
+  \param area Requested area for the image in scale coordinates
+  \param imageSize Size of the requested image
+
+   \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending
+           on the color map.
+
+   \sa QwtRasterData::value(), QwtColorMap::rgb(),
+       QwtColorMap::colorIndex()
+*/
+QImage QwtPlotSpectrogram::renderImage(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &area, const QSize &imageSize ) const
+{
+    if ( imageSize.isEmpty() || d_data->data == NULL 
+        || d_data->colorMap == NULL )
+    {
+        return QImage();
+    }
+
+    const QwtInterval intensityRange = d_data->data->interval( Qt::ZAxis );
+    if ( !intensityRange.isValid() )
+        return QImage();
+
+    QImage::Format format = ( d_data->colorMap->format() == QwtColorMap::RGB )
+        ? QImage::Format_ARGB32 : QImage::Format_Indexed8;
+
+    QImage image( imageSize, format );
+
+    if ( d_data->colorMap->format() == QwtColorMap::Indexed )
+        image.setColorTable( d_data->colorMap->colorTable( intensityRange ) );
+
+    d_data->data->initRaster( area, image.size() );
+
+#if DEBUG_RENDER
+    QElapsedTimer time;
+    time.start();
+#endif
+
+#if QT_VERSION >= 0x040400 && !defined(QT_NO_QFUTURE)
+    uint numThreads = renderThreadCount();
+
+    if ( numThreads <= 0 )
+        numThreads = QThread::idealThreadCount();
+
+    if ( numThreads <= 0 )
+        numThreads = 1;
+
+    const int numRows = imageSize.height() / numThreads;
+
+    QList< QFuture<void> > futures;
+    for ( uint i = 0; i < numThreads; i++ )
+    {
+        QRect tile( 0, i * numRows, image.width(), numRows );
+        if ( i == numThreads - 1 )
+        {
+            tile.setHeight( image.height() - i * numRows );
+            renderTile( xMap, yMap, tile, &image );
+        }
+        else
+        {
+            futures += QtConcurrent::run(
+                this, &QwtPlotSpectrogram::renderTile,
+                xMap, yMap, tile, &image );
+        }
+    }
+    for ( int i = 0; i < futures.size(); i++ )
+        futures[i].waitForFinished();
+
+#else // QT_VERSION < 0x040400
+    const QRect tile( 0, 0, image.width(), image.height() );
+    renderTile( xMap, yMap, tile, &image );
+#endif
+
+#if DEBUG_RENDER
+    const qint64 elapsed = time.elapsed();
+    qDebug() << "renderImage" << imageSize << elapsed;
+#endif
+
+    d_data->data->discardRaster();
+
+    return image;
+}
+
+/*!
+    \brief Render a tile of an image.
+
+    Rendering in tiles can be used to composite an image in parallel
+    threads.
+
+    \param xMap X-Scale Map
+    \param yMap Y-Scale Map
+    \param tile Geometry of the tile in image coordinates
+    \param image Image to be rendered
+*/
+void QwtPlotSpectrogram::renderTile(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRect &tile, QImage *image ) const
+{
+    const QwtInterval range = d_data->data->interval( Qt::ZAxis );
+    if ( !range.isValid() )
+        return;
+
+    if ( d_data->colorMap->format() == QwtColorMap::RGB )
+    {
+        for ( int y = tile.top(); y <= tile.bottom(); y++ )
+        {
+            const double ty = yMap.invTransform( y );
+
+            QRgb *line = reinterpret_cast<QRgb *>( image->scanLine( y ) );
+            line += tile.left();
+
+            for ( int x = tile.left(); x <= tile.right(); x++ )
+            {
+                const double tx = xMap.invTransform( x );
+
+                *line++ = d_data->colorMap->rgb( range,
+                    d_data->data->value( tx, ty ) );
+            }
+        }
+    }
+    else if ( d_data->colorMap->format() == QwtColorMap::Indexed )
+    {
+        for ( int y = tile.top(); y <= tile.bottom(); y++ )
+        {
+            const double ty = yMap.invTransform( y );
+
+            unsigned char *line = image->scanLine( y );
+            line += tile.left();
+
+            for ( int x = tile.left(); x <= tile.right(); x++ )
+            {
+                const double tx = xMap.invTransform( x );
+
+                *line++ = d_data->colorMap->colorIndex( range,
+                    d_data->data->value( tx, ty ) );
+            }
+        }
+    }
+}
+
+/*!
+   \brief Return the raster to be used by the CONREC contour algorithm.
+
+   A larger size will improve the precision of the CONREC algorithm,
+   but will slow down the time that is needed to calculate the lines.
+
+   The default implementation returns rect.size() / 2 bounded to
+   the resolution depending on pixelSize().
+
+   \param area Rectangle, where to calculate the contour lines
+   \param rect Rectangle in pixel coordinates, where to paint the contour lines
+   \return Raster to be used by the CONREC contour algorithm.
+
+   \note The size will be bounded to rect.size().
+
+   \sa drawContourLines(), QwtRasterData::contourLines()
+*/
+QSize QwtPlotSpectrogram::contourRasterSize( 
+    const QRectF &area, const QRect &rect ) const
+{
+    QSize raster = rect.size() / 2;
+
+    const QRectF pixelRect = pixelHint( area );
+    if ( !pixelRect.isEmpty() )
+    {
+        const QSize res( qCeil( rect.width() / pixelRect.width() ),
+            qCeil( rect.height() / pixelRect.height() ) );
+        raster = raster.boundedTo( res );
+    }
+
+    return raster;
+}
+
+/*!
+   Calculate contour lines
+
+   \param rect Rectangle, where to calculate the contour lines
+   \param raster Raster, used by the CONREC algorithm
+   \return Calculated contour lines
+
+   \sa contourLevels(), setConrecFlag(),
+       QwtRasterData::contourLines()
+*/
+QwtRasterData::ContourLines QwtPlotSpectrogram::renderContourLines(
+    const QRectF &rect, const QSize &raster ) const
+{
+    if ( d_data->data == NULL )
+        return QwtRasterData::ContourLines();
+
+    return d_data->data->contourLines( rect, raster,
+        d_data->contourLevels, d_data->conrecFlags );
+}
+
+/*!
+   Paint the contour lines
+
+   \param painter Painter
+   \param xMap Maps x-values into pixel coordinates.
+   \param yMap Maps y-values into pixel coordinates.
+   \param contourLines Contour lines
+
+   \sa renderContourLines(), defaultContourPen(), contourPen()
+*/
+void QwtPlotSpectrogram::drawContourLines( QPainter *painter,
+        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+        const QwtRasterData::ContourLines &contourLines ) const
+{
+    if ( d_data->data == NULL )
+        return;
+
+    const int numLevels = d_data->contourLevels.size();
+    for ( int l = 0; l < numLevels; l++ )
+    {
+        const double level = d_data->contourLevels[l];
+
+        QPen pen = defaultContourPen();
+        if ( pen.style() == Qt::NoPen )
+            pen = contourPen( level );
+
+        if ( pen.style() == Qt::NoPen )
+            continue;
+
+        painter->setPen( pen );
+
+        const QPolygonF &lines = contourLines[level];
+        for ( int i = 0; i < lines.size(); i += 2 )
+        {
+            const QPointF p1( xMap.transform( lines[i].x() ),
+                yMap.transform( lines[i].y() ) );
+            const QPointF p2( xMap.transform( lines[i+1].x() ),
+                yMap.transform( lines[i+1].y() ) );
+
+            QwtPainter::drawLine( painter, p1, p2 );
+        }
+    }
+}
+
+/*!
+  \brief Draw the spectrogram
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+
+  \sa setDisplayMode(), renderImage(),
+      QwtPlotRasterItem::draw(), drawContourLines()
+*/
+void QwtPlotSpectrogram::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( d_data->displayMode & ImageMode )
+        QwtPlotRasterItem::draw( painter, xMap, yMap, canvasRect );
+
+    if ( d_data->displayMode & ContourMode )
+    {
+        // Add some pixels at the borders
+        const int margin = 2;
+        QRectF rasterRect( canvasRect.x() - margin, canvasRect.y() - margin,
+            canvasRect.width() + 2 * margin, canvasRect.height() + 2 * margin );
+
+        QRectF area = QwtScaleMap::invTransform( xMap, yMap, rasterRect );
+
+        const QRectF br = boundingRect();
+        if ( br.isValid() )
+        {
+            area &= br;
+            if ( area.isEmpty() )
+                return;
+
+            rasterRect = QwtScaleMap::transform( xMap, yMap, area );
+        }
+
+        QSize raster = contourRasterSize( area, rasterRect.toRect() );
+        raster = raster.boundedTo( rasterRect.toRect().size() );
+        if ( raster.isValid() )
+        {
+            const QwtRasterData::ContourLines lines =
+                renderContourLines( area, raster );
+
+            drawContourLines( painter, xMap, yMap, lines );
+        }
+    }
+}
diff --git a/source/third_party/qwt/qwt_plot_svgitem.cpp b/source/third_party/qwt/qwt_plot_svgitem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c1d9934a9f185d77ebe39d21b673cbdf108a1eb1
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_svgitem.cpp
@@ -0,0 +1,224 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_svgitem.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+
+#ifndef QT_NO_SVG
+
+#include <QtSvg/qsvgrenderer.h>
+
+class QwtPlotSvgItem::PrivateData
+{
+public:
+    PrivateData()
+    {
+    }
+
+    QRectF boundingRect;
+    QSvgRenderer renderer;
+};
+
+/*!
+   \brief Constructor
+
+   Sets the following item attributes:
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   \param title Title
+*/
+QwtPlotSvgItem::QwtPlotSvgItem( const QString& title ):
+    QwtPlotItem( QwtText( title ) )
+{
+    init();
+}
+
+/*!
+   \brief Constructor
+
+   Sets the following item attributes:
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   \param title Title
+*/
+QwtPlotSvgItem::QwtPlotSvgItem( const QwtText& title ):
+    QwtPlotItem( title )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotSvgItem::~QwtPlotSvgItem()
+{
+    delete d_data;
+}
+
+void QwtPlotSvgItem::init()
+{
+    d_data = new PrivateData();
+    d_data->boundingRect = QwtPlotItem::boundingRect();
+
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 8.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotSVG
+int QwtPlotSvgItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotSVG;
+}
+
+/*!
+   Load a SVG file
+
+   \param rect Bounding rectangle
+   \param fileName SVG file name
+
+   \return true, if the SVG file could be loaded
+*/
+bool QwtPlotSvgItem::loadFile( const QRectF &rect,
+    const QString &fileName )
+{
+    d_data->boundingRect = rect;
+    const bool ok = d_data->renderer.load( fileName );
+
+    legendChanged();
+    itemChanged();
+
+    return ok;
+}
+
+/*!
+   Load SVG data
+
+   \param rect Bounding rectangle
+   \param data in SVG format
+
+   \return true, if the SVG data could be loaded
+*/
+bool QwtPlotSvgItem::loadData( const QRectF &rect,
+    const QByteArray &data )
+{
+    d_data->boundingRect = rect;
+    const bool ok = d_data->renderer.load( data );
+
+    legendChanged();
+    itemChanged();
+
+    return ok;
+}
+
+//! Bounding rectangle of the item
+QRectF QwtPlotSvgItem::boundingRect() const
+{
+    return d_data->boundingRect;
+}
+
+//! \return Renderer used to render the SVG data
+const QSvgRenderer &QwtPlotSvgItem::renderer() const
+{
+    return d_data->renderer;
+}
+
+//! \return Renderer used to render the SVG data
+QSvgRenderer &QwtPlotSvgItem::renderer()
+{
+    return d_data->renderer;
+}
+
+/*!
+  Draw the SVG item
+
+  \param painter Painter
+  \param xMap X-Scale Map
+  \param yMap Y-Scale Map
+  \param canvasRect Contents rect of the plot canvas
+*/
+void QwtPlotSvgItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    const QRectF cRect = QwtScaleMap::invTransform(
+        xMap, yMap, canvasRect.toRect() );
+    const QRectF bRect = boundingRect();
+    if ( bRect.isValid() && cRect.isValid() )
+    {
+        QRectF rect = bRect;
+        if ( bRect.contains( cRect ) )
+            rect = cRect;
+
+        const QRectF r = QwtScaleMap::transform( xMap, yMap, rect );
+        render( painter, viewBox( rect ), r );
+    }
+}
+
+/*!
+  Render the SVG data
+
+  \param painter Painter
+  \param viewBox View Box, see QSvgRenderer::viewBox()
+  \param rect Target rectangle on the paint device
+*/
+void QwtPlotSvgItem::render( QPainter *painter,
+    const QRectF &viewBox, const QRectF &rect ) const
+{
+    if ( !viewBox.isValid() )
+        return;
+
+    QRectF r = rect;
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        r.setLeft ( qRound( r.left() ) );
+        r.setRight ( qRound( r.right() ) );
+        r.setTop ( qRound( r.top() ) );
+        r.setBottom ( qRound( r.bottom() ) );
+    }
+
+    d_data->renderer.setViewBox( viewBox );
+    d_data->renderer.render( painter, r );
+}
+
+/*!
+  Calculate the view box from rect and boundingRect().
+
+  \param rect Rectangle in scale coordinates
+  \return View box, see QSvgRenderer::viewBox()
+*/
+QRectF QwtPlotSvgItem::viewBox( const QRectF &rect ) const
+{
+    const QSize sz = d_data->renderer.defaultSize();
+    const QRectF br = boundingRect();
+
+    if ( !rect.isValid() || !br.isValid() || sz.isNull() )
+        return QRectF();
+
+    QwtScaleMap xMap;
+    xMap.setScaleInterval( br.left(), br.right() );
+    xMap.setPaintInterval( 0, sz.width() );
+
+    QwtScaleMap yMap;
+    yMap.setScaleInterval( br.top(), br.bottom() );
+    yMap.setPaintInterval( sz.height(), 0 );
+
+    const double x1 = xMap.transform( rect.left() );
+    const double x2 = xMap.transform( rect.right() );
+    const double y1 = yMap.transform( rect.bottom() );
+    const double y2 = yMap.transform( rect.top() );
+
+    return QRectF( x1, y1, x2 - x1, y2 - y1 );
+}
+
+#endif
\ No newline at end of file
diff --git a/source/third_party/qwt/qwt_plot_textlabel.cpp b/source/third_party/qwt/qwt_plot_textlabel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..141364c57e79445a0bb98db649fcaa0e6c949499
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_textlabel.cpp
@@ -0,0 +1,272 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_textlabel.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_map.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qmath.h>
+
+static QRect qwtItemRect( int renderFlags,
+    const QRectF &rect, const QSizeF &itemSize ) 
+{
+    int x;
+    if ( renderFlags & Qt::AlignLeft )
+    {
+        x = rect.left();
+    }
+    else if ( renderFlags & Qt::AlignRight )
+    {
+        x = rect.right() - itemSize.width();
+    }
+    else
+    {
+        x = rect.center().x() - 0.5 * itemSize.width();
+    }
+
+    int y;
+    if ( renderFlags & Qt::AlignTop ) 
+    {
+        y = rect.top();
+    }
+    else if ( renderFlags & Qt::AlignBottom )
+    {
+        y = rect.bottom() - itemSize.height();
+    }
+    else
+    {
+        y = rect.center().y() - 0.5 * itemSize.height();
+    }
+
+    return QRect( x, y, itemSize.width(), itemSize.height() );
+}
+
+class QwtPlotTextLabel::PrivateData
+{   
+public:
+    PrivateData():
+        margin( 5 )
+    {
+    }
+
+    QwtText text;
+    int margin;
+
+    QPixmap pixmap;
+};  
+
+/*!
+   \brief Constructor
+
+   Initializes an text label with an empty text
+
+   Sets the following item attributes:
+
+   - QwtPlotItem::AutoScale: true
+   - QwtPlotItem::Legend:    false
+
+   The z value is initialized by 150
+
+   \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
+*/
+
+QwtPlotTextLabel::QwtPlotTextLabel():
+    QwtPlotItem( QwtText( "Label" ) )
+{
+    d_data = new PrivateData;
+
+    setItemAttribute( QwtPlotItem::AutoScale, false );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 150 );
+}
+
+//! Destructor
+QwtPlotTextLabel::~QwtPlotTextLabel()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotTextLabel
+int QwtPlotTextLabel::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotTextLabel;
+}
+
+/*!
+  Set the text 
+
+  The label will be aligned to the plot canvas according to
+  the alignment flags of text.
+
+  \param text Text to be displayed
+
+  \sa text(), QwtText::renderFlags()
+*/
+void QwtPlotTextLabel::setText( const QwtText &text )
+{
+    if ( d_data->text != text )
+    {
+        d_data->text = text;
+
+        invalidateCache();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Text to be displayed
+  \sa setText()
+*/
+QwtText QwtPlotTextLabel::text() const
+{
+    return d_data->text;
+}
+
+/*!
+  Set the margin
+
+  The margin is the distance between the contentsRect()
+  of the plot canvas and the rectangle where the label can
+  be displayed.
+
+  \param margin Margin
+
+  \sa margin(), textRect()
+ */
+void QwtPlotTextLabel::setMargin( int margin )
+{
+    margin = qMax( margin, 0 );
+    if ( d_data->margin != margin )
+    {
+        d_data->margin = margin;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Margin added to the contentsMargins() of the canvas
+  \sa setMargin()
+*/
+int QwtPlotTextLabel::margin() const
+{
+    return d_data->margin;
+}
+
+/*!
+  Draw the text label
+
+  \param painter Painter
+  \param xMap x Scale Map
+  \param yMap y Scale Map
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+
+  \sa textRect()
+*/
+
+void QwtPlotTextLabel::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    Q_UNUSED( xMap );
+    Q_UNUSED( yMap );
+
+    const int m = d_data->margin;
+
+    const QRectF rect = textRect( canvasRect.adjusted( m, m, -m, -m ),
+        d_data->text.textSize( painter->font() ) );
+
+    bool doCache = QwtPainter::roundingAlignment( painter );
+    if ( doCache )
+    {
+        switch( painter->paintEngine()->type() )
+        {
+            case QPaintEngine::Picture:
+            case QPaintEngine::User: // usually QwtGraphic
+            {
+                // don't use a cache for record/replay devices
+                doCache = false;
+                break;
+            }
+            default:;
+        }
+    }
+
+    if ( doCache )
+    {
+        // when the paint device is aligning it is not one
+        // where scalability matters ( PDF, SVG ).
+        // As rendering a text label is an expensive operation
+        // we use a cache.
+
+        int pw = 0;
+        if ( d_data->text.borderPen().style() != Qt::NoPen )
+            pw = qMax( d_data->text.borderPen().width(), 1 );
+
+        QRect pixmapRect; 
+        pixmapRect.setLeft( qFloor( rect.left() ) - pw );
+        pixmapRect.setTop( qFloor( rect.top() ) - pw );
+        pixmapRect.setRight( qCeil( rect.right() ) + pw );
+        pixmapRect.setBottom( qCeil( rect.bottom() ) + pw );
+        
+#define QWT_HIGH_DPI 1
+
+#if QT_VERSION >= 0x050100 && QWT_HIGH_DPI
+        const qreal pixelRatio = painter->device()->devicePixelRatio();
+        const QSize scaledSize = pixmapRect.size() * pixelRatio;
+#else
+        const QSize scaledSize = pixmapRect.size();
+#endif
+        if ( d_data->pixmap.isNull() || 
+            ( scaledSize != d_data->pixmap.size() )  )
+        {
+            d_data->pixmap = QPixmap( scaledSize );
+#if QT_VERSION >= 0x050100 && QWT_HIGH_DPI
+            d_data->pixmap.setDevicePixelRatio( pixelRatio );
+#endif
+            d_data->pixmap.fill( Qt::transparent );
+
+            const QRect r( pw, pw, 
+                pixmapRect.width() - 2 * pw, pixmapRect.height() - 2 * pw );
+
+            QPainter pmPainter( &d_data->pixmap );
+            d_data->text.draw( &pmPainter, r );
+        }
+
+        painter->drawPixmap( pixmapRect, d_data->pixmap );
+    }
+    else
+    {
+        d_data->text.draw( painter, rect );
+    }
+}
+
+/*!
+   \brief Align the text label
+
+   \param rect Canvas rectangle with margins subtracted
+   \param textSize Size required to draw the text
+
+   \return A rectangle aligned according the the alignment flags of
+           the text.
+
+   \sa setMargin(), QwtText::renderFlags(), QwtText::textSize()
+ */
+QRectF QwtPlotTextLabel::textRect( 
+    const QRectF &rect, const QSizeF &textSize ) const
+{
+    return qwtItemRect( d_data->text.renderFlags(), rect, textSize );
+}
+
+//!  Invalidate all internal cache
+void QwtPlotTextLabel::invalidateCache()
+{
+    d_data->pixmap = QPixmap();
+}
diff --git a/source/third_party/qwt/qwt_plot_tradingcurve.cpp b/source/third_party/qwt/qwt_plot_tradingcurve.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b99d466ab4d8803af3a05d4334ae75cf4fd713b4
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_tradingcurve.cpp
@@ -0,0 +1,682 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_tradingcurve.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_clipper.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+
+static inline bool qwtIsSampleInside( const QwtOHLCSample &sample,
+    double tMin, double tMax, double vMin, double vMax )
+{
+    const double t = sample.time;
+    const QwtInterval interval = sample.boundingInterval();
+
+    const bool isOffScreen = ( t < tMin ) || ( t > tMax )
+        || ( interval.maxValue() < vMin ) || ( interval.minValue() > vMax );
+
+    return !isOffScreen;
+}
+
+class QwtPlotTradingCurve::PrivateData
+{
+public:
+    PrivateData():
+        symbolStyle( QwtPlotTradingCurve::CandleStick ),
+        symbolExtent( 0.6 ),
+        minSymbolWidth( 2.0 ),
+        maxSymbolWidth( -1.0 ),
+        paintAttributes( QwtPlotTradingCurve::ClipSymbols )
+    {
+        symbolBrush[0] = QBrush( Qt::white );
+        symbolBrush[1] = QBrush( Qt::black );
+    }
+
+    QwtPlotTradingCurve::SymbolStyle symbolStyle;
+    double symbolExtent;
+    double minSymbolWidth;
+    double maxSymbolWidth;
+
+    QPen symbolPen;
+    QBrush symbolBrush[2]; // Increasing/Decreasing
+
+    QwtPlotTradingCurve::PaintAttributes paintAttributes;
+};
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotTradingCurve::QwtPlotTradingCurve( const QwtText &title ):
+    QwtPlotSeriesItem( title )
+{
+    init();
+}
+
+/*!
+  Constructor
+  \param title Title of the curve
+*/
+QwtPlotTradingCurve::QwtPlotTradingCurve( const QString &title ):
+    QwtPlotSeriesItem( QwtText( title ) )
+{
+    init();
+}
+
+//! Destructor
+QwtPlotTradingCurve::~QwtPlotTradingCurve()
+{
+    delete d_data;
+}
+
+//! Initialize internal members
+void QwtPlotTradingCurve::init()
+{
+    setItemAttribute( QwtPlotItem::Legend, true );
+    setItemAttribute( QwtPlotItem::AutoScale, true );
+
+    d_data = new PrivateData;
+    setData( new QwtTradingChartData() );
+
+    setZ( 19.0 );
+}
+
+//! \return QwtPlotItem::Rtti_PlotTradingCurve
+int QwtPlotTradingCurve::rtti() const
+{
+    return QwtPlotTradingCurve::Rtti_PlotTradingCurve;
+}
+
+/*!
+  Specify an attribute how to draw the curve
+
+  \param attribute Paint attribute
+  \param on On/Off
+  \sa testPaintAttribute()
+*/
+void QwtPlotTradingCurve::setPaintAttribute(
+    PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+    \return True, when attribute is enabled
+    \sa PaintAttribute, setPaintAttribute()
+*/
+bool QwtPlotTradingCurve::testPaintAttribute(
+    PaintAttribute attribute ) const
+{
+    return ( d_data->paintAttributes & attribute );
+}
+
+/*!
+  Initialize data with an array of samples.
+  \param samples Vector of samples
+
+  \sa QwtPlotSeriesItem::setData()
+*/
+void QwtPlotTradingCurve::setSamples(
+    const QVector<QwtOHLCSample> &samples )
+{
+    setData( new QwtTradingChartData( samples ) );
+}
+
+/*!
+  Assign a series of samples
+    
+  setSamples() is just a wrapper for setData() without any additional
+  value - beside that it is easier to find for the developer.
+    
+  \param data Data
+  \warning The item takes ownership of the data object, deleting
+           it when its not used anymore. 
+*/
+void QwtPlotTradingCurve::setSamples(
+    QwtSeriesData<QwtOHLCSample> *data )
+{
+    setData( data );
+}   
+
+/*!
+  Set the symbol style
+
+  \param style Symbol style
+
+  \sa symbolStyle(), setSymbolExtent(),
+      setSymbolPen(), setSymbolBrush()
+*/
+void QwtPlotTradingCurve::setSymbolStyle( SymbolStyle style )
+{
+    if ( style != d_data->symbolStyle )
+    {
+        d_data->symbolStyle = style;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Symbol style
+  \sa setSymbolStyle(), symbolExtent(), symbolPen(), symbolBrush()
+*/
+QwtPlotTradingCurve::SymbolStyle QwtPlotTradingCurve::symbolStyle() const
+{
+    return d_data->symbolStyle;
+}
+
+/*! 
+  Build and assign the symbol pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotTradingCurve::setSymbolPen( 
+    const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setSymbolPen( QPen( color, width, style ) );
+}
+
+/*!
+  \brief Set the symbol pen
+
+  The symbol pen is used for rendering the lines of the
+  bar or candlestick symbols
+
+  \sa symbolPen(), setSymbolBrush()
+*/
+void QwtPlotTradingCurve::setSymbolPen( const QPen &pen )
+{
+    if ( pen != d_data->symbolPen )
+    {
+        d_data->symbolPen = pen;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Symbol pen
+  \sa setSymbolPen(), symbolBrush()
+*/
+QPen QwtPlotTradingCurve::symbolPen() const
+{
+    return d_data->symbolPen;
+}
+
+/*!
+  Set the symbol brush
+
+  \param direction Direction type
+  \param brush Brush used to fill the body of all candlestick
+               symbols with the direction
+
+  \sa symbolBrush(), setSymbolPen()
+*/
+void QwtPlotTradingCurve::setSymbolBrush(
+    Direction direction, const QBrush &brush )
+{
+    if ( direction < 0 || direction >= 2 )
+        return;
+
+    if ( brush != d_data->symbolBrush[ direction ] )
+    {
+        d_data->symbolBrush[ direction ] = brush;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \param direction
+  \return Brush used to fill the body of all candlestick
+          symbols with the direction
+
+  \sa setSymbolPen(), symbolBrush()
+*/
+QBrush QwtPlotTradingCurve::symbolBrush( Direction direction ) const
+{
+    if ( direction < 0 || direction >= 2 )
+        return QBrush();
+
+    return d_data->symbolBrush[ direction ];
+}
+
+/*!
+  \brief Set the extent of the symbol
+
+  The width of the symbol is given in scale coordinates. When painting
+  a symbol the width is scaled into paint device coordinates
+  by scaledSymbolWidth(). The scaled width is bounded by
+  minSymbolWidth(), maxSymbolWidth()
+
+  \param extent Symbol width in scale coordinates
+
+  \sa symbolExtent(), scaledSymbolWidth(), 
+      setMinSymbolWidth(), setMaxSymbolWidth()
+*/
+void QwtPlotTradingCurve::setSymbolExtent( double extent )
+{
+    extent = qMax( 0.0, extent );
+    if ( extent != d_data->symbolExtent )
+    {
+        d_data->symbolExtent = extent;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Extent of a symbol in scale coordinates
+  \sa setSymbolExtent(), scaledSymbolWidth(),
+      minSymbolWidth(), maxSymbolWidth()
+*/
+double QwtPlotTradingCurve::symbolExtent() const
+{
+    return d_data->symbolExtent;
+}
+
+/*!
+  Set a minimum for the symbol width
+
+  \param width Width in paint device coordinates
+  \sa minSymbolWidth(), setMaxSymbolWidth(), setSymbolExtent()
+ */
+void QwtPlotTradingCurve::setMinSymbolWidth( double width )
+{
+    width = qMax( width, 0.0 );
+    if ( width != d_data->minSymbolWidth )
+    {
+        d_data->minSymbolWidth = width;
+
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Minmum for the symbol width
+  \sa setMinSymbolWidth(), maxSymbolWidth(), symbolExtent()
+ */
+double QwtPlotTradingCurve::minSymbolWidth() const
+{
+    return d_data->minSymbolWidth;
+}
+
+/*!
+  Set a maximum for the symbol width
+
+  A value <= 0.0 means an unlimited width
+
+  \param width Width in paint device coordinates
+  \sa maxSymbolWidth(), setMinSymbolWidth(), setSymbolExtent()
+ */
+void QwtPlotTradingCurve::setMaxSymbolWidth( double width )
+{
+    if ( width != d_data->maxSymbolWidth )
+    {
+        d_data->maxSymbolWidth = width;
+    
+        legendChanged();
+        itemChanged();
+    }
+}
+
+/*!
+  \return Maximum for the symbol width
+  \sa setMaxSymbolWidth(), minSymbolWidth(), symbolExtent()
+ */
+double QwtPlotTradingCurve::maxSymbolWidth() const
+{
+    return d_data->maxSymbolWidth;
+}
+
+/*!
+  \return Bounding rectangle of all samples.
+  For an empty series the rectangle is invalid.
+*/
+QRectF QwtPlotTradingCurve::boundingRect() const
+{
+    QRectF rect = QwtPlotSeriesItem::boundingRect();
+    if ( rect.isValid() && orientation() == Qt::Vertical )
+        rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() );
+
+    return rect;
+}
+
+/*!
+  Draw an interval of the curve
+
+  \param painter Painter
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted. If to < 0 the
+         curve will be painted to its last point.
+
+  \sa drawSymbols()
+*/
+void QwtPlotTradingCurve::drawSeries( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    if ( to < 0 )
+        to = dataSize() - 1;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( from > to )
+        return;
+
+    painter->save();
+
+    if ( d_data->symbolStyle != QwtPlotTradingCurve::NoSymbol )
+        drawSymbols( painter, xMap, yMap, canvasRect, from, to );
+
+    painter->restore();
+}
+
+/*!
+  Draw symbols
+
+  \param painter Painter
+  \param xMap x map
+  \param yMap y map
+  \param canvasRect Contents rectangle of the canvas
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \sa drawSeries()
+*/
+void QwtPlotTradingCurve::drawSymbols( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect, int from, int to ) const
+{
+    const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect );
+
+    const QwtScaleMap *timeMap, *valueMap;
+    double tMin, tMax, vMin, vMax;
+
+    const Qt::Orientation orient = orientation();
+    if ( orient == Qt::Vertical )
+    {
+        timeMap = &xMap;
+        valueMap = &yMap;
+
+        tMin = tr.left();
+        tMax = tr.right();
+        vMin = tr.top();
+        vMax = tr.bottom();
+    }
+    else
+    {
+        timeMap = &yMap;
+        valueMap = &xMap;
+
+        vMin = tr.left();
+        vMax = tr.right();
+        tMin = tr.top();
+        tMax = tr.bottom();
+    }
+
+    const bool inverted = timeMap->isInverting();
+    const bool doClip = d_data->paintAttributes & ClipSymbols;
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double symbolWidth = scaledSymbolWidth( xMap, yMap, canvasRect );
+    if ( doAlign )
+        symbolWidth = qFloor( 0.5 * symbolWidth ) * 2.0;
+
+    QPen pen = d_data->symbolPen;
+    pen.setCapStyle( Qt::FlatCap );
+
+    painter->setPen( pen );
+
+    for ( int i = from; i <= to; i++ )
+    {
+        const QwtOHLCSample s = sample( i );
+
+        if ( !doClip || qwtIsSampleInside( s, tMin, tMax, vMin, vMax ) )
+        {
+            QwtOHLCSample translatedSample;
+
+            translatedSample.time = timeMap->transform( s.time );
+            translatedSample.open = valueMap->transform( s.open );
+            translatedSample.high = valueMap->transform( s.high );
+            translatedSample.low = valueMap->transform( s.low );
+            translatedSample.close = valueMap->transform( s.close );
+
+            const int brushIndex = ( s.open < s.close )
+                ? QwtPlotTradingCurve::Increasing
+                : QwtPlotTradingCurve::Decreasing;
+
+            if ( doAlign )
+            {
+                translatedSample.time = qRound( translatedSample.time );
+                translatedSample.open = qRound( translatedSample.open );
+                translatedSample.high = qRound( translatedSample.high );
+                translatedSample.low = qRound( translatedSample.low );
+                translatedSample.close = qRound( translatedSample.close );
+            }
+
+            switch( d_data->symbolStyle )
+            {
+                case Bar:
+                {
+                    drawBar( painter, translatedSample, 
+                        orient, inverted, symbolWidth );
+                    break;
+                }
+                case CandleStick:
+                {
+                    painter->setBrush( d_data->symbolBrush[ brushIndex ] );
+                    drawCandleStick( painter, translatedSample, 
+                        orient, symbolWidth );
+                    break;
+                }
+                default:
+                {
+                    if ( d_data->symbolStyle >= UserSymbol )
+                    {
+                        painter->setBrush( d_data->symbolBrush[ brushIndex ] );
+                        drawUserSymbol( painter, d_data->symbolStyle,
+                            translatedSample, orient, inverted, symbolWidth );
+                    }
+                }
+            }
+        }
+    }
+}
+
+/*!
+  \brief Draw a symbol for a symbol style >= UserSymbol
+
+  The implementation does nothing and is intended to be overloaded
+
+  \param painter Qt painter, initialized with pen/brush
+  \param symbolStyle Symbol style
+  \param sample Samples already translated into paint device coordinates
+  \param orientation Vertical or horizontal
+  \param inverted True, when the opposite scale 
+                  ( Qt::Vertical: x, Qt::Horizontal: y ) is increasing
+                  in the opposite direction as QPainter coordinates.
+  \param symbolWidth Width of the symbol in paint device coordinates
+*/
+void QwtPlotTradingCurve::drawUserSymbol( QPainter *painter,
+    SymbolStyle symbolStyle, const QwtOHLCSample &sample,
+    Qt::Orientation orientation, bool inverted, double symbolWidth ) const
+{
+    Q_UNUSED( painter )
+    Q_UNUSED( symbolStyle )
+    Q_UNUSED( orientation )
+    Q_UNUSED( inverted )
+    Q_UNUSED( symbolWidth )
+    Q_UNUSED( sample )
+}
+
+/*!
+  \brief Draw a bar
+
+  \param painter Qt painter, initialized with pen/brush
+  \param sample Sample, already translated into paint device coordinates
+  \param orientation Vertical or horizontal
+  \param inverted When inverted is false the open tick is painted
+                  to the left/top, otherwise it is painted right/bottom.
+                  The close tick is painted in the opposite direction
+                  of the open tick.
+                  painted in the opposite d
+                  opposite direction.
+  \param width Width or height of the candle, depending on the orientation
+
+  \sa Bar
+*/
+void QwtPlotTradingCurve::drawBar( QPainter *painter,
+    const QwtOHLCSample &sample, Qt::Orientation orientation, 
+    bool inverted, double width ) const
+{
+    double w2 = 0.5 * width;
+    if ( inverted )
+        w2 *= -1;
+
+    if ( orientation == Qt::Vertical )
+    {
+        QwtPainter::drawLine( painter,
+            sample.time, sample.low, sample.time, sample.high );
+
+        QwtPainter::drawLine( painter,
+            sample.time - w2, sample.open, sample.time, sample.open );
+        QwtPainter::drawLine( painter,
+            sample.time + w2, sample.close, sample.time, sample.close );
+    }
+    else
+    {
+        QwtPainter::drawLine( painter, sample.low, sample.time,
+            sample.high, sample.time );
+        QwtPainter::drawLine( painter,
+            sample.open, sample.time - w2, sample.open, sample.time );
+        QwtPainter::drawLine( painter,
+            sample.close, sample.time + w2, sample.close, sample.time );
+    }
+}
+
+/*!
+  \brief Draw a candle stick
+
+  \param painter Qt painter, initialized with pen/brush
+  \param sample Samples already translated into paint device coordinates
+  \param orientation Vertical or horizontal
+  \param width Width or height of the candle, depending on the orientation
+
+  \sa CandleStick
+*/
+void QwtPlotTradingCurve::drawCandleStick( QPainter *painter,
+    const QwtOHLCSample &sample, Qt::Orientation orientation, 
+    double width ) const
+{
+    const double t = sample.time;
+    const double v1 = qMin( sample.low, sample.high );
+    const double v2 = qMin( sample.open, sample.close );
+    const double v3 = qMax( sample.low, sample.high );
+    const double v4 = qMax( sample.open, sample.close );
+
+    if ( orientation == Qt::Vertical )
+    {
+        QwtPainter::drawLine( painter, t, v1, t, v2 );
+        QwtPainter::drawLine( painter, t, v3, t, v4 );
+
+        QRectF rect( t - 0.5 * width, sample.open,
+            width, sample.close - sample.open );
+
+        QwtPainter::drawRect( painter, rect );
+    }
+    else
+    {
+        QwtPainter::drawLine( painter, v1, t, v2, t );
+        QwtPainter::drawLine( painter, v3, t, v4, t );
+
+        const QRectF rect( sample.open, t - 0.5 * width,
+            sample.close - sample.open, width );
+
+        QwtPainter::drawRect( painter, rect );
+    }
+}
+
+/*!
+  \return A rectangle filled with the color of the symbol pen
+
+  \param index Index of the legend entry 
+                ( usually there is only one )
+  \param size Icon size
+
+  \sa setLegendIconSize(), legendData()
+*/
+QwtGraphic QwtPlotTradingCurve::legendIcon( int index,
+    const QSizeF &size ) const
+{
+    Q_UNUSED( index );
+    return defaultIcon( d_data->symbolPen.color(), size );
+}
+
+/*!
+  Calculate the symbol width in paint coordinates
+
+  The width is calculated by scaling the symbol extent into
+  paint device coordinates bounded by the minimum/maximum
+  symbol width.
+
+  \param xMap Maps x-values into pixel coordinates.
+  \param yMap Maps y-values into pixel coordinates.
+  \param canvasRect Contents rectangle of the canvas
+
+  \return Symbol width in paint coordinates
+
+  \sa symbolExtent(), minSymbolWidth(), maxSymbolWidth()
+*/
+double QwtPlotTradingCurve::scaledSymbolWidth(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    Q_UNUSED( canvasRect );
+
+    if ( d_data->maxSymbolWidth > 0.0 &&
+        d_data->minSymbolWidth >= d_data->maxSymbolWidth )
+    {
+        return d_data->minSymbolWidth;
+    }
+
+    const QwtScaleMap *map =
+        ( orientation() == Qt::Vertical ) ? &xMap : &yMap;
+
+    const double pos = map->transform( map->s1() + d_data->symbolExtent ); 
+
+    double width = qAbs( pos - map->p1() );
+
+    width = qMax( width,  d_data->minSymbolWidth );
+    if ( d_data->maxSymbolWidth > 0.0 )
+        width = qMin( width, d_data->maxSymbolWidth );
+
+    return width;
+}
diff --git a/source/third_party/qwt/qwt_plot_xml.cpp b/source/third_party/qwt/qwt_plot_xml.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a62a0f04d23e9cfdb660cc8a052f45f87edffb20
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_xml.cpp
@@ -0,0 +1,42 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot.h"
+
+/*!
+  This method is intended for manipulating the plot widget
+  from a specific editor in the Qwt designer plugin.
+
+  \warning The plot editor has never been implemented.
+*/
+void QwtPlot::applyProperties( const QString & /* xmlDocument */ )
+{
+#if 0
+    // Temporary dummy code, for designer tests
+    setTitle( xmlDocument );
+    replot();
+#endif
+}
+
+/*!
+  This method is intended for manipulating the plot widget
+  from a specific editor in the Qwt designer plugin.
+
+  \return QString::null
+  \warning The plot editor has never been implemented.
+*/
+QString QwtPlot::grabProperties() const
+{
+#if 0
+    // Temporary dummy code, for designer tests
+    return title().text();
+#else
+    return QString::null;
+#endif
+}
diff --git a/source/third_party/qwt/qwt_plot_zoneitem.cpp b/source/third_party/qwt/qwt_plot_zoneitem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cf74a79f102c2a3cb54713a4d99814dc8f835210
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_zoneitem.cpp
@@ -0,0 +1,315 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_zoneitem.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_map.h"
+#include <qpainter.h>
+
+class QwtPlotZoneItem::PrivateData
+{   
+public:
+    PrivateData():
+        orientation( Qt::Vertical ),
+        pen( Qt::NoPen )
+    {
+        QColor c( Qt::darkGray );
+        c.setAlpha( 100 );
+        brush = QBrush( c );
+    }
+        
+    Qt::Orientation orientation;
+    QPen pen;
+    QBrush brush;
+    QwtInterval interval;
+};  
+
+/*!
+   \brief Constructor
+
+   Initializes the zone with no pen and a semi transparent gray brush
+
+   Sets the following item attributes:
+
+   - QwtPlotItem::AutoScale: false
+   - QwtPlotItem::Legend:    false
+
+   The z value is initialized by 5
+
+   \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
+*/
+QwtPlotZoneItem::QwtPlotZoneItem():
+    QwtPlotItem( QwtText( "Zone" ) )
+{
+    d_data = new PrivateData;
+
+    setItemAttribute( QwtPlotItem::AutoScale, false );
+    setItemAttribute( QwtPlotItem::Legend, false );
+
+    setZ( 5 );
+}
+
+//! Destructor
+QwtPlotZoneItem::~QwtPlotZoneItem()
+{
+    delete d_data;
+}
+
+//! \return QwtPlotItem::Rtti_PlotZone
+int QwtPlotZoneItem::rtti() const
+{
+    return QwtPlotItem::Rtti_PlotZone;
+}
+
+/*! 
+  Build and assign a pen
+    
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
+  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
+  to hide this incompatibility.
+    
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+    
+  \sa pen(), brush()
+ */ 
+void QwtPlotZoneItem::setPen( const QColor &color, qreal width, Qt::PenStyle style )
+{   
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  \brief Assign a pen 
+
+  The pen is used to draw the border lines of the zone
+
+  \param pen Pen
+  \sa pen(), setBrush()
+*/
+void QwtPlotZoneItem::setPen( const QPen &pen )
+{
+    if ( d_data->pen != pen )
+    {
+        d_data->pen = pen;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Pen used to draw the border lines
+  \sa setPen(), brush()
+*/
+const QPen &QwtPlotZoneItem::pen() const
+{
+    return d_data->pen;
+}
+
+/*! 
+  \brief Assign a brush 
+    
+  The brush is used to fill the zone
+
+  \param brush Brush
+  \sa pen(), setBrush()
+*/
+void QwtPlotZoneItem::setBrush( const QBrush &brush )
+{
+    if ( d_data->brush != brush )
+    {
+        d_data->brush = brush;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Brush used to fill the zone
+  \sa setPen(), brush()
+*/
+const QBrush &QwtPlotZoneItem::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  \brief Set the orientation of the zone
+
+  A horizontal zone highlights an interval of the y axis, 
+  a vertical zone of the x axis. It is unbounded in the 
+  opposite direction.
+
+  \sa orientation(), QwtPlotItem::setAxes()
+*/
+void QwtPlotZoneItem::setOrientation( Qt::Orientation orientation )
+{
+    if ( d_data->orientation != orientation )
+    {
+        d_data->orientation = orientation;
+        itemChanged();
+    }
+}
+
+/*!
+  \return Orientation of the zone
+  \sa setOrientation()
+ */
+Qt::Orientation QwtPlotZoneItem::orientation()
+{
+    return d_data->orientation;
+}
+
+/*!
+  Set the interval of the zone
+
+  For a horizontal zone the interval is related to the y axis,
+  for a vertical zone it is related to the x axis.
+
+  \param min Minimum of the interval
+  \param max Maximum of the interval
+
+  \sa interval(), setOrientation()
+ */
+void QwtPlotZoneItem::setInterval( double min, double max )
+{
+    setInterval( QwtInterval( min, max ) );
+}
+
+/*!
+  Set the interval of the zone
+
+  For a horizontal zone the interval is related to the y axis,
+  for a vertical zone it is related to the x axis.
+
+  \param interval Zone interval
+
+  \sa interval(), setOrientation()
+ */
+void QwtPlotZoneItem::setInterval( const QwtInterval &interval )
+{
+    if ( d_data->interval != interval )
+    {
+        d_data->interval = interval;
+        itemChanged(); 
+    }   
+}   
+
+/*!
+  \return Zone interval
+  \sa setInterval(), orientation()
+ */
+QwtInterval QwtPlotZoneItem::interval() const
+{
+    return d_data->interval;
+}   
+
+/*!
+  Draw the zone
+
+  \param painter Painter
+  \param xMap x Scale Map
+  \param yMap y Scale Map
+  \param canvasRect Contents rectangle of the canvas in painter coordinates
+*/
+
+void QwtPlotZoneItem::draw( QPainter *painter,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QRectF &canvasRect ) const
+{
+    if ( !d_data->interval.isValid() )
+        return;
+
+    QPen pen = d_data->pen;
+    pen.setCapStyle( Qt::FlatCap );
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        double y1 = yMap.transform( d_data->interval.minValue() );
+        double y2 = yMap.transform( d_data->interval.maxValue() );
+
+        if ( doAlign )
+        {
+            y1 = qRound( y1 );
+            y2 = qRound( y2 );
+        }
+
+        QRectF r( canvasRect.left(), y1, canvasRect.width(), y2 - y1 );
+        r = r.normalized();
+
+        if ( ( d_data->brush.style() != Qt::NoBrush ) && ( y1 != y2 ) )
+        {
+            QwtPainter::fillRect( painter, r, d_data->brush );
+        }
+
+        if ( d_data->pen.style() != Qt::NoPen )
+        {
+            painter->setPen( d_data->pen );
+
+            QwtPainter::drawLine( painter, r.left(), r.top(), r.right(), r.top() );
+            QwtPainter::drawLine( painter, r.left(), r.bottom(), r.right(), r.bottom() );
+        }
+    }
+    else
+    {
+        double x1 = xMap.transform( d_data->interval.minValue() );
+        double x2 = xMap.transform( d_data->interval.maxValue() );
+
+        if ( doAlign )
+        {
+            x1 = qRound( x1 );
+            x2 = qRound( x2 );
+        }
+
+        QRectF r( x1, canvasRect.top(), x2 - x1, canvasRect.height() );
+        r = r.normalized();
+
+        if ( ( d_data->brush.style() != Qt::NoBrush ) && ( x1 != x2 ) )
+        {
+            QwtPainter::fillRect( painter, r, d_data->brush );
+        }
+
+        if ( d_data->pen.style() != Qt::NoPen )
+        {
+            painter->setPen( d_data->pen );
+
+            QwtPainter::drawLine( painter, r.left(), r.top(), r.left(), r.bottom() );
+            QwtPainter::drawLine( painter, r.right(), r.top(), r.right(), r.bottom() );
+        }
+    }
+}
+
+/*! 
+  The bounding rectangle is build from the interval in one direction
+  and something invalid for the opposite direction.
+
+  \return An invalid rectangle with valid boundaries in one direction
+*/
+QRectF QwtPlotZoneItem::boundingRect() const
+{
+    QRectF br = QwtPlotItem::boundingRect();
+
+    const QwtInterval &intv = d_data->interval;
+
+    if ( intv.isValid() )
+    {
+        if ( d_data->orientation == Qt::Horizontal )
+        {
+            br.setTop( intv.minValue() );
+            br.setBottom( intv.maxValue() );
+        }
+        else
+        {
+            br.setLeft( intv.minValue() );
+            br.setRight( intv.maxValue() );
+        }
+    }
+
+    return br;
+}
diff --git a/source/third_party/qwt/qwt_plot_zoomer.cpp b/source/third_party/qwt/qwt_plot_zoomer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ea8f2ae562e4815df29f85639980c30dbbf9ef1
--- /dev/null
+++ b/source/third_party/qwt/qwt_plot_zoomer.cpp
@@ -0,0 +1,664 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_plot_zoomer.h"
+#include "qwt/qwt_plot.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_picker_machine.h"
+#include <qalgorithms.h>
+
+static QwtInterval qwtExpandedZoomInterval( double v1, double v2, 
+    double minRange, const QwtTransform* transform )
+{
+    double min = v1;
+    double max = v2;
+
+    if ( max - min < minRange )
+    {
+        min = 0.5 * ( min + max - minRange );
+        max = min + minRange;
+
+        if ( transform )
+        {
+            // f.e the logarithmic scale doesn't allow values
+            // outside [QwtLogTransform::LogMin/QwtLogTransform::LogMax]
+
+            double minBounded = transform->bounded( min );
+            double maxBounded = transform->bounded( max );
+
+            if ( minBounded != min )
+            {
+                maxBounded = transform->bounded( minBounded + minRange );
+            }
+            else if ( maxBounded != max )
+            {
+                minBounded = transform->bounded( maxBounded - minRange );
+            }
+
+            min = minBounded;
+            max = maxBounded;
+        }
+    }
+
+    return QwtInterval( min, max );
+}
+
+static QRectF qwtExpandedZoomRect( const QRectF &zoomRect, const QSizeF &minSize,
+    const QwtTransform* transformX, const QwtTransform* transformY )
+{
+    QRectF r = zoomRect;
+
+    if ( minSize.width() > r.width() )
+    {
+        const QwtInterval intv = qwtExpandedZoomInterval(
+            r.left(), r.right(), minSize.width(), transformX );
+
+        r.setLeft( intv.minValue() );
+        r.setRight( intv.maxValue() );
+    }
+
+    if ( minSize.height() > r.height() )
+    {
+        const QwtInterval intv = qwtExpandedZoomInterval(
+            zoomRect.top(), zoomRect.bottom(), minSize.height(), transformY );
+
+        r.setTop( intv.minValue() );
+        r.setBottom( intv.maxValue() );
+    }
+
+    return r;
+}
+
+class QwtPlotZoomer::PrivateData
+{
+public:
+    uint zoomRectIndex;
+    QStack<QRectF> zoomStack;
+
+    int maxStackDepth;
+};
+
+/*!
+  \brief Create a zoomer for a plot canvas.
+
+  The zoomer is set to those x- and y-axis of the parent plot of the
+  canvas that are enabled. If both or no x-axis are enabled, the picker
+  is set to QwtPlot::xBottom. If both or no y-axis are
+  enabled, it is set to QwtPlot::yLeft.
+
+  The zoomer is initialized with a QwtPickerDragRectMachine,
+  the tracker mode is set to QwtPicker::ActiveOnly and the rubber band
+  is set to QwtPicker::RectRubberBand
+
+  \param canvas Plot canvas to observe, also the parent object
+  \param doReplot Call QwtPlot::replot() for the attached plot before initializing
+                  the zoomer with its scales. This might be necessary,
+                  when the plot is in a state with pending scale changes.
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase()
+*/
+QwtPlotZoomer::QwtPlotZoomer( QWidget *canvas, bool doReplot ):
+    QwtPlotPicker( canvas )
+{
+    if ( canvas )
+        init( doReplot );
+}
+
+/*!
+  \brief Create a zoomer for a plot canvas.
+
+  The zoomer is initialized with a QwtPickerDragRectMachine,
+  the tracker mode is set to QwtPicker::ActiveOnly and the rubber band
+  is set to QwtPicker;;RectRubberBand
+
+  \param xAxis X axis of the zoomer
+  \param yAxis Y axis of the zoomer
+  \param canvas Plot canvas to observe, also the parent object
+  \param doReplot Call QwtPlot::replot() for the attached plot before initializing
+                  the zoomer with its scales. This might be necessary,
+                  when the plot is in a state with pending scale changes.
+
+  \sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase()
+*/
+
+QwtPlotZoomer::QwtPlotZoomer( int xAxis, int yAxis,
+        QWidget *canvas, bool doReplot ):
+    QwtPlotPicker( xAxis, yAxis, canvas )
+{
+    if ( canvas )
+        init( doReplot );
+}
+
+//! Init the zoomer, used by the constructors
+void QwtPlotZoomer::init( bool doReplot )
+{
+    d_data = new PrivateData;
+
+    d_data->maxStackDepth = -1;
+
+    setTrackerMode( ActiveOnly );
+    setRubberBand( RectRubberBand );
+    setStateMachine( new QwtPickerDragRectMachine() );
+
+    if ( doReplot && plot() )
+        plot()->replot();
+
+    setZoomBase( scaleRect() );
+}
+
+QwtPlotZoomer::~QwtPlotZoomer()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Limit the number of recursive zoom operations to depth.
+
+  A value of -1 set the depth to unlimited, 0 disables zooming.
+  If the current zoom rectangle is below depth, the plot is unzoomed.
+
+  \param depth Maximum for the stack depth
+  \sa maxStackDepth()
+  \note depth doesn't include the zoom base, so zoomStack().count() might be
+              maxStackDepth() + 1.
+*/
+void QwtPlotZoomer::setMaxStackDepth( int depth )
+{
+    d_data->maxStackDepth = depth;
+
+    if ( depth >= 0 )
+    {
+        // unzoom if the current depth is below d_data->maxStackDepth
+
+        const int zoomOut =
+            int( d_data->zoomStack.count() ) - 1 - depth; // -1 for the zoom base
+
+        if ( zoomOut > 0 )
+        {
+            zoom( -zoomOut );
+            for ( int i = int( d_data->zoomStack.count() ) - 1;
+                i > int( d_data->zoomRectIndex ); i-- )
+            {
+                ( void )d_data->zoomStack.pop(); // remove trailing rects
+            }
+        }
+    }
+}
+
+/*!
+  \return Maximal depth of the zoom stack.
+  \sa setMaxStackDepth()
+*/
+int QwtPlotZoomer::maxStackDepth() const
+{
+    return d_data->maxStackDepth;
+}
+
+/*!
+  \return The zoom stack. zoomStack()[0] is the zoom base,
+          zoomStack()[1] the first zoomed rectangle.
+
+  \sa setZoomStack(), zoomRectIndex()
+*/
+const QStack<QRectF> &QwtPlotZoomer::zoomStack() const
+{
+    return d_data->zoomStack;
+}
+
+/*!
+  \return Initial rectangle of the zoomer
+  \sa setZoomBase(), zoomRect()
+*/
+QRectF QwtPlotZoomer::zoomBase() const
+{
+    return d_data->zoomStack[0];
+}
+
+/*!
+  Reinitialized the zoom stack with scaleRect() as base.
+
+  \param doReplot Call QwtPlot::replot() for the attached plot before initializing
+                  the zoomer with its scales. This might be necessary,
+                  when the plot is in a state with pending scale changes.
+
+  \sa zoomBase(), scaleRect() QwtPlot::autoReplot(), QwtPlot::replot().
+*/
+void QwtPlotZoomer::setZoomBase( bool doReplot )
+{
+    QwtPlot *plt = plot();
+    if ( plt == NULL )
+        return;
+
+    if ( doReplot )
+        plt->replot();
+
+    d_data->zoomStack.clear();
+    d_data->zoomStack.push( scaleRect() );
+    d_data->zoomRectIndex = 0;
+
+    rescale();
+}
+
+/*!
+  \brief Set the initial size of the zoomer.
+
+  base is united with the current scaleRect() and the zoom stack is
+  reinitialized with it as zoom base. plot is zoomed to scaleRect().
+
+  \param base Zoom base
+
+  \sa zoomBase(), scaleRect()
+*/
+void QwtPlotZoomer::setZoomBase( const QRectF &base )
+{
+    const QwtPlot *plt = plot();
+    if ( !plt )
+        return;
+
+    const QRectF sRect = scaleRect();
+    const QRectF bRect = base | sRect;
+
+    d_data->zoomStack.clear();
+    d_data->zoomStack.push( bRect );
+    d_data->zoomRectIndex = 0;
+
+    if ( base != sRect )
+    {
+        d_data->zoomStack.push( sRect );
+        d_data->zoomRectIndex++;
+    }
+
+    rescale();
+}
+
+/*!
+  \return Rectangle at the current position on the zoom stack.
+  \sa zoomRectIndex(), scaleRect().
+*/
+QRectF QwtPlotZoomer::zoomRect() const
+{
+    return d_data->zoomStack[d_data->zoomRectIndex];
+}
+
+/*!
+  \return Index of current position of zoom stack.
+*/
+uint QwtPlotZoomer::zoomRectIndex() const
+{
+    return d_data->zoomRectIndex;
+}
+
+/*!
+  \brief Zoom in
+
+  Clears all rectangles above the current position of the
+  zoom stack and pushes the normalized rectangle on it.
+
+  \note If the maximal stack depth is reached, zoom is ignored.
+  \note The zoomed signal is emitted.
+*/
+
+void QwtPlotZoomer::zoom( const QRectF &rect )
+{
+    if ( d_data->maxStackDepth >= 0 &&
+            int( d_data->zoomRectIndex ) >= d_data->maxStackDepth )
+    {
+        return;
+    }
+
+    const QRectF zoomRect = rect.normalized();
+    if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] )
+    {
+        for ( uint i = int( d_data->zoomStack.count() ) - 1;
+           i > d_data->zoomRectIndex; i-- )
+        {
+            ( void )d_data->zoomStack.pop();
+        }
+
+        d_data->zoomStack.push( zoomRect );
+        d_data->zoomRectIndex++;
+
+        rescale();
+
+        Q_EMIT zoomed( zoomRect );
+    }
+}
+
+/*!
+  \brief Zoom in or out
+
+  Activate a rectangle on the zoom stack with an offset relative
+  to the current position. Negative values of offset will zoom out,
+  positive zoom in. A value of 0 zooms out to the zoom base.
+
+  \param offset Offset relative to the current position of the zoom stack.
+  \note The zoomed signal is emitted.
+  \sa zoomRectIndex()
+*/
+void QwtPlotZoomer::zoom( int offset )
+{
+    if ( offset == 0 )
+        d_data->zoomRectIndex = 0;
+    else
+    {
+        int newIndex = d_data->zoomRectIndex + offset;
+        newIndex = qMax( 0, newIndex );
+        newIndex = qMin( int( d_data->zoomStack.count() ) - 1, newIndex );
+
+        d_data->zoomRectIndex = uint( newIndex );
+    }
+
+    rescale();
+
+    Q_EMIT zoomed( zoomRect() );
+}
+
+/*!
+  \brief Assign a zoom stack
+
+  In combination with other types of navigation it might be useful to
+  modify to manipulate the complete zoom stack.
+
+  \param zoomStack New zoom stack
+  \param zoomRectIndex Index of the current position of zoom stack.
+                       In case of -1 the current position is at the top
+                       of the stack.
+
+  \note The zoomed signal might be emitted.
+  \sa zoomStack(), zoomRectIndex()
+*/
+void QwtPlotZoomer::setZoomStack(
+    const QStack<QRectF> &zoomStack, int zoomRectIndex )
+{
+    if ( zoomStack.isEmpty() )
+        return;
+
+    if ( d_data->maxStackDepth >= 0 &&
+        int( zoomStack.count() ) > d_data->maxStackDepth )
+    {
+        return;
+    }
+
+    if ( zoomRectIndex < 0 || zoomRectIndex > int( zoomStack.count() ) )
+        zoomRectIndex = zoomStack.count() - 1;
+
+    const bool doRescale = zoomStack[zoomRectIndex] != zoomRect();
+
+    d_data->zoomStack = zoomStack;
+    d_data->zoomRectIndex = uint( zoomRectIndex );
+
+    if ( doRescale )
+    {
+        rescale();
+        Q_EMIT zoomed( zoomRect() );
+    }
+}
+
+/*!
+  Adjust the observed plot to zoomRect()
+
+  \note Initiates QwtPlot::replot()
+*/
+
+void QwtPlotZoomer::rescale()
+{
+    QwtPlot *plt = plot();
+    if ( !plt )
+        return;
+
+    const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
+    if ( rect != scaleRect() )
+    {
+        const bool doReplot = plt->autoReplot();
+        plt->setAutoReplot( false );
+
+        double x1 = rect.left();
+        double x2 = rect.right();
+        if ( !plt->axisScaleDiv( xAxis() ).isIncreasing() )
+            qSwap( x1, x2 );
+
+        plt->setAxisScale( xAxis(), x1, x2 );
+
+        double y1 = rect.top();
+        double y2 = rect.bottom();
+        if ( !plt->axisScaleDiv( yAxis() ).isIncreasing() )
+            qSwap( y1, y2 );
+
+        plt->setAxisScale( yAxis(), y1, y2 );
+
+        plt->setAutoReplot( doReplot );
+
+        plt->replot();
+    }
+}
+
+/*!
+  Reinitialize the axes, and set the zoom base to their scales.
+
+  \param xAxis X axis
+  \param yAxis Y axis
+*/
+
+void QwtPlotZoomer::setAxis( int xAxis, int yAxis )
+{
+    if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() )
+    {
+        QwtPlotPicker::setAxis( xAxis, yAxis );
+        setZoomBase( scaleRect() );
+    }
+}
+
+/*!
+   Qt::MidButton zooms out one position on the zoom stack,
+   Qt::RightButton to the zoom base.
+
+   Changes the current position on the stack, but doesn't pop
+   any rectangle.
+
+   \note The mouse events can be changed, using
+         QwtEventPattern::setMousePattern: 2, 1
+*/
+void QwtPlotZoomer::widgetMouseReleaseEvent( QMouseEvent *me )
+{
+    if ( mouseMatch( MouseSelect2, me ) )
+        zoom( 0 );
+    else if ( mouseMatch( MouseSelect3, me ) )
+        zoom( -1 );
+    else if ( mouseMatch( MouseSelect6, me ) )
+        zoom( +1 );
+    else
+        QwtPlotPicker::widgetMouseReleaseEvent( me );
+}
+
+/*!
+   Qt::Key_Plus zooms in, Qt::Key_Minus zooms out one position on the
+   zoom stack, Qt::Key_Escape zooms out to the zoom base.
+
+   Changes the current position on the stack, but doesn't pop
+   any rectangle.
+
+   \note The keys codes can be changed, using
+         QwtEventPattern::setKeyPattern: 3, 4, 5
+*/
+
+void QwtPlotZoomer::widgetKeyPressEvent( QKeyEvent *ke )
+{
+    if ( !isActive() )
+    {
+        if ( keyMatch( KeyUndo, ke ) )
+            zoom( -1 );
+        else if ( keyMatch( KeyRedo, ke ) )
+            zoom( +1 );
+        else if ( keyMatch( KeyHome, ke ) )
+            zoom( 0 );
+    }
+
+    QwtPlotPicker::widgetKeyPressEvent( ke );
+}
+
+/*!
+  Move the current zoom rectangle.
+
+  \param dx X offset
+  \param dy Y offset
+
+  \note The changed rectangle is limited by the zoom base
+*/
+void QwtPlotZoomer::moveBy( double dx, double dy )
+{
+    const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
+    moveTo( QPointF( rect.left() + dx, rect.top() + dy ) );
+}
+
+/*!
+  Move the the current zoom rectangle.
+
+  \param pos New position
+
+  \sa QRectF::moveTo()
+  \note The changed rectangle is limited by the zoom base
+*/
+void QwtPlotZoomer::moveTo( const QPointF &pos )
+{
+    double x = pos.x();
+    double y = pos.y();
+
+    if ( x < zoomBase().left() )
+        x = zoomBase().left();
+    if ( x > zoomBase().right() - zoomRect().width() )
+        x = zoomBase().right() - zoomRect().width();
+
+    if ( y < zoomBase().top() )
+        y = zoomBase().top();
+    if ( y > zoomBase().bottom() - zoomRect().height() )
+        y = zoomBase().bottom() - zoomRect().height();
+
+    if ( x != zoomRect().left() || y != zoomRect().top() )
+    {
+        d_data->zoomStack[d_data->zoomRectIndex].moveTo( x, y );
+        rescale();
+    }
+}
+
+/*!
+  \brief Check and correct a selected rectangle
+
+  Reject rectangles with a height or width < 2, otherwise
+  expand the selected rectangle to a minimum size of 11x11
+  and accept it.
+
+  \return true If the rectangle is accepted, or has been changed
+          to an accepted one.
+*/
+
+bool QwtPlotZoomer::accept( QPolygon &pa ) const
+{
+    if ( pa.count() < 2 )
+        return false;
+
+    QRect rect = QRect( pa[0], pa[int( pa.count() ) - 1] );
+    rect = rect.normalized();
+
+    const int minSize = 2;
+    if ( rect.width() < minSize && rect.height() < minSize )
+        return false;
+
+    const int minZoomSize = 11;
+
+    const QPoint center = rect.center();
+    rect.setSize( rect.size().expandedTo( QSize( minZoomSize, minZoomSize ) ) );
+    rect.moveCenter( center );
+
+    pa.resize( 2 );
+    pa[0] = rect.topLeft();
+    pa[1] = rect.bottomRight();
+
+    return true;
+}
+
+/*!
+  \brief Limit zooming by a minimum rectangle
+
+  \return zoomBase().width() / 10e4, zoomBase().height() / 10e4
+*/
+QSizeF QwtPlotZoomer::minZoomSize() const
+{
+    return QSizeF( d_data->zoomStack[0].width() / 10e4,
+        d_data->zoomStack[0].height() / 10e4 );
+}
+
+/*!
+  Rejects selections, when the stack depth is too deep, or
+  the zoomed rectangle is minZoomSize().
+
+  \sa minZoomSize(), maxStackDepth()
+*/
+void QwtPlotZoomer::begin()
+{
+    if ( d_data->maxStackDepth >= 0 )
+    {
+        if ( d_data->zoomRectIndex >= uint( d_data->maxStackDepth ) )
+            return;
+    }
+
+    const QSizeF minSize = minZoomSize();
+    if ( minSize.isValid() )
+    {
+        const QSizeF sz =
+            d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999;
+
+        if ( minSize.width() >= sz.width() &&
+            minSize.height() >= sz.height() )
+        {
+            return;
+        }
+    }
+
+    QwtPlotPicker::begin();
+}
+
+/*!
+  Expand the selected rectangle to minZoomSize() and zoom in
+  if accepted.
+
+  \param ok If true, complete the selection and emit selected signals
+            otherwise discard the selection.
+
+  \sa accept(), minZoomSize()
+  \return True if the selection has been accepted, false otherwise
+*/
+bool QwtPlotZoomer::end( bool ok )
+{
+    ok = QwtPlotPicker::end( ok );
+    if ( !ok )
+        return false;
+
+    QwtPlot *plot = QwtPlotZoomer::plot();
+    if ( !plot )
+        return false;
+
+    const QPolygon &pa = selection();
+    if ( pa.count() < 2 )
+        return false;
+
+    QRect rect = QRect( pa[0], pa[int( pa.count() - 1 )] );
+    rect = rect.normalized();
+
+    const QwtScaleMap xMap = plot->canvasMap( xAxis() );
+    const QwtScaleMap yMap = plot->canvasMap( yAxis() );
+
+    QRectF zoomRect = QwtScaleMap::invTransform( xMap, yMap, rect ).normalized();
+
+    zoomRect = qwtExpandedZoomRect( zoomRect, minZoomSize(),
+        xMap.transformation(), yMap.transformation() );
+
+    zoom( zoomRect );
+
+    return true;
+}
diff --git a/source/third_party/qwt/qwt_point_3d.cpp b/source/third_party/qwt/qwt_point_3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d489a3b71f327f2f8d390f7b1411281c3bc72953
--- /dev/null
+++ b/source/third_party/qwt/qwt_point_3d.cpp
@@ -0,0 +1,22 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_point_3d.h"
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtPoint3D &point )
+{
+    debug.nospace() << "QwtPoint3D(" << point.x() 
+        << "," << point.y() << "," << point.z() << ")";
+    return debug.space();
+}
+
+#endif
+
diff --git a/source/third_party/qwt/qwt_point_data.cpp b/source/third_party/qwt/qwt_point_data.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..86cde212c3cacab0aab4898568d12b1efb9c79d4
--- /dev/null
+++ b/source/third_party/qwt/qwt_point_data.cpp
@@ -0,0 +1,307 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_point_data.h"
+#include "qwt/qwt_math.h"
+#include <string.h>
+
+/*!
+  Constructor
+
+  \param x Array of x values
+  \param y Array of y values
+
+  \sa QwtPlotCurve::setData(), QwtPlotCurve::setSamples()
+*/
+QwtPointArrayData::QwtPointArrayData(
+        const QVector<double> &x, const QVector<double> &y ):
+    d_x( x ),
+    d_y( y )
+{
+}
+
+/*!
+  Constructor
+
+  \param x Array of x values
+  \param y Array of y values
+  \param size Size of the x and y arrays
+  \sa QwtPlotCurve::setData(), QwtPlotCurve::setSamples()
+*/
+QwtPointArrayData::QwtPointArrayData( const double *x,
+        const double *y, size_t size )
+{
+    d_x.resize( size );
+    ::memcpy( d_x.data(), x, size * sizeof( double ) );
+
+    d_y.resize( size );
+    ::memcpy( d_y.data(), y, size * sizeof( double ) );
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtPointArrayData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+//! \return Size of the data set
+size_t QwtPointArrayData::size() const
+{
+    return qMin( d_x.size(), d_y.size() );
+}
+
+/*!
+  Return the sample at position i
+
+  \param index Index
+  \return Sample at position i
+*/
+QPointF QwtPointArrayData::sample( size_t index ) const
+{
+    return QPointF( d_x[int( index )], d_y[int( index )] );
+}
+
+//! \return Array of the x-values
+const QVector<double> &QwtPointArrayData::xData() const
+{
+    return d_x;
+}
+
+//! \return Array of the y-values
+const QVector<double> &QwtPointArrayData::yData() const
+{
+    return d_y;
+}
+
+/*!
+  Constructor
+
+  \param x Array of x values
+  \param y Array of y values
+  \param size Size of the x and y arrays
+
+  \warning The programmer must assure that the memory blocks referenced
+           by the pointers remain valid during the lifetime of the
+           QwtPlotCPointer object.
+
+  \sa QwtPlotCurve::setData(), QwtPlotCurve::setRawSamples()
+*/
+QwtCPointerData::QwtCPointerData(
+        const double *x, const double *y, size_t size ):
+    d_x( x ),
+    d_y( y ),
+    d_size( size )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtCPointerData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+//! \return Size of the data set
+size_t QwtCPointerData::size() const
+{
+    return d_size;
+}
+
+/*!
+  Return the sample at position i
+
+  \param index Index
+  \return Sample at position i
+*/
+QPointF QwtCPointerData::sample( size_t index ) const
+{
+    return QPointF( d_x[int( index )], d_y[int( index )] );
+}
+
+//! \return Array of the x-values
+const double *QwtCPointerData::xData() const
+{
+    return d_x;
+}
+
+//! \return Array of the y-values
+const double *QwtCPointerData::yData() const
+{
+    return d_y;
+}
+
+/*!
+   Constructor
+
+   \param size Number of points
+   \param interval Bounding interval for the points
+
+   \sa setInterval(), setSize()
+*/
+QwtSyntheticPointData::QwtSyntheticPointData(
+        size_t size, const QwtInterval &interval ):
+    d_size( size ),
+    d_interval( interval )
+{
+}
+
+/*!
+  Change the number of points
+
+  \param size Number of points
+  \sa size(), setInterval()
+*/
+void QwtSyntheticPointData::setSize( size_t size )
+{
+    d_size = size;
+}
+
+/*!
+  \return Number of points
+  \sa setSize(), interval()
+*/
+size_t QwtSyntheticPointData::size() const
+{
+    return d_size;
+}
+
+/*!
+   Set the bounding interval
+
+   \param interval Interval
+   \sa interval(), setSize()
+*/
+void QwtSyntheticPointData::setInterval( const QwtInterval &interval )
+{
+    d_interval = interval.normalized();
+}
+
+/*!
+   \return Bounding interval
+   \sa setInterval(), size()
+*/
+QwtInterval QwtSyntheticPointData::interval() const
+{
+    return d_interval;
+}
+
+/*!
+   Set a the "rectangle of interest"
+
+   QwtPlotSeriesItem defines the current area of the plot canvas
+   as "rect of interest" ( QwtPlotSeriesItem::updateScaleDiv() ).
+
+   If interval().isValid() == false the x values are calculated
+   in the interval rect.left() -> rect.right().
+
+   \sa rectOfInterest()
+*/
+void QwtSyntheticPointData::setRectOfInterest( const QRectF &rect )
+{
+    d_rectOfInterest = rect;
+    d_intervalOfInterest = QwtInterval(
+        rect.left(), rect.right() ).normalized();
+}
+
+/*!
+   \return "rectangle of interest"
+   \sa setRectOfInterest()
+*/
+QRectF QwtSyntheticPointData::rectOfInterest() const
+{
+    return d_rectOfInterest;
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  This implementation iterates over all points, what could often
+  be implemented much faster using the characteristics of the series.
+  When there are many points it is recommended to overload and
+  reimplement this method using the characteristics of the series
+  ( if possible ).
+
+  \return Bounding rectangle
+*/
+QRectF QwtSyntheticPointData::boundingRect() const
+{
+    if ( d_size == 0 ||
+        !( d_interval.isValid() || d_intervalOfInterest.isValid() ) )
+    {
+        return QRectF( 1.0, 1.0, -2.0, -2.0 ); // something invalid
+    }
+
+    return qwtBoundingRect( *this );
+}
+
+/*!
+   Calculate the point from an index
+
+   \param index Index
+   \return QPointF(x(index), y(x(index)));
+
+   \warning For invalid indices ( index < 0 || index >= size() )
+            (0, 0) is returned.
+*/
+QPointF QwtSyntheticPointData::sample( size_t index ) const
+{
+    if ( index >= d_size )
+        return QPointF( 0, 0 );
+
+    const double xValue = x( index );
+    const double yValue = y( xValue );
+
+    return QPointF( xValue, yValue );
+}
+
+/*!
+   Calculate a x-value from an index
+
+   x values are calculated by dividing an interval into
+   equidistant steps. If !interval().isValid() the
+   interval is calculated from the "rectangle of interest".
+
+   \param index Index of the requested point 
+   \return Calculated x coordinate
+
+   \sa interval(), rectOfInterest(), y()
+*/
+double QwtSyntheticPointData::x( uint index ) const
+{
+    const QwtInterval &interval = d_interval.isValid() ?
+        d_interval : d_intervalOfInterest;
+
+    if ( !interval.isValid() ) 
+        return 0.0;
+
+    if ( d_size <= 1 )
+        return interval.minValue();
+
+    const double dx = interval.width() / ( d_size - 1 );
+    return interval.minValue() + index * dx;
+}
diff --git a/source/third_party/qwt/qwt_point_mapper.cpp b/source/third_party/qwt/qwt_point_mapper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f537c1a687ed90bd426315b094e552e68de58013
--- /dev/null
+++ b/source/third_party/qwt/qwt_point_mapper.cpp
@@ -0,0 +1,717 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_point_mapper.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_pixel_matrix.h"
+#include <qpolygon.h>
+#include <qimage.h>
+#include <qpen.h>
+#include <qpainter.h>
+
+#if QT_VERSION >= 0x040400
+
+#include <qthread.h>
+#include <qfuture.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+
+#if !defined(QT_NO_QFUTURE)
+#define QWT_USE_THREADS 0
+#endif
+
+#endif
+
+static QRectF qwtInvalidRect( 0.0, 0.0, -1.0, -1.0 );
+
+// Helper class to work around the 5 parameters
+// limitation of QtConcurrent::run()
+class QwtDotsCommand
+{
+public:
+    const QwtSeriesData<QPointF> *series;
+    int from;
+    int to;
+    QRgb rgb;
+};
+
+static void qwtRenderDots(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtDotsCommand command, const QPoint &pos, QImage *image ) 
+{
+    const QRgb rgb = command.rgb;
+    QRgb *bits = reinterpret_cast<QRgb *>( image->bits() );
+
+    const int w = image->width();
+    const int h = image->height();
+
+    const int x0 = pos.x();
+    const int y0 = pos.y();
+
+    for ( int i = command.from; i <= command.to; i++ )
+    {
+        const QPointF sample = command.series->sample( i );
+
+        const int x = static_cast<int>( xMap.transform( sample.x() ) + 0.5 ) - x0;
+        const int y = static_cast<int>( yMap.transform( sample.y() ) + 0.5 ) - y0;
+
+        if ( x >= 0 && x < w && y >= 0 && y < h )
+            bits[ y * w + x ] = rgb;
+    }
+}
+
+static inline int qwtRoundValue( double value )
+{
+    return qRound( value );
+}
+
+// some functors, so that the compile can inline
+struct QwtRoundI
+{
+    inline int operator()( double value )
+    {
+        return qwtRoundValue( value );
+    }
+};
+
+struct QwtRoundF
+{
+    inline double operator()( double value )
+    {
+#if 1
+        // MS Windows and at least IRIX does not have C99's nearbyint() function
+        return ( value >= 0.0 ) ? ::floor( value + 0.5 ) : ::ceil( value - 0.5 );
+#else
+        // slightly faster than the code above
+        return nearbyint( value );
+#endif
+    }
+};
+
+struct QwtNoRoundF
+{   
+    inline double operator()( double value )
+    {
+        return value;
+    }
+};
+
+// mapping points without any filtering - beside checking
+// the bounding rectangle
+
+template<class Polygon, class Point, class Round>
+static inline Polygon qwtToPoints( 
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, 
+    int from, int to, Round round )
+{
+    Polygon polyline( to - from + 1 );
+    Point *points = polyline.data();
+
+    int numPoints = 0;
+
+    if ( boundingRect.isValid() )
+    {
+        // iterating over all values
+        // filtering out all points outside of
+        // the bounding rectangle
+
+        for ( int i = from; i <= to; i++ )
+        {
+            const QPointF sample = series->sample( i );
+
+            const double x = xMap.transform( sample.x() );
+            const double y = yMap.transform( sample.y() );
+
+            if ( boundingRect.contains( x, y ) )
+            {
+                points[ numPoints ].rx() = round( x );
+                points[ numPoints ].ry() = round( y );
+
+                numPoints++;
+            }
+        }
+
+        polyline.resize( numPoints );
+    }
+    else
+    {
+        // simply iterating over all values
+        // without any filtering
+
+        for ( int i = from; i <= to; i++ )
+        {
+            const QPointF sample = series->sample( i );
+
+            const double x = xMap.transform( sample.x() );
+            const double y = yMap.transform( sample.y() );
+
+            points[ numPoints ].rx() = round( x );
+            points[ numPoints ].ry() = round( y );
+
+            numPoints++;
+        }
+    }
+
+    return polyline;
+}
+
+static inline QPolygon qwtToPointsI(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series,
+    int from, int to )
+{
+    return qwtToPoints<QPolygon, QPoint>( 
+        boundingRect, xMap, yMap, series, from, to, QwtRoundI() );
+}
+
+template<class Round>
+static inline QPolygonF qwtToPointsF(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series,
+    int from, int to, Round round )
+{
+    return qwtToPoints<QPolygonF, QPointF>( 
+        boundingRect, xMap, yMap, series, from, to, round );
+}
+
+// Mapping points with filtering out consecutive
+// points mapped to the same position
+
+template<class Polygon, class Point, class Round>
+static inline Polygon qwtToPolylineFiltered( 
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, 
+    int from, int to, Round round )
+{
+    // in curves with many points consecutive points
+    // are often mapped to the same position. As this might
+    // result in empty lines ( or symbols hidden by others )
+    // we try to filter them out
+
+    Polygon polyline( to - from + 1 );
+    Point *points = polyline.data();
+
+    const QPointF sample0 = series->sample( from );
+
+    points[0].rx() = round( xMap.transform( sample0.x() ) );
+    points[0].ry() = round( yMap.transform( sample0.y() ) );
+
+    int pos = 0;
+    for ( int i = from + 1; i <= to; i++ )
+    {
+        const QPointF sample = series->sample( i );
+
+        const Point p( round( xMap.transform( sample.x() ) ),
+            round( yMap.transform( sample.y() ) ) );
+
+        if ( points[pos] != p )
+            points[++pos] = p;
+    }
+
+    polyline.resize( pos + 1 );
+    return polyline;
+}
+
+static inline QPolygon qwtToPolylineFilteredI(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series,
+    int from, int to )
+{
+    return qwtToPolylineFiltered<QPolygon, QPoint>(
+        xMap, yMap, series, from, to, QwtRoundI() );
+}
+
+template<class Round>
+static inline QPolygonF qwtToPolylineFilteredF(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series,
+    int from, int to, Round round )
+{
+    return qwtToPolylineFiltered<QPolygonF, QPointF>(
+        xMap, yMap, series, from, to, round );
+} 
+
+template<class Polygon, class Point>
+static inline Polygon qwtToPointsFiltered(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to )
+{
+    // F.e. in scatter plots ( no connecting lines ) we
+    // can sort out all duplicates ( not only consecutive points )
+
+    Polygon polygon( to - from + 1 );
+    Point *points = polygon.data();
+
+    QwtPixelMatrix pixelMatrix( boundingRect.toAlignedRect() );
+
+    int numPoints = 0;
+    for ( int i = from; i <= to; i++ )
+    {
+        const QPointF sample = series->sample( i );
+
+        const int x = qwtRoundValue( xMap.transform( sample.x() ) );
+        const int y = qwtRoundValue( yMap.transform( sample.y() ) );
+
+        if ( pixelMatrix.testAndSetPixel( x, y, true ) == false )
+        {
+            points[ numPoints ].rx() = x;
+            points[ numPoints ].ry() = y;
+
+            numPoints++;
+        }
+    }
+
+    polygon.resize( numPoints );
+    return polygon;
+}
+
+static inline QPolygon qwtToPointsFilteredI(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to )
+{
+    return qwtToPointsFiltered<QPolygon, QPoint>(
+        boundingRect, xMap, yMap, series, from, to );
+} 
+
+static inline QPolygonF qwtToPointsFilteredF(
+    const QRectF &boundingRect,
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to )
+{
+    return qwtToPointsFiltered<QPolygonF, QPointF>(
+        boundingRect, xMap, yMap, series, from, to );
+}
+
+class QwtPointMapper::PrivateData
+{
+public:
+    PrivateData():
+        boundingRect( qwtInvalidRect )
+    {
+    }
+
+    QRectF boundingRect;
+    QwtPointMapper::TransformationFlags flags;
+};
+
+//! Constructor
+QwtPointMapper::QwtPointMapper()
+{
+    d_data = new PrivateData();
+}
+
+//! Destructor
+QwtPointMapper::~QwtPointMapper()
+{
+    delete d_data;
+}
+
+/*!
+  Set the flags affecting the transformation process
+
+  \param flags Flags
+  \sa flags(), setFlag()
+ */
+void QwtPointMapper::setFlags( TransformationFlags flags )
+{
+    d_data->flags = flags;
+}
+
+/*!
+  \return Flags affecting the transformation process
+  \sa setFlags(), setFlag()
+ */
+QwtPointMapper::TransformationFlags QwtPointMapper::flags() const
+{
+    return d_data->flags;
+}
+
+/*!
+  Modify a flag affecting the transformation process
+
+  \param flag Flag type
+  \param on Value
+
+  \sa flag(), setFlags()
+ */
+void QwtPointMapper::setFlag( TransformationFlag flag, bool on )
+{
+    if ( on )
+        d_data->flags |= flag;
+    else
+        d_data->flags &= ~flag;
+}
+
+/*!
+  \return True, when the flag is set
+  \param flag Flag type
+  \sa setFlag(), setFlags()
+ */
+bool QwtPointMapper::testFlag( TransformationFlag flag ) const
+{
+    return d_data->flags & flag;
+}
+
+/*!
+  Set a bounding rectangle for the point mapping algorithm
+
+  A valid bounding rectangle can be used for optimizations
+
+  \param rect Bounding rectangle
+  \sa boundingRect()
+ */
+void QwtPointMapper::setBoundingRect( const QRectF &rect )
+{
+    d_data->boundingRect = rect;
+}
+
+/*!
+  \return Bounding rectangle
+  \sa setBoundingRect()
+ */
+QRectF QwtPointMapper::boundingRect() const
+{
+    return d_data->boundingRect;
+}
+
+/*!
+  \brief Translate a series of points into a QPolygonF
+
+  When the WeedOutPoints flag is enabled consecutive points,
+  that are mapped to the same position will be one point. 
+
+  When RoundPoints is set all points are rounded to integers
+  but returned as PolygonF - what only makes sense
+  when the further processing of the values need a QPolygonF.
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \return Translated polygon
+*/
+QPolygonF QwtPointMapper::toPolygonF(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to ) const
+{
+    QPolygonF polyline;
+
+    if ( d_data->flags & WeedOutPoints )
+    {
+        if ( d_data->flags & RoundPoints )
+        {
+            polyline = qwtToPolylineFilteredF( 
+                xMap, yMap, series, from, to, QwtRoundF() );
+        }
+        else
+        {
+            polyline = qwtToPolylineFilteredF( 
+                xMap, yMap, series, from, to, QwtNoRoundF() );
+        }
+    }
+    else
+    {
+        if ( d_data->flags & RoundPoints )
+        {
+            polyline = qwtToPointsF( qwtInvalidRect, 
+                xMap, yMap, series, from, to, QwtRoundF() );
+        }
+        else
+        {
+            polyline = qwtToPointsF( qwtInvalidRect, 
+                xMap, yMap, series, from, to, QwtNoRoundF() );
+        }
+    }
+
+    return polyline;
+}
+
+/*!
+  \brief Translate a series of points into a QPolygon
+
+  When the WeedOutPoints flag is enabled consecutive points,
+  that are mapped to the same position will be one point. 
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \return Translated polygon
+*/
+QPolygon QwtPointMapper::toPolygon(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to ) const
+{
+    QPolygon polyline;
+
+    if ( d_data->flags & WeedOutPoints )
+    {
+        polyline = qwtToPolylineFilteredI( 
+            xMap, yMap, series, from, to );
+    }
+    else
+    {
+        polyline = qwtToPointsI( 
+            qwtInvalidRect, xMap, yMap, series, from, to );
+    }
+
+    return polyline;
+}
+
+/*!
+  \brief Translate a series into a QPolygonF
+
+  - WeedOutPoints & RoundPoints & boundingRect().isValid()
+    All points that are mapped to the same position 
+    will be one point. Points outside of the bounding
+    rectangle are ignored.
+ 
+  - WeedOutPoints & RoundPoints & !boundingRect().isValid()
+    All consecutive points that are mapped to the same position 
+    will one point
+
+  - WeedOutPoints & !RoundPoints 
+    All consecutive points that are mapped to the same position 
+    will one point
+
+  - !WeedOutPoints & boundingRect().isValid()
+    Points outside of the bounding rectangle are ignored.
+
+  When RoundPoints is set all points are rounded to integers
+  but returned as PolygonF - what only makes sense
+  when the further processing of the values need a QPolygonF.
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \return Translated polygon
+*/
+QPolygonF QwtPointMapper::toPointsF(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to ) const
+{
+    QPolygonF points;
+
+    if ( d_data->flags & WeedOutPoints )
+    {
+        if ( d_data->flags & RoundPoints )
+        {
+            if ( d_data->boundingRect.isValid() )
+            {   
+                points = qwtToPointsFilteredF( d_data->boundingRect,
+                    xMap, yMap, series, from, to );
+            }
+            else
+            {   
+                // without a bounding rectangle all we can
+                // do is to filter out duplicates of
+                // consecutive points
+
+                points = qwtToPolylineFilteredF( 
+                    xMap, yMap, series, from, to, QwtRoundF() );
+            }
+        }
+        else
+        {
+            // when rounding is not allowed we can't use
+            // qwtToPointsFilteredF
+
+            points = qwtToPolylineFilteredF( 
+                xMap, yMap, series, from, to, QwtNoRoundF() );
+        }
+    }
+    else
+    {
+        if ( d_data->flags & RoundPoints )
+        {
+            points = qwtToPointsF( d_data->boundingRect,
+                xMap, yMap, series, from, to, QwtRoundF() );
+        }
+        else
+        {
+            points = qwtToPointsF( d_data->boundingRect,
+                xMap, yMap, series, from, to, QwtNoRoundF() );
+        }
+    }
+
+    return points;
+}
+
+/*!
+  \brief Translate a series of points into a QPolygon
+
+  - WeedOutPoints & boundingRect().isValid()
+    All points that are mapped to the same position 
+    will be one point. Points outside of the bounding
+    rectangle are ignored.
+ 
+  - WeedOutPoints & !boundingRect().isValid()
+    All consecutive points that are mapped to the same position 
+    will one point
+
+  - !WeedOutPoints & boundingRect().isValid()
+    Points outside of the bounding rectangle are ignored.
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+
+  \return Translated polygon
+*/
+QPolygon QwtPointMapper::toPoints(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to ) const
+{
+    QPolygon points;
+
+    if ( d_data->flags & WeedOutPoints )
+    {
+        if ( d_data->boundingRect.isValid() )
+        {
+            points = qwtToPointsFilteredI( d_data->boundingRect,
+                xMap, yMap, series, from, to );
+        }
+        else
+        {
+            // when we don't have the bounding rectangle all
+            // we can do is to filter out consecutive duplicates
+
+            points = qwtToPolylineFilteredI( 
+                xMap, yMap, series, from, to );
+        }
+    }
+    else
+    {
+        points = qwtToPointsI( 
+            d_data->boundingRect, xMap, yMap, series, from, to );
+    }
+
+    return points;
+}
+
+
+/*!
+  \brief Translate a series into a QImage
+
+  \param xMap x map
+  \param yMap y map
+  \param series Series of points to be mapped
+  \param from Index of the first point to be painted
+  \param to Index of the last point to be painted
+  \param pen Pen used for drawing a point
+             of the image, where a point is mapped to
+  \param antialiased True, when the dots should be displayed
+                     antialiased
+  \param numThreads Number of threads to be used for rendering.
+                   If numThreads is set to 0, the system specific
+                   ideal thread count is used.
+
+  \return Image displaying the series
+*/
+QImage QwtPointMapper::toImage(
+    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
+    const QwtSeriesData<QPointF> *series, int from, int to, 
+    const QPen &pen, bool antialiased, uint numThreads ) const
+{
+    Q_UNUSED( antialiased )
+
+#if QWT_USE_THREADS
+    if ( numThreads == 0 )
+        numThreads = QThread::idealThreadCount();
+
+    if ( numThreads <= 0 )
+        numThreads = 1;
+#else
+    Q_UNUSED( numThreads )
+#endif
+
+    // a very special optimization for scatter plots
+    // where every sample is mapped to one pixel only.
+
+    const QRect rect = d_data->boundingRect.toAlignedRect();
+
+    QImage image( rect.size(), QImage::Format_ARGB32 );
+    image.fill( Qt::transparent );
+
+    if ( pen.width() <= 1 && pen.color().alpha() == 255 )
+    {
+        QwtDotsCommand command;
+        command.series = series;
+        command.rgb = pen.color().rgba();
+
+#if QWT_USE_THREADS
+        const int numPoints = ( to - from + 1 ) / numThreads;
+
+        QList< QFuture<void> > futures;
+        for ( uint i = 0; i < numThreads; i++ )
+        {
+            const QPoint pos = rect.topLeft();
+
+            const int index0 = from + i * numPoints;
+            if ( i == numThreads - 1 )
+            {
+                command.from = index0;
+                command.to = to;
+
+                qwtRenderDots( xMap, yMap, command, pos, &image );
+            }
+            else
+            {
+                command.from = index0;
+                command.to = index0 + numPoints - 1;
+
+                futures += QtConcurrent::run( &qwtRenderDots, 
+                    xMap, yMap, command, pos, &image );
+            }
+        }
+        for ( int i = 0; i < futures.size(); i++ )
+            futures[i].waitForFinished();
+#else
+        command.from = from;
+        command.to = to;
+
+        qwtRenderDots( xMap, yMap, command, rect.topLeft(), &image );
+#endif
+    }
+    else
+    {
+        // fallback implementation: to be replaced later by
+        // setting the pixels of the image like above, TODO ...
+
+        QPainter painter( &image );
+        painter.setPen( pen );
+        painter.setRenderHint( QPainter::Antialiasing, antialiased );
+
+        const int chunkSize = 1000;
+        for ( int i = from; i <= to; i += chunkSize )
+        {
+            const int indexTo = qMin( i + chunkSize - 1, to );
+            const QPolygon points = toPoints(
+                xMap, yMap, series, i, indexTo );
+
+            painter.drawPoints( points );
+        }
+    }
+
+    return image;
+}
diff --git a/source/third_party/qwt/qwt_point_polar.cpp b/source/third_party/qwt/qwt_point_polar.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..220c1e0682b5f023e5cde3de97718dd93f970e4c
--- /dev/null
+++ b/source/third_party/qwt/qwt_point_polar.cpp
@@ -0,0 +1,121 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * QwtPolar Widget Library
+ * Copyright (C) 2008   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_point_polar.h"
+#include "qwt/qwt_math.h"
+
+#if QT_VERSION < 0x040601
+#define qAtan2(y, x) ::atan2(y, x)
+#endif
+
+/*!
+   Convert and assign values from a point in Cartesian coordinates
+
+   \param p Point in Cartesian coordinates
+   \sa setPoint(), toPoint()
+*/
+QwtPointPolar::QwtPointPolar( const QPointF &p )
+{
+    d_radius = qSqrt( qwtSqr( p.x() ) + qwtSqr( p.y() ) );
+    d_azimuth = qAtan2( p.y(), p.x() );
+}
+
+/*!
+   Convert and assign values from a point in Cartesian coordinates
+   \param p Point in Cartesian coordinates
+*/
+void QwtPointPolar::setPoint( const QPointF &p )
+{
+    d_radius = qSqrt( qwtSqr( p.x() ) + qwtSqr( p.y() ) );
+    d_azimuth = qAtan2( p.y(), p.x() );
+}
+
+/*!
+   Convert and return values in Cartesian coordinates
+
+   \return Converted point in Cartesian coordinates
+
+   \note Invalid or null points will be returned as QPointF(0.0, 0.0)
+   \sa isValid(), isNull()
+*/
+QPointF QwtPointPolar::toPoint() const
+{
+    if ( d_radius <= 0.0 )
+        return QPointF( 0.0, 0.0 );
+
+    const double x = d_radius * qCos( d_azimuth );
+    const double y = d_radius * qSin( d_azimuth );
+
+    return QPointF( x, y );
+}
+
+/*!
+    \brief Compare 2 points
+
+    Two points are equal to each other if radius and
+    azimuth-coordinates are the same. Points are not equal, when
+    the azimuth differs, but other.azimuth() == azimuth() % (2 * PI).
+
+    \return True if the point is equal to other; otherwise return false.
+
+    \sa normalized()
+*/
+bool QwtPointPolar::operator==( const QwtPointPolar &other ) const
+{
+    return d_radius == other.d_radius && d_azimuth == other.d_azimuth;
+}
+
+/*!
+    Compare 2 points
+
+    Two points are equal to each other if radius and
+    azimuth-coordinates are the same. Points are not equal, when
+    the azimuth differs, but other.azimuth() == azimuth() % (2 * PI).
+
+    \return True if the point is not equal to other; otherwise return false.
+    \sa normalized()
+*/
+bool QwtPointPolar::operator!=( const QwtPointPolar &other ) const
+{
+    return d_radius != other.d_radius || d_azimuth != other.d_azimuth;
+}
+
+/*!
+   Normalize radius and azimuth
+
+   When the radius is < 0.0 it is set to 0.0. The azimuth is
+   a value >= 0.0 and < 2 * M_PI.
+
+   \return Normalized point
+*/
+QwtPointPolar QwtPointPolar::normalized() const
+{
+    const double radius = qMax( d_radius, 0.0 );
+
+    double azimuth = d_azimuth;
+    if ( azimuth < -2.0 * M_PI || azimuth >= 2 * M_PI )
+        azimuth = ::fmod( d_azimuth, 2 * M_PI );
+
+    if ( azimuth < 0.0 )
+        azimuth += 2 * M_PI;
+
+    return QwtPointPolar( azimuth, radius );
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtPointPolar &point )
+{
+    debug.nospace() << "QwtPointPolar(" 
+        << point.azimuth() << "," << point.radius() << ")";
+
+    return debug.space();
+}
+
+#endif
+
diff --git a/source/third_party/qwt/qwt_raster_data.cpp b/source/third_party/qwt/qwt_raster_data.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51de395e31664a6a8861c2adfc636548f26262ab
--- /dev/null
+++ b/source/third_party/qwt/qwt_raster_data.cpp
@@ -0,0 +1,404 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_raster_data.h"
+#include "qwt/qwt_point_3d.h"
+#include <qnumeric.h>
+
+class QwtRasterData::ContourPlane
+{
+public:
+    inline ContourPlane( double z ):
+        d_z( z )
+    {
+    }
+
+    inline bool intersect( const QwtPoint3D vertex[3],
+        QPointF line[2], bool ignoreOnPlane ) const;
+
+    inline double z() const { return d_z; }
+
+private:
+    inline int compare( double z ) const;
+    inline QPointF intersection(
+        const QwtPoint3D& p1, const QwtPoint3D &p2 ) const;
+
+    double d_z;
+};
+
+inline bool QwtRasterData::ContourPlane::intersect(
+    const QwtPoint3D vertex[3], QPointF line[2],
+    bool ignoreOnPlane ) const
+{
+    bool found = true;
+
+    // Are the vertices below (-1), on (0) or above (1) the plan ?
+    const int eq1 = compare( vertex[0].z() );
+    const int eq2 = compare( vertex[1].z() );
+    const int eq3 = compare( vertex[2].z() );
+
+    /*
+        (a) All the vertices lie below the contour level.
+        (b) Two vertices lie below and one on the contour level.
+        (c) Two vertices lie below and one above the contour level.
+        (d) One vertex lies below and two on the contour level.
+        (e) One vertex lies below, one on and one above the contour level.
+        (f) One vertex lies below and two above the contour level.
+        (g) Three vertices lie on the contour level.
+        (h) Two vertices lie on and one above the contour level.
+        (i) One vertex lies on and two above the contour level.
+        (j) All the vertices lie above the contour level.
+     */
+
+    static const int tab[3][3][3] =
+    {
+        // jump table to avoid nested case statements
+        { { 0, 0, 8 }, { 0, 2, 5 }, { 7, 6, 9 } },
+        { { 0, 3, 4 }, { 1, 10, 1 }, { 4, 3, 0 } },
+        { { 9, 6, 7 }, { 5, 2, 0 }, { 8, 0, 0 } }
+    };
+
+    const int edgeType = tab[eq1+1][eq2+1][eq3+1];
+    switch ( edgeType )
+    {
+        case 1:
+            // d(0,0,-1), h(0,0,1)
+            line[0] = vertex[0].toPoint();
+            line[1] = vertex[1].toPoint();
+            break;
+        case 2:
+            // d(-1,0,0), h(1,0,0)
+            line[0] = vertex[1].toPoint();
+            line[1] = vertex[2].toPoint();
+            break;
+        case 3:
+            // d(0,-1,0), h(0,1,0)
+            line[0] = vertex[2].toPoint();
+            line[1] = vertex[0].toPoint();
+            break;
+        case 4:
+            // e(0,-1,1), e(0,1,-1)
+            line[0] = vertex[0].toPoint();
+            line[1] = intersection( vertex[1], vertex[2] );
+            break;
+        case 5:
+            // e(-1,0,1), e(1,0,-1)
+            line[0] = vertex[1].toPoint();
+            line[1] = intersection( vertex[2], vertex[0] );
+            break;
+        case 6:
+            // e(-1,1,0), e(1,0,-1)
+            line[0] = vertex[2].toPoint();
+            line[1] = intersection( vertex[0], vertex[1] );
+            break;
+        case 7:
+            // c(-1,1,-1), f(1,1,-1)
+            line[0] = intersection( vertex[0], vertex[1] );
+            line[1] = intersection( vertex[1], vertex[2] );
+            break;
+        case 8:
+            // c(-1,-1,1), f(1,1,-1)
+            line[0] = intersection( vertex[1], vertex[2] );
+            line[1] = intersection( vertex[2], vertex[0] );
+            break;
+        case 9:
+            // f(-1,1,1), c(1,-1,-1)
+            line[0] = intersection( vertex[2], vertex[0] );
+            line[1] = intersection( vertex[0], vertex[1] );
+            break;
+        case 10:
+            // g(0,0,0)
+            // The CONREC algorithm has no satisfying solution for
+            // what to do, when all vertices are on the plane.
+
+            if ( ignoreOnPlane )
+                found = false;
+            else
+            {
+                line[0] = vertex[2].toPoint();
+                line[1] = vertex[0].toPoint();
+            }
+            break;
+        default:
+            found = false;
+    }
+
+    return found;
+}
+
+inline int QwtRasterData::ContourPlane::compare( double z ) const
+{
+    if ( z > d_z )
+        return 1;
+
+    if ( z < d_z )
+        return -1;
+
+    return 0;
+}
+
+inline QPointF QwtRasterData::ContourPlane::intersection(
+    const QwtPoint3D& p1, const QwtPoint3D &p2 ) const
+{
+    const double h1 = p1.z() - d_z;
+    const double h2 = p2.z() - d_z;
+
+    const double x = ( h2 * p1.x() - h1 * p2.x() ) / ( h2 - h1 );
+    const double y = ( h2 * p1.y() - h1 * p2.y() ) / ( h2 - h1 );
+
+    return QPointF( x, y );
+}
+
+//! Constructor
+QwtRasterData::QwtRasterData()
+{
+}
+
+//! Destructor
+QwtRasterData::~QwtRasterData()
+{
+}
+
+/*!
+   Set the bounding interval for the x, y or z coordinates.
+
+   \param axis Axis
+   \param interval Bounding interval
+
+   \sa interval()
+*/
+void QwtRasterData::setInterval( Qt::Axis axis, const QwtInterval &interval )
+{
+    d_intervals[axis] = interval;
+}
+
+/*!
+  \brief Initialize a raster
+
+  Before the composition of an image QwtPlotSpectrogram calls initRaster(),
+  announcing the area and its resolution that will be requested.
+
+  The default implementation does nothing, but for data sets that
+  are stored in files, it might be good idea to reimplement initRaster(),
+  where the data is resampled and loaded into memory.
+
+  \param area Area of the raster
+  \param raster Number of horizontal and vertical pixels
+
+  \sa initRaster(), value()
+*/
+void QwtRasterData::initRaster( const QRectF &area, const QSize &raster )
+{
+    Q_UNUSED( area );
+    Q_UNUSED( raster );
+}
+
+/*!
+  \brief Discard a raster
+
+  After the composition of an image QwtPlotSpectrogram calls discardRaster().
+
+  The default implementation does nothing, but if data has been loaded
+  in initRaster(), it could deleted now.
+
+  \sa initRaster(), value()
+*/
+void QwtRasterData::discardRaster()
+{
+}
+
+/*!
+   \brief Pixel hint
+
+   pixelHint() returns the geometry of a pixel, that can be used 
+   to calculate the resolution and alignment of the plot item, that is
+   representing the data. 
+   
+   Width and height of the hint need to be the horizontal  
+   and vertical distances between 2 neighbored points. 
+   The center of the hint has to be the position of any point 
+   ( it doesn't matter which one ).
+
+   An empty hint indicates, that there are values for any detail level.
+
+   Limiting the resolution of the image might significantly improve
+   the performance and heavily reduce the amount of memory when rendering
+   a QImage from the raster data. 
+
+   The default implementation returns an empty rectangle recommending
+   to render in target device ( f.e. screen ) resolution.
+
+   \param area In most implementations the resolution of the data doesn't
+               depend on the requested area.
+
+   \return Bounding rectangle of a pixel 
+*/
+QRectF QwtRasterData::pixelHint( const QRectF &area ) const
+{
+    Q_UNUSED( area );
+    return QRectF(); 
+}
+
+/*!
+   Calculate contour lines
+
+   \param rect Bounding rectangle for the contour lines
+   \param raster Number of data pixels of the raster data
+   \param levels List of limits, where to insert contour lines
+   \param flags Flags to customize the contouring algorithm
+
+   \return Calculated contour lines
+
+   An adaption of CONREC, a simple contouring algorithm.
+   http://local.wasp.uwa.edu.au/~pbourke/papers/conrec/
+*/
+QwtRasterData::ContourLines QwtRasterData::contourLines(
+    const QRectF &rect, const QSize &raster,
+    const QList<double> &levels, ConrecFlags flags ) const
+{
+    ContourLines contourLines;
+
+    if ( levels.size() == 0 || !rect.isValid() || !raster.isValid() )
+        return contourLines;
+
+    const double dx = rect.width() / raster.width();
+    const double dy = rect.height() / raster.height();
+
+    const bool ignoreOnPlane =
+        flags & QwtRasterData::IgnoreAllVerticesOnLevel;
+
+    const QwtInterval range = interval( Qt::ZAxis );
+    bool ignoreOutOfRange = false;
+    if ( range.isValid() )
+        ignoreOutOfRange = flags & IgnoreOutOfRange;
+
+    QwtRasterData *that = const_cast<QwtRasterData *>( this );
+    that->initRaster( rect, raster );
+
+    for ( int y = 0; y < raster.height() - 1; y++ )
+    {
+        enum Position
+        {
+            Center,
+
+            TopLeft,
+            TopRight,
+            BottomRight,
+            BottomLeft,
+
+            NumPositions
+        };
+
+        QwtPoint3D xy[NumPositions];
+
+        for ( int x = 0; x < raster.width() - 1; x++ )
+        {
+            const QPointF pos( rect.x() + x * dx, rect.y() + y * dy );
+
+            if ( x == 0 )
+            {
+                xy[TopRight].setX( pos.x() );
+                xy[TopRight].setY( pos.y() );
+                xy[TopRight].setZ(
+                    value( xy[TopRight].x(), xy[TopRight].y() )
+                );
+
+                xy[BottomRight].setX( pos.x() );
+                xy[BottomRight].setY( pos.y() + dy );
+                xy[BottomRight].setZ(
+                    value( xy[BottomRight].x(), xy[BottomRight].y() )
+                );
+            }
+
+            xy[TopLeft] = xy[TopRight];
+            xy[BottomLeft] = xy[BottomRight];
+
+            xy[TopRight].setX( pos.x() + dx );
+            xy[TopRight].setY( pos.y() );
+            xy[BottomRight].setX( pos.x() + dx );
+            xy[BottomRight].setY( pos.y() + dy );
+
+            xy[TopRight].setZ(
+                value( xy[TopRight].x(), xy[TopRight].y() )
+            );
+            xy[BottomRight].setZ(
+                value( xy[BottomRight].x(), xy[BottomRight].y() )
+            );
+
+            double zMin = xy[TopLeft].z();
+            double zMax = zMin;
+            double zSum = zMin;
+
+            for ( int i = TopRight; i <= BottomLeft; i++ )
+            {
+                const double z = xy[i].z();
+
+                zSum += z;
+                if ( z < zMin )
+                    zMin = z;
+                if ( z > zMax )
+                    zMax = z;
+            }
+
+            if ( qIsNaN( zSum ) )
+            {
+                // one of the points is NaN
+                continue;
+            }
+
+            if ( ignoreOutOfRange )
+            {
+                if ( !range.contains( zMin ) || !range.contains( zMax ) )
+                    continue;
+            }
+
+            if ( zMax < levels[0] ||
+                zMin > levels[levels.size() - 1] )
+            {
+                continue;
+            }
+
+            xy[Center].setX( pos.x() + 0.5 * dx );
+            xy[Center].setY( pos.y() + 0.5 * dy );
+            xy[Center].setZ( 0.25 * zSum );
+
+            const int numLevels = levels.size();
+            for ( int l = 0; l < numLevels; l++ )
+            {
+                const double level = levels[l];
+                if ( level < zMin || level > zMax )
+                    continue;
+                QPolygonF &lines = contourLines[level];
+                const ContourPlane plane( level );
+
+                QPointF line[2];
+                QwtPoint3D vertex[3];
+
+                for ( int m = TopLeft; m < NumPositions; m++ )
+                {
+                    vertex[0] = xy[m];
+                    vertex[1] = xy[0];
+                    vertex[2] = xy[m != BottomLeft ? m + 1 : TopLeft];
+
+                    const bool intersects =
+                        plane.intersect( vertex, line, ignoreOnPlane );
+                    if ( intersects )
+                    {
+                        lines += line[0];
+                        lines += line[1];
+                    }
+                }
+            }
+        }
+    }
+
+    that->discardRaster();
+
+    return contourLines;
+}
diff --git a/source/third_party/qwt/qwt_round_scale_draw.cpp b/source/third_party/qwt/qwt_round_scale_draw.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7db1e81bcec877349cded04b2d4218689fdff859
--- /dev/null
+++ b/source/third_party/qwt/qwt_round_scale_draw.cpp
@@ -0,0 +1,314 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_round_scale_draw.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_math.h"
+#include <qpen.h>
+#include <qpainter.h>
+#include <qfontmetrics.h>
+#include <qmath.h>
+
+class QwtRoundScaleDraw::PrivateData
+{
+public:
+    PrivateData():
+        center( 50.0, 50.0 ),
+        radius( 50.0 ),
+        startAngle( -135.0 ),
+        endAngle( 135.0 )
+    {
+    }
+
+    QPointF center;
+    double radius;
+
+    double startAngle;
+    double endAngle;
+};
+
+/*!
+  \brief Constructor
+
+  The range of the scale is initialized to [0, 100],
+  The center is set to (50, 50) with a radius of 50.
+  The angle range is set to [-135, 135].
+*/
+QwtRoundScaleDraw::QwtRoundScaleDraw()
+{
+    d_data = new QwtRoundScaleDraw::PrivateData;
+
+    setRadius( 50 );
+    scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
+}
+
+//! Destructor
+QwtRoundScaleDraw::~QwtRoundScaleDraw()
+{
+    delete d_data;
+}
+
+/*!
+  Change of radius the scale
+
+  Radius is the radius of the backbone without ticks and labels.
+
+  \param radius New Radius
+  \sa moveCenter()
+*/
+void QwtRoundScaleDraw::setRadius( double radius )
+{
+    d_data->radius = radius;
+}
+
+/*!
+  Get the radius
+
+  Radius is the radius of the backbone without ticks and labels.
+
+  \return Radius of the scale
+  \sa setRadius(), extent()
+*/
+double QwtRoundScaleDraw::radius() const
+{
+    return d_data->radius;
+}
+
+/*!
+   Move the center of the scale draw, leaving the radius unchanged
+
+   \param center New center
+   \sa setRadius()
+*/
+void QwtRoundScaleDraw::moveCenter( const QPointF &center )
+{
+    d_data->center = center;
+}
+
+//! Get the center of the scale
+QPointF QwtRoundScaleDraw::center() const
+{
+    return d_data->center;
+}
+
+/*!
+  \brief Adjust the baseline circle segment for round scales.
+
+  The baseline will be drawn from min(angle1,angle2) to max(angle1, angle2).
+  The default setting is [ -135, 135 ].
+  An angle of 0 degrees corresponds to the 12 o'clock position,
+  and positive angles count in a clockwise direction.
+  \param angle1
+  \param angle2 boundaries of the angle interval in degrees.
+  \warning <ul>
+  <li>The angle range is limited to [-360, 360] degrees. Angles exceeding
+      this range will be clipped.
+  <li>For angles more or equal than 360 degrees above or below min(angle1, angle2),
+      scale marks will not be drawn.
+  <li>If you need a counterclockwise scale, use QwtScaleDiv::setInterval()
+  </ul>
+*/
+void QwtRoundScaleDraw::setAngleRange( double angle1, double angle2 )
+{
+#if 0
+    angle1 = qBound( -360.0, angle1, 360.0 );
+    angle2 = qBound( -360.0, angle2, 360.0 );
+#endif
+
+    d_data->startAngle = angle1;
+    d_data->endAngle = angle2;
+
+    if ( d_data->startAngle == d_data->endAngle )
+    {
+        d_data->startAngle -= 1;
+        d_data->endAngle += 1;
+    }
+
+    scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle );
+}
+
+/*!
+   Draws the label for a major scale tick
+
+   \param painter Painter
+   \param value Value
+
+   \sa drawTick(), drawBackbone()
+*/
+void QwtRoundScaleDraw::drawLabel( QPainter *painter, double value ) const
+{
+    const double tval = scaleMap().transform( value );
+    if ( ( tval >= d_data->startAngle + 360.0 )
+        || ( tval <= d_data->startAngle - 360.0 ) )
+    {
+        return;
+    }
+
+    const QwtText label = tickLabel( painter->font(), value );
+    if ( label.isEmpty() )
+        return;
+
+    double radius = d_data->radius;
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
+        hasComponent( QwtAbstractScaleDraw::Backbone ) )
+    {
+        radius += spacing();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+        radius += tickLength( QwtScaleDiv::MajorTick );
+
+    const QSizeF sz = label.textSize( painter->font() );
+    const double arc = qwtRadians( tval );
+
+    const double x = d_data->center.x() +
+        ( radius + sz.width() / 2.0 ) * qSin( arc );
+    const double y = d_data->center.y() -
+        ( radius + sz.height() / 2.0 ) * qCos( arc );
+
+    const QRectF r( x - sz.width() / 2, y - sz.height() / 2,
+        sz.width(), sz.height() );
+    label.draw( painter, r );
+}
+
+/*!
+   Draw a tick
+
+   \param painter Painter
+   \param value Value of the tick
+   \param len Lenght of the tick
+
+   \sa drawBackbone(), drawLabel()
+*/
+void QwtRoundScaleDraw::drawTick( QPainter *painter, double value, double len ) const
+{
+    if ( len <= 0 )
+        return;
+
+    const double tval = scaleMap().transform( value );
+
+    const double cx = d_data->center.x();
+    const double cy = d_data->center.y();
+    const double radius = d_data->radius;
+
+    if ( ( tval < d_data->startAngle + 360.0 )
+        && ( tval > d_data->startAngle - 360.0 ) )
+    {
+        const double arc = qwtRadians( tval );
+
+        const double sinArc = qSin( arc );
+        const double cosArc = qCos( arc );
+
+        const double x1 = cx + radius * sinArc;
+        const double x2 = cx + ( radius + len ) * sinArc;
+        const double y1 = cy - radius * cosArc;
+        const double y2 = cy - ( radius + len ) * cosArc;
+
+        QwtPainter::drawLine( painter, x1, y1, x2, y2 );
+    }
+}
+
+/*!
+   Draws the baseline of the scale
+   \param painter Painter
+
+   \sa drawTick(), drawLabel()
+*/
+void QwtRoundScaleDraw::drawBackbone( QPainter *painter ) const
+{
+    const double deg1 = scaleMap().p1();
+    const double deg2 = scaleMap().p2();
+
+    const int a1 = qRound( qMin( deg1, deg2 ) - 90 );
+    const int a2 = qRound( qMax( deg1, deg2 ) - 90 );
+
+    const double radius = d_data->radius;
+    const double x = d_data->center.x() - radius;
+    const double y = d_data->center.y() - radius;
+
+    painter->drawArc( QRectF( x, y, 2 * radius, 2 * radius ),
+        -a2 * 16, ( a2 - a1 + 1 ) * 16 );          // counterclockwise
+}
+
+/*!
+   Calculate the extent of the scale
+
+   The extent is the distance between the baseline to the outermost
+   pixel of the scale draw. radius() + extent() is an upper limit
+   for the radius of the bounding circle.
+
+   \param font Font used for painting the labels
+   \return Calculated extent
+
+   \sa setMinimumExtent(), minimumExtent()
+   \warning The implemented algorithm is not too smart and
+            calculates only an upper limit, that might be a
+            few pixels too large
+*/
+double QwtRoundScaleDraw::extent( const QFont &font ) const
+{
+    double d = 0.0;
+
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
+    {
+        const QwtScaleDiv &sd = scaleDiv();
+        const QList<double> &ticks = sd.ticks( QwtScaleDiv::MajorTick );
+        for ( int i = 0; i < ticks.count(); i++ )
+        {
+            const double value = ticks[i];
+            if ( !sd.contains( value ) )
+                continue;
+
+            const double tval = scaleMap().transform( value );
+            if ( ( tval < d_data->startAngle + 360 )
+                && ( tval > d_data->startAngle - 360 ) )
+            {
+                const QwtText label = tickLabel( font, value );
+                if ( label.isEmpty() )
+                    continue;
+
+                const double arc = qwtRadians( tval );
+
+                const QSizeF sz = label.textSize( font );
+                const double off = qMax( sz.width(), sz.height() );
+
+                double x = off * qSin( arc );
+                double y = off * qCos( arc );
+
+                const double dist = qSqrt( x * x + y * y );
+                if ( dist > d )
+                    d = dist;
+            }
+        }
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+    {
+        d += maxTickLength();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
+    {
+        const double pw = qMax( 1, penWidth() );  // pen width can be zero
+        d += pw;
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) &&
+        ( hasComponent( QwtAbstractScaleDraw::Ticks ) ||
+            hasComponent( QwtAbstractScaleDraw::Backbone ) ) )
+    {
+        d += spacing();
+    }
+
+    d = qMax( d, minimumExtent() );
+
+    return d;
+}
diff --git a/source/third_party/qwt/qwt_sampling_thread.cpp b/source/third_party/qwt/qwt_sampling_thread.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51e981b66a3ae5e1028c79bb093fddc960fd582f
--- /dev/null
+++ b/source/third_party/qwt/qwt_sampling_thread.cpp
@@ -0,0 +1,106 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_sampling_thread.h"
+#include "qwt/qwt_system_clock.h"
+
+class QwtSamplingThread::PrivateData
+{
+public:
+    QwtSystemClock clock;
+
+    double interval;
+    bool isStopped;
+};
+
+
+//! Constructor
+QwtSamplingThread::QwtSamplingThread( QObject *parent ):
+    QThread( parent )
+{
+    d_data = new PrivateData;
+    d_data->interval = 1000; // 1 second
+    d_data->isStopped = true;
+}
+
+//! Destructor
+QwtSamplingThread::~QwtSamplingThread()
+{
+    delete d_data;
+}
+
+/*!
+   Change the interval (in ms), when sample() is called.
+   The default interval is 1000.0 ( = 1s )
+
+   \param interval Interval
+   \sa interval()
+*/
+void QwtSamplingThread::setInterval( double interval )
+{
+    if ( interval < 0.0 )
+        interval = 0.0;
+
+    d_data->interval = interval;
+}
+
+/*!
+   \return Interval (in ms), between 2 calls of sample()
+   \sa setInterval()
+*/
+double QwtSamplingThread::interval() const
+{
+    return d_data->interval;
+}
+
+/*!
+   \return Time (in ms) since the thread was started
+   \sa QThread::start(), run()
+*/
+double QwtSamplingThread::elapsed() const
+{
+    if ( d_data->isStopped )
+        return 0.0;
+
+    return d_data->clock.elapsed();
+}
+
+/*!
+   Terminate the collecting thread
+   \sa QThread::start(), run()
+*/
+void QwtSamplingThread::stop()
+{
+    d_data->isStopped = true;
+}
+
+/*!
+   Loop collecting samples started from QThread::start()
+   \sa stop()
+*/
+void QwtSamplingThread::run()
+{
+    d_data->clock.start();
+    d_data->isStopped = false;
+
+    while ( !d_data->isStopped )
+    {
+        const double elapsed = d_data->clock.elapsed();
+        sample( elapsed / 1000.0 );
+
+        if ( d_data->interval > 0.0 )
+        {
+            const double msecs =
+                d_data->interval - ( d_data->clock.elapsed() - elapsed );
+
+            if ( msecs > 0.0 )
+                usleep( qRound( 1000.0 * msecs ) );
+        }
+    }
+}
diff --git a/source/third_party/qwt/qwt_scale_div.cpp b/source/third_party/qwt/qwt_scale_div.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a24006509c4653c920b03fa5f3fc85b058e2d338
--- /dev/null
+++ b/source/third_party/qwt/qwt_scale_div.cpp
@@ -0,0 +1,331 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_math.h"
+#include <qalgorithms.h>
+
+/*!
+  Construct a division without ticks
+
+  \param lowerBound First boundary
+  \param upperBound Second boundary
+
+  \note lowerBound might be greater than upperBound for inverted scales
+ */
+QwtScaleDiv::QwtScaleDiv( double lowerBound, double upperBound ):
+    d_lowerBound( lowerBound ),
+    d_upperBound( upperBound )
+{
+}
+
+/*!
+  Construct a scale division
+
+  \param interval Interval
+  \param ticks List of major, medium and minor ticks
+*/
+QwtScaleDiv::QwtScaleDiv( const QwtInterval &interval,
+        QList<double> ticks[NTickTypes] ):
+    d_lowerBound( interval.minValue() ),
+    d_upperBound( interval.maxValue() )
+{
+    for ( int i = 0; i < NTickTypes; i++ )
+        d_ticks[i] = ticks[i];
+}
+
+/*!
+  Construct a scale division
+
+  \param lowerBound First boundary
+  \param upperBound Second boundary
+  \param ticks List of major, medium and minor ticks
+
+  \note lowerBound might be greater than upperBound for inverted scales
+*/
+QwtScaleDiv::QwtScaleDiv( double lowerBound, double upperBound,
+        QList<double> ticks[NTickTypes] ):
+    d_lowerBound( lowerBound ),
+    d_upperBound( upperBound )
+{
+    for ( int i = 0; i < NTickTypes; i++ )
+        d_ticks[i] = ticks[i];
+}
+
+/*!
+  Construct a scale division
+
+  \param lowerBound First boundary
+  \param upperBound Second boundary
+  \param minorTicks List of minor ticks
+  \param mediumTicks List medium ticks
+  \param majorTicks List of major ticks
+
+  \note lowerBound might be greater than upperBound for inverted scales
+*/
+QwtScaleDiv::QwtScaleDiv( double lowerBound, double upperBound,
+        const QList<double> &minorTicks, 
+        const QList<double> &mediumTicks,
+        const QList<double> &majorTicks ):
+    d_lowerBound( lowerBound ),
+    d_upperBound( upperBound )
+{
+    d_ticks[ MinorTick ] = minorTicks;
+    d_ticks[ MediumTick ] = mediumTicks;
+    d_ticks[ MajorTick ] = majorTicks;
+}
+
+/*!
+  Change the interval
+
+  \param lowerBound First boundary
+  \param upperBound Second boundary
+
+  \note lowerBound might be greater than upperBound for inverted scales
+*/
+void QwtScaleDiv::setInterval( double lowerBound, double upperBound )
+{
+    d_lowerBound = lowerBound;
+    d_upperBound = upperBound;
+}
+
+/*!
+   Change the interval
+
+   \param interval Interval
+*/
+void QwtScaleDiv::setInterval( const QwtInterval &interval )
+{
+    d_lowerBound = interval.minValue();
+    d_upperBound = interval.maxValue();
+}
+
+/*!
+  \return lowerBound -> upperBound
+*/
+QwtInterval QwtScaleDiv::interval() const
+{
+    return QwtInterval( d_lowerBound, d_upperBound );
+}
+
+/*!
+  Set the first boundary
+
+  \param lowerBound First boundary
+  \sa lowerBiound(), setUpperBound()
+ */
+void QwtScaleDiv::setLowerBound( double lowerBound  )
+{
+    d_lowerBound = lowerBound;
+}
+
+/*!
+  \return First boundary
+  \sa upperBound()
+*/
+double QwtScaleDiv::lowerBound() const
+{
+    return d_lowerBound;
+}   
+
+/*!
+  Set the second boundary
+
+  \param upperBound Second boundary
+  \sa upperBound(), setLowerBound()
+ */
+void QwtScaleDiv::setUpperBound( double upperBound  )
+{
+    d_upperBound = upperBound;
+} 
+
+/*!
+  \return upper bound
+  \sa lowerBound()
+*/
+double QwtScaleDiv::upperBound() const
+{
+    return d_upperBound;
+}
+
+/*!
+  \return upperBound() - lowerBound()
+*/
+double QwtScaleDiv::range() const
+{
+    return d_upperBound - d_lowerBound;
+}
+
+/*!
+  \brief Equality operator
+  \return true if this instance is equal to other
+*/
+bool QwtScaleDiv::operator==( const QwtScaleDiv &other ) const
+{
+    if ( d_lowerBound != other.d_lowerBound ||
+        d_upperBound != other.d_upperBound )
+    {
+        return false;
+    }
+
+    for ( int i = 0; i < NTickTypes; i++ )
+    {
+        if ( d_ticks[i] != other.d_ticks[i] )
+            return false;
+    }
+
+    return true;
+}
+
+/*!
+  \brief Inequality
+  \return true if this instance is not equal to other
+*/
+bool QwtScaleDiv::operator!=( const QwtScaleDiv &other ) const
+{
+    return ( !( *this == other ) );
+}
+
+//! Check if the scale division is empty( lowerBound() == upperBound() )
+bool QwtScaleDiv::isEmpty() const
+{
+    return ( d_lowerBound == d_upperBound );
+}
+
+//! Check if the scale division is increasing( lowerBound() <= upperBound() )
+bool QwtScaleDiv::isIncreasing() const
+{
+    return d_lowerBound <= d_upperBound;
+}
+
+/*!
+  Return if a value is between lowerBound() and upperBound()
+
+  \param value Value
+  \return true/false
+*/
+bool QwtScaleDiv::contains( double value ) const
+{
+    const double min = qMin( d_lowerBound, d_upperBound );
+    const double max = qMax( d_lowerBound, d_upperBound );
+
+    return value >= min && value <= max;
+}
+
+/*! 
+   Invert the scale division
+   \sa inverted()
+ */
+void QwtScaleDiv::invert()
+{
+    qSwap( d_lowerBound, d_upperBound );
+
+    for ( int i = 0; i < NTickTypes; i++ )
+    {
+        QList<double>& ticks = d_ticks[i];
+
+        const int size = ticks.count();
+        const int size2 = size / 2;
+
+        for ( int j = 0; j < size2; j++ )
+            qSwap( ticks[j], ticks[size - 1 - j] );
+    }
+}
+
+/*! 
+  \return A scale division with inverted boundaries and ticks
+  \sa invert()
+ */
+QwtScaleDiv QwtScaleDiv::inverted() const
+{
+    QwtScaleDiv other = *this;
+    other.invert();
+
+    return other;
+}
+
+/*! 
+   Return a scale division with an interval [lowerBound, upperBound]
+   where all ticks outside this interval are removed
+
+   \param lowerBound Lower bound
+   \param upperBound Upper bound
+
+   \return Scale division with all ticks inside of the given interval
+
+   \note lowerBound might be greater than upperBound for inverted scales
+*/
+QwtScaleDiv QwtScaleDiv::bounded( 
+    double lowerBound, double upperBound ) const
+{
+    const double min = qMin( lowerBound, upperBound );
+    const double max = qMax( lowerBound, upperBound );
+
+    QwtScaleDiv sd;
+    sd.setInterval( lowerBound, upperBound );
+
+    for ( int tickType = 0; tickType < QwtScaleDiv::NTickTypes; tickType++ )
+    {
+        const QList<double> &ticks = d_ticks[ tickType ];
+
+        QList<double> boundedTicks;
+        for ( int i = 0; i < ticks.size(); i++ )
+        {
+            const double tick = ticks[i];
+            if ( tick >= min && tick <= max )
+                boundedTicks += tick;
+        }
+
+        sd.setTicks( tickType, boundedTicks );
+    }
+
+    return sd;
+
+}
+
+/*!
+    Assign ticks
+
+   \param type MinorTick, MediumTick or MajorTick
+   \param ticks Values of the tick positions
+*/
+void QwtScaleDiv::setTicks( int type, const QList<double> &ticks )
+{
+    if ( type >= 0 && type < NTickTypes )
+        d_ticks[type] = ticks;
+}
+
+/*!
+   Return a list of ticks
+
+   \param type MinorTick, MediumTick or MajorTick
+   \return Tick list
+*/
+QList<double> QwtScaleDiv::ticks( int type ) const
+{
+    if ( type >= 0 && type < NTickTypes )
+        return d_ticks[type];
+
+    return QList<double>();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtScaleDiv &scaleDiv )
+{
+    debug << scaleDiv.lowerBound() << "<->" << scaleDiv.upperBound();
+    debug << "Major: " << scaleDiv.ticks( QwtScaleDiv::MajorTick );
+    debug << "Medium: " << scaleDiv.ticks( QwtScaleDiv::MediumTick );
+    debug << "Minor: " << scaleDiv.ticks( QwtScaleDiv::MinorTick );
+
+    return debug;
+}
+
+#endif
+
diff --git a/source/third_party/qwt/qwt_scale_draw.cpp b/source/third_party/qwt/qwt_scale_draw.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f27b7e1bb841984ced97a72105fd058ed0b06852
--- /dev/null
+++ b/source/third_party/qwt/qwt_scale_draw.cpp
@@ -0,0 +1,926 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_scale_draw.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_painter.h"
+#include <qpen.h>
+#include <qpainter.h>
+#include <qmath.h>
+
+#if QT_VERSION < 0x040601
+#define qFastSin(x) qSin(x)
+#define qFastCos(x) qCos(x)
+#endif
+
+class QwtScaleDraw::PrivateData
+{
+public:
+    PrivateData():
+        len( 0 ),
+        alignment( QwtScaleDraw::BottomScale ),
+        labelAlignment( 0 ),
+        labelRotation( 0.0 )
+    {
+    }
+
+    QPointF pos;
+    double len;
+
+    Alignment alignment;
+
+    Qt::Alignment labelAlignment;
+    double labelRotation;
+};
+
+/*!
+  \brief Constructor
+
+  The range of the scale is initialized to [0, 100],
+  The position is at (0, 0) with a length of 100.
+  The orientation is QwtAbstractScaleDraw::Bottom.
+*/
+QwtScaleDraw::QwtScaleDraw()
+{
+    d_data = new QwtScaleDraw::PrivateData;
+    setLength( 100 );
+}
+
+//! Destructor
+QwtScaleDraw::~QwtScaleDraw()
+{
+    delete d_data;
+}
+
+/*!
+   Return alignment of the scale
+   \sa setAlignment()
+   \return Alignment of the scale
+*/
+QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
+{
+    return d_data->alignment;
+}
+
+/*!
+   Set the alignment of the scale
+
+   \param align Alignment of the scale
+
+   The default alignment is QwtScaleDraw::BottomScale
+   \sa alignment()
+*/
+void QwtScaleDraw::setAlignment( Alignment align )
+{
+    d_data->alignment = align;
+}
+
+/*!
+  Return the orientation
+
+  TopScale, BottomScale are horizontal (Qt::Horizontal) scales,
+  LeftScale, RightScale are vertical (Qt::Vertical) scales.
+
+  \return Orientation of the scale
+
+  \sa alignment()
+*/
+Qt::Orientation QwtScaleDraw::orientation() const
+{
+    switch ( d_data->alignment )
+    {
+        case TopScale:
+        case BottomScale:
+            return Qt::Horizontal;
+        case LeftScale:
+        case RightScale:
+        default:
+            return Qt::Vertical;
+    }
+}
+
+/*!
+  \brief Determine the minimum border distance
+
+  This member function returns the minimum space
+  needed to draw the mark labels at the scale's endpoints.
+
+  \param font Font
+  \param start Start border distance
+  \param end End border distance
+*/
+void QwtScaleDraw::getBorderDistHint( 
+    const QFont &font, int &start, int &end ) const
+{
+    start = 0;
+    end = 1.0;
+
+    if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
+        return;
+
+    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
+    if ( ticks.count() == 0 )
+        return;
+
+    // Find the ticks, that are mapped to the borders.
+    // minTick is the tick, that is mapped to the top/left-most position
+    // in widget coordinates.
+
+    double minTick = ticks[0];
+    double minPos = scaleMap().transform( minTick );
+    double maxTick = minTick;
+    double maxPos = minPos;
+
+    for ( int i = 1; i < ticks.count(); i++ )
+    {
+        const double tickPos = scaleMap().transform( ticks[i] );
+        if ( tickPos < minPos )
+        {
+            minTick = ticks[i];
+            minPos = tickPos;
+        }
+        if ( tickPos > scaleMap().transform( maxTick ) )
+        {
+            maxTick = ticks[i];
+            maxPos = tickPos;
+        }
+    }
+
+    double e = 0.0;
+    double s = 0.0;
+    if ( orientation() == Qt::Vertical )
+    {
+        s = -labelRect( font, minTick ).top();
+        s -= qAbs( minPos - qRound( scaleMap().p2() ) );
+
+        e = labelRect( font, maxTick ).bottom();
+        e -= qAbs( maxPos - scaleMap().p1() );
+    }
+    else
+    {
+        s = -labelRect( font, minTick ).left();
+        s -= qAbs( minPos - scaleMap().p1() );
+
+        e = labelRect( font, maxTick ).right();
+        e -= qAbs( maxPos - scaleMap().p2() );
+    }
+
+    if ( s < 0.0 )
+        s = 0.0;
+    if ( e < 0.0 )
+        e = 0.0;
+
+    start = qCeil( s );
+    end = qCeil( e );
+}
+
+/*!
+  Determine the minimum distance between two labels, that is necessary
+  that the texts don't overlap.
+
+  \param font Font
+  \return The maximum width of a label
+
+  \sa getBorderDistHint()
+*/
+
+int QwtScaleDraw::minLabelDist( const QFont &font ) const
+{
+    if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
+        return 0;
+
+    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
+    if ( ticks.isEmpty() )
+        return 0;
+
+    const QFontMetrics fm( font );
+
+    const bool vertical = ( orientation() == Qt::Vertical );
+
+    QRectF bRect1;
+    QRectF bRect2 = labelRect( font, ticks[0] );
+    if ( vertical )
+    {
+        bRect2.setRect( -bRect2.bottom(), 0.0, bRect2.height(), bRect2.width() );
+    }
+
+    double maxDist = 0.0;
+
+    for ( int i = 1; i < ticks.count(); i++ )
+    {
+        bRect1 = bRect2;
+        bRect2 = labelRect( font, ticks[i] );
+        if ( vertical )
+        {
+            bRect2.setRect( -bRect2.bottom(), 0.0,
+                bRect2.height(), bRect2.width() );
+        }
+
+        double dist = fm.leading(); // space between the labels
+        if ( bRect1.right() > 0 )
+            dist += bRect1.right();
+        if ( bRect2.left() < 0 )
+            dist += -bRect2.left();
+
+        if ( dist > maxDist )
+            maxDist = dist;
+    }
+
+    double angle = qwtRadians( labelRotation() ); 
+    if ( vertical )
+        angle += M_PI / 2;
+
+    const double sinA = qFastSin( angle ); // qreal -> double
+    if ( qFuzzyCompare( sinA + 1.0, 1.0 ) )
+        return qCeil( maxDist );
+
+    const int fmHeight = fm.ascent() - 2;
+
+    // The distance we need until there is
+    // the height of the label font. This height is needed
+    // for the neighbored label.
+
+    double labelDist = fmHeight / qFastSin( angle ) * qFastCos( angle );
+    if ( labelDist < 0 )
+        labelDist = -labelDist;
+
+    // For text orientations close to the scale orientation
+
+    if ( labelDist > maxDist )
+        labelDist = maxDist;
+
+    // For text orientations close to the opposite of the
+    // scale orientation
+
+    if ( labelDist < fmHeight )
+        labelDist = fmHeight;
+
+    return qCeil( labelDist );
+}
+
+/*!
+   Calculate the width/height that is needed for a
+   vertical/horizontal scale.
+
+   The extent is calculated from the pen width of the backbone,
+   the major tick length, the spacing and the maximum width/height
+   of the labels.
+
+   \param font Font used for painting the labels
+   \return Extent
+
+   \sa minLength()
+*/
+double QwtScaleDraw::extent( const QFont &font ) const
+{
+    double d = 0;
+
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
+    {
+        if ( orientation() == Qt::Vertical )
+            d = maxLabelWidth( font );
+        else
+            d = maxLabelHeight( font );
+
+        if ( d > 0 )
+            d += spacing();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+    {
+        d += maxTickLength();
+    }
+
+    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
+    {
+        const double pw = qMax( 1, penWidth() );  // pen width can be zero
+        d += pw;
+    }
+
+    d = qMax( d, minimumExtent() );
+    return d;
+}
+
+/*!
+   Calculate the minimum length that is needed to draw the scale
+
+   \param font Font used for painting the labels
+   \return Minimum length that is needed to draw the scale
+
+   \sa extent()
+*/
+int QwtScaleDraw::minLength( const QFont &font ) const
+{
+    int startDist, endDist;
+    getBorderDistHint( font, startDist, endDist );
+
+    const QwtScaleDiv &sd = scaleDiv();
+
+    const uint minorCount =
+        sd.ticks( QwtScaleDiv::MinorTick ).count() +
+        sd.ticks( QwtScaleDiv::MediumTick ).count();
+    const uint majorCount =
+        sd.ticks( QwtScaleDiv::MajorTick ).count();
+
+    int lengthForLabels = 0;
+    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
+        lengthForLabels = minLabelDist( font ) * majorCount;
+
+    int lengthForTicks = 0;
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+    {
+        const double pw = qMax( 1, penWidth() );  // penwidth can be zero
+        lengthForTicks = qCeil( ( majorCount + minorCount ) * ( pw + 1.0 ) );
+    }
+
+    return startDist + endDist + qMax( lengthForLabels, lengthForTicks );
+}
+
+/*!
+   Find the position, where to paint a label
+
+   The position has a distance that depends on the length of the ticks 
+   in direction of the alignment().
+
+   \param value Value
+   \return Position, where to paint a label
+*/
+QPointF QwtScaleDraw::labelPosition( double value ) const
+{
+    const double tval = scaleMap().transform( value );
+    double dist = spacing();
+    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
+        dist += qMax( 1, penWidth() );
+
+    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
+        dist += tickLength( QwtScaleDiv::MajorTick );
+
+    double px = 0;
+    double py = 0;
+
+    switch ( alignment() )
+    {
+        case RightScale:
+        {
+            px = d_data->pos.x() + dist;
+            py = tval;
+            break;
+        }
+        case LeftScale:
+        {
+            px = d_data->pos.x() - dist;
+            py = tval;
+            break;
+        }
+        case BottomScale:
+        {
+            px = tval;
+            py = d_data->pos.y() + dist;
+            break;
+        }
+        case TopScale:
+        {
+            px = tval;
+            py = d_data->pos.y() - dist;
+            break;
+        }
+    }
+
+    return QPointF( px, py );
+}
+
+/*!
+   Draw a tick
+
+   \param painter Painter
+   \param value Value of the tick
+   \param len Length of the tick
+
+   \sa drawBackbone(), drawLabel()
+*/
+void QwtScaleDraw::drawTick( QPainter *painter, double value, double len ) const
+{
+    if ( len <= 0 )
+        return;
+
+    const bool roundingAlignment = QwtPainter::roundingAlignment( painter );
+
+    QPointF pos = d_data->pos;
+
+    double tval = scaleMap().transform( value );
+    if ( roundingAlignment )
+        tval = qRound( tval );
+
+    const int pw = penWidth();
+    int a = 0;
+    if ( pw > 1 && roundingAlignment )
+        a = 1;
+
+    switch ( alignment() )
+    {
+        case LeftScale:
+        {
+            double x1 = pos.x() + a;
+            double x2 = pos.x() + a - pw - len;
+            if ( roundingAlignment )
+            {
+                x1 = qRound( x1 );
+                x2 = qRound( x2 );
+            }
+
+            QwtPainter::drawLine( painter, x1, tval, x2, tval );
+            break;
+        }
+
+        case RightScale:
+        {
+            double x1 = pos.x();
+            double x2 = pos.x() + pw + len;
+            if ( roundingAlignment )
+            {
+                x1 = qRound( x1 );
+                x2 = qRound( x2 );
+            }
+
+            QwtPainter::drawLine( painter, x1, tval, x2, tval );
+            break;
+        }
+
+        case BottomScale:
+        {
+            double y1 = pos.y();
+            double y2 = pos.y() + pw + len;
+            if ( roundingAlignment )
+            {
+                y1 = qRound( y1 );
+                y2 = qRound( y2 );
+            }
+
+            QwtPainter::drawLine( painter, tval, y1, tval, y2 );
+            break;
+        }
+
+        case TopScale:
+        {
+            double y1 = pos.y() + a;
+            double y2 = pos.y() - pw - len + a;
+            if ( roundingAlignment )
+            {
+                y1 = qRound( y1 );
+                y2 = qRound( y2 );
+            }
+
+            QwtPainter::drawLine( painter, tval, y1, tval, y2 );
+            break;
+        }
+    }
+}
+
+/*!
+   Draws the baseline of the scale
+   \param painter Painter
+
+   \sa drawTick(), drawLabel()
+*/
+void QwtScaleDraw::drawBackbone( QPainter *painter ) const
+{
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    const QPointF &pos = d_data->pos;
+    const double len = d_data->len;
+    const int pw = qMax( penWidth(), 1 );
+
+    // pos indicates a border not the center of the backbone line
+    // so we need to shift its position depending on the pen width
+    // and the alignment of the scale
+
+    double off;
+    if ( doAlign )
+    {
+        if ( alignment() == LeftScale || alignment() == TopScale )
+            off = ( pw - 1 ) / 2;
+        else
+            off = pw / 2;
+    }
+    else
+    {
+        off = 0.5 * penWidth();
+    }
+
+    switch ( alignment() )
+    {
+        case LeftScale:
+        {
+            double x = pos.x() - off;
+            if ( doAlign )
+                x = qRound( x );
+
+            QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
+            break;
+        }
+        case RightScale:
+        {
+            double x = pos.x() + off;
+            if ( doAlign )
+                x = qRound( x );
+
+            QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
+            break;
+        }
+        case TopScale:
+        {
+            double y = pos.y() - off;
+            if ( doAlign )
+                y = qRound( y );
+
+            QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
+            break;
+        }
+        case BottomScale:
+        {
+            double y = pos.y() + off;
+            if ( doAlign )
+                y = qRound( y );
+
+            QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
+            break;
+        }
+    }
+}
+
+/*!
+  \brief Move the position of the scale
+
+  The meaning of the parameter pos depends on the alignment:
+  <dl>
+  <dt>QwtScaleDraw::LeftScale
+  <dd>The origin is the topmost point of the
+      backbone. The backbone is a vertical line.
+      Scale marks and labels are drawn
+      at the left of the backbone.
+  <dt>QwtScaleDraw::RightScale
+  <dd>The origin is the topmost point of the
+      backbone. The backbone is a vertical line.
+      Scale marks and labels are drawn
+      at the right of the backbone.
+  <dt>QwtScaleDraw::TopScale
+  <dd>The origin is the leftmost point of the
+      backbone. The backbone is a horizontal line.
+      Scale marks and labels are drawn
+      above the backbone.
+  <dt>QwtScaleDraw::BottomScale
+  <dd>The origin is the leftmost point of the
+      backbone. The backbone is a horizontal line
+      Scale marks and labels are drawn
+      below the backbone.
+  </dl>
+
+  \param pos Origin of the scale
+
+  \sa pos(), setLength()
+*/
+void QwtScaleDraw::move( const QPointF &pos )
+{
+    d_data->pos = pos;
+    updateMap();
+}
+
+/*!
+   \return Origin of the scale
+   \sa move(), length()
+*/
+QPointF QwtScaleDraw::pos() const
+{
+    return d_data->pos;
+}
+
+/*!
+  Set the length of the backbone.
+
+  The length doesn't include the space needed for
+  overlapping labels.
+
+  \param length Length of the backbone
+
+  \sa move(), minLabelDist()
+*/
+void QwtScaleDraw::setLength( double length )
+{
+#if 1
+    if ( length >= 0 && length < 10 )
+        length = 10;
+
+    // why should we accept negative lengths ???
+    if ( length < 0 && length > -10 )
+        length = -10;
+#else
+    length = qMax( length, 10 );
+#endif
+
+    d_data->len = length;
+    updateMap();
+}
+
+/*!
+   \return the length of the backbone
+   \sa setLength(), pos()
+*/
+double QwtScaleDraw::length() const
+{
+    return d_data->len;
+}
+
+/*!
+   Draws the label for a major scale tick
+
+   \param painter Painter
+   \param value Value
+
+   \sa drawTick(), drawBackbone(), boundingLabelRect()
+*/
+void QwtScaleDraw::drawLabel( QPainter *painter, double value ) const
+{
+    QwtText lbl = tickLabel( painter->font(), value );
+    if ( lbl.isEmpty() )
+        return;
+
+    QPointF pos = labelPosition( value );
+
+    QSizeF labelSize = lbl.textSize( painter->font() );
+
+    const QTransform transform = labelTransformation( pos, labelSize );
+
+    painter->save();
+    painter->setWorldTransform( transform, true );
+
+    lbl.draw ( painter, QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
+
+    painter->restore();
+}
+
+/*!
+  \brief Find the bounding rectangle for the label.
+
+  The coordinates of the rectangle are absolute ( calculated from pos() ).
+  in direction of the tick.
+
+  \param font Font used for painting
+  \param value Value
+
+  \return Bounding rectangle
+  \sa labelRect()
+*/
+QRect QwtScaleDraw::boundingLabelRect( const QFont &font, double value ) const
+{
+    QwtText lbl = tickLabel( font, value );
+    if ( lbl.isEmpty() )
+        return QRect();
+
+    const QPointF pos = labelPosition( value );
+    QSizeF labelSize = lbl.textSize( font );
+
+    const QTransform transform = labelTransformation( pos, labelSize );
+    return transform.mapRect( QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
+}
+
+/*!
+   Calculate the transformation that is needed to paint a label
+   depending on its alignment and rotation.
+
+   \param pos Position where to paint the label
+   \param size Size of the label
+
+   \return Transformation matrix
+   \sa setLabelAlignment(), setLabelRotation()
+*/
+QTransform QwtScaleDraw::labelTransformation(
+    const QPointF &pos, const QSizeF &size ) const
+{
+    QTransform transform;
+    transform.translate( pos.x(), pos.y() );
+    transform.rotate( labelRotation() );
+
+    int flags = labelAlignment();
+    if ( flags == 0 )
+    {
+        switch ( alignment() )
+        {
+            case RightScale:
+            {
+                if ( flags == 0 )
+                    flags = Qt::AlignRight | Qt::AlignVCenter;
+                break;
+            }
+            case LeftScale:
+            {
+                if ( flags == 0 )
+                    flags = Qt::AlignLeft | Qt::AlignVCenter;
+                break;
+            }
+            case BottomScale:
+            {
+                if ( flags == 0 )
+                    flags = Qt::AlignHCenter | Qt::AlignBottom;
+                break;
+            }
+            case TopScale:
+            {
+                if ( flags == 0 )
+                    flags = Qt::AlignHCenter | Qt::AlignTop;
+                break;
+            }
+        }
+    }
+
+    double x, y;
+
+    if ( flags & Qt::AlignLeft )
+        x = -size.width();
+    else if ( flags & Qt::AlignRight )
+        x = 0.0;
+    else // Qt::AlignHCenter
+        x = -( 0.5 * size.width() );
+
+    if ( flags & Qt::AlignTop )
+        y = -size.height();
+    else if ( flags & Qt::AlignBottom )
+        y = 0;
+    else // Qt::AlignVCenter
+        y = -( 0.5 * size.height() );
+
+    transform.translate( x, y );
+
+    return transform;
+}
+
+/*!
+  Find the bounding rectangle for the label. The coordinates of
+  the rectangle are relative to spacing + tick length from the backbone
+  in direction of the tick.
+
+  \param font Font used for painting
+  \param value Value
+
+   \return Bounding rectangle that is needed to draw a label
+*/
+QRectF QwtScaleDraw::labelRect( const QFont &font, double value ) const
+{
+    QwtText lbl = tickLabel( font, value );
+    if ( lbl.isEmpty() )
+        return QRectF( 0.0, 0.0, 0.0, 0.0 );
+
+    const QPointF pos = labelPosition( value );
+
+    const QSizeF labelSize = lbl.textSize( font );
+    const QTransform transform = labelTransformation( pos, labelSize );
+
+    QRectF br = transform.mapRect( QRectF( QPointF( 0, 0 ), labelSize ) );
+    br.translate( -pos.x(), -pos.y() );
+
+    return br;
+}
+
+/*!
+   Calculate the size that is needed to draw a label
+
+   \param font Label font
+   \param value Value
+
+   \return Size that is needed to draw a label
+*/
+QSizeF QwtScaleDraw::labelSize( const QFont &font, double value ) const
+{
+    return labelRect( font, value ).size();
+}
+
+/*!
+  Rotate all labels.
+
+  When changing the rotation, it might be necessary to
+  adjust the label flags too. Finding a useful combination is
+  often the result of try and error.
+
+  \param rotation Angle in degrees. When changing the label rotation,
+                  the label flags often needs to be adjusted too.
+
+  \sa setLabelAlignment(), labelRotation(), labelAlignment().
+
+*/
+void QwtScaleDraw::setLabelRotation( double rotation )
+{
+    d_data->labelRotation = rotation;
+}
+
+/*!
+  \return the label rotation
+  \sa setLabelRotation(), labelAlignment()
+*/
+double QwtScaleDraw::labelRotation() const
+{
+    return d_data->labelRotation;
+}
+
+/*!
+  \brief Change the label flags
+
+  Labels are aligned to the point tick length + spacing away from the backbone.
+
+  The alignment is relative to the orientation of the label text.
+  In case of an flags of 0 the label will be aligned
+  depending on the orientation of the scale:
+
+      QwtScaleDraw::TopScale: Qt::AlignHCenter | Qt::AlignTop\n
+      QwtScaleDraw::BottomScale: Qt::AlignHCenter | Qt::AlignBottom\n
+      QwtScaleDraw::LeftScale: Qt::AlignLeft | Qt::AlignVCenter\n
+      QwtScaleDraw::RightScale: Qt::AlignRight | Qt::AlignVCenter\n
+
+  Changing the alignment is often necessary for rotated labels.
+
+  \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h>
+
+  \sa setLabelRotation(), labelRotation(), labelAlignment()
+  \warning The various alignments might be confusing.
+           The alignment of the label is not the alignment
+           of the scale and is not the alignment of the flags
+           ( QwtText::flags() ) returned from QwtAbstractScaleDraw::label().
+*/
+
+void QwtScaleDraw::setLabelAlignment( Qt::Alignment alignment )
+{
+    d_data->labelAlignment = alignment;
+}
+
+/*!
+  \return the label flags
+  \sa setLabelAlignment(), labelRotation()
+*/
+Qt::Alignment QwtScaleDraw::labelAlignment() const
+{
+    return d_data->labelAlignment;
+}
+
+/*!
+  \param font Font
+  \return the maximum width of a label
+*/
+int QwtScaleDraw::maxLabelWidth( const QFont &font ) const
+{
+    double maxWidth = 0.0;
+
+    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
+    for ( int i = 0; i < ticks.count(); i++ )
+    {
+        const double v = ticks[i];
+        if ( scaleDiv().contains( v ) )
+        {
+            const double w = labelSize( font, ticks[i] ).width();
+            if ( w > maxWidth )
+                maxWidth = w;
+        }
+    }
+
+    return qCeil( maxWidth );
+}
+
+/*!
+  \param font Font
+  \return the maximum height of a label
+*/
+int QwtScaleDraw::maxLabelHeight( const QFont &font ) const
+{
+    double maxHeight = 0.0;
+
+    const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
+    for ( int i = 0; i < ticks.count(); i++ )
+    {
+        const double v = ticks[i];
+        if ( scaleDiv().contains( v ) )
+        {
+            const double h = labelSize( font, ticks[i] ).height();
+            if ( h > maxHeight )
+                maxHeight = h;
+        }
+    }
+
+    return qCeil( maxHeight );
+}
+
+void QwtScaleDraw::updateMap()
+{
+    const QPointF pos = d_data->pos;
+    double len = d_data->len;
+
+    QwtScaleMap &sm = scaleMap();
+    if ( orientation() == Qt::Vertical )
+        sm.setPaintInterval( pos.y() + len, pos.y() );
+    else
+        sm.setPaintInterval( pos.x(), pos.x() + len );
+}
diff --git a/source/third_party/qwt/qwt_scale_engine.cpp b/source/third_party/qwt/qwt_scale_engine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a44dba691b846e57e54b51f6e48f4cd2d0d6d675
--- /dev/null
+++ b/source/third_party/qwt/qwt_scale_engine.cpp
@@ -0,0 +1,1140 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_scale_engine.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_scale_map.h"
+#include <qalgorithms.h>
+#include <qmath.h>
+#include <float.h>
+#include <limits>
+
+#if QT_VERSION < 0x040601
+#define qFabs(x) ::fabs(x)
+#define qExp(x) ::exp(x)
+#endif
+
+static inline double qwtLog( double base, double value )
+{
+    return log( value ) / log( base );
+}
+
+static inline QwtInterval qwtLogInterval( double base, const QwtInterval &interval )
+{
+    return QwtInterval( qwtLog( base, interval.minValue() ),
+            qwtLog( base, interval.maxValue() ) );
+}
+
+static inline QwtInterval qwtPowInterval( double base, const QwtInterval &interval ) 
+{
+    return QwtInterval( qPow( base, interval.minValue() ),
+            qPow( base, interval.maxValue() ) );
+}
+
+static inline long double qwtIntervalWidthL( const QwtInterval &interval )
+{
+    if ( !interval.isValid() )
+        return 0.0;
+
+    return static_cast<long double>( interval.maxValue() )
+        - static_cast<long double>( interval.minValue() );
+}
+
+#if 1
+
+// this version often doesn't find the best ticks: f.e for 15: 5, 10
+static double qwtStepSize( double intervalSize, int maxSteps, uint base )
+{
+    const double minStep = 
+        QwtScaleArithmetic::divideInterval( intervalSize, maxSteps, base );
+
+    if ( minStep != 0.0 )
+    {
+        // # ticks per interval
+        const int numTicks = qCeil( qAbs( intervalSize / minStep ) ) - 1;
+
+        // Do the minor steps fit into the interval?
+        if ( qwtFuzzyCompare( ( numTicks +  1 ) * qAbs( minStep ),
+            qAbs( intervalSize ), intervalSize ) > 0 )
+        {
+            // The minor steps doesn't fit into the interval
+            return 0.5 * intervalSize;
+        }
+    }
+
+    return minStep;
+}
+
+#else
+
+static double qwtStepSize( double intervalSize, int maxSteps, uint base )
+{
+    if ( maxSteps <= 0 )
+        return 0.0;
+
+    if ( maxSteps > 2 )
+    {
+        for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
+        {
+            const double stepSize = intervalSize / numSteps;
+
+            const double p = ::floor( ::log( stepSize ) / ::log( base ) );
+            const double fraction = qPow( base, p );
+
+            for ( uint n = base; n > 1; n /= 2 )
+            {
+                if ( qFuzzyCompare( stepSize, n * fraction ) )
+                    return stepSize;
+
+                if ( n == 3 && ( base % 2 ) == 0 )
+                {
+                    if ( qFuzzyCompare( stepSize, 2 * fraction ) )
+                        return stepSize;
+                }
+            }
+        }
+    }
+
+    return intervalSize * 0.5;
+}
+
+#endif
+
+static const double _eps = 1.0e-6;
+
+/*!
+  Ceil a value, relative to an interval
+
+  \param value Value to be ceiled
+  \param intervalSize Interval size
+
+  \return Rounded value
+
+  \sa floorEps()
+*/
+double QwtScaleArithmetic::ceilEps( double value,
+    double intervalSize )
+{
+    const double eps = _eps * intervalSize;
+
+    value = ( value - eps ) / intervalSize;
+    return ::ceil( value ) * intervalSize;
+}
+
+/*!
+  Floor a value, relative to an interval
+
+  \param value Value to be floored
+  \param intervalSize Interval size
+
+  \return Rounded value
+  \sa floorEps()
+*/
+double QwtScaleArithmetic::floorEps( double value, double intervalSize )
+{
+    const double eps = _eps * intervalSize;
+
+    value = ( value + eps ) / intervalSize;
+    return ::floor( value ) * intervalSize;
+}
+
+/*!
+  \brief Divide an interval into steps
+
+  \f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$
+
+  \param intervalSize Interval size
+  \param numSteps Number of steps
+  \return Step size
+*/
+double QwtScaleArithmetic::divideEps( double intervalSize, double numSteps )
+{
+    if ( numSteps == 0.0 || intervalSize == 0.0 )
+        return 0.0;
+
+    return ( intervalSize - ( _eps * intervalSize ) ) / numSteps;
+}
+
+/*!
+  Calculate a step size for a given interval
+
+  \param intervalSize Interval size
+  \param numSteps Number of steps
+  \param base Base for the division ( usually 10 )
+
+  \return Calculated step size
+ */
+double QwtScaleArithmetic::divideInterval( 
+    double intervalSize, int numSteps, uint base ) 
+{
+    if ( numSteps <= 0 )
+        return 0.0;
+
+    const double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps );
+    if ( v == 0.0 )
+        return 0.0;
+
+    const double lx = qwtLog( base, qFabs( v ) );
+    const double p = ::floor( lx );
+
+    const double fraction = qPow( base, lx - p );
+
+    uint n = base;
+    while ( ( n > 1 ) && ( fraction <= n / 2 ) )
+        n /= 2;
+
+    double stepSize = n * qPow( base, p );
+    if ( v < 0 )
+        stepSize = -stepSize;
+
+    return stepSize;
+}
+
+class QwtScaleEngine::PrivateData
+{
+public:
+    PrivateData():
+        attributes( QwtScaleEngine::NoAttribute ),
+        lowerMargin( 0.0 ),
+        upperMargin( 0.0 ),
+        referenceValue( 0.0 ),
+        base( 10 ),
+        transform( NULL )
+    {
+    }
+
+    ~PrivateData()
+    {
+        delete transform;
+    }
+
+    QwtScaleEngine::Attributes attributes;
+
+    double lowerMargin;
+    double upperMargin;
+
+    double referenceValue;
+
+    uint base;
+
+    QwtTransform* transform;
+};
+
+/*!
+  Constructor
+
+  \param base Base of the scale engine
+  \sa setBase()
+ */
+QwtScaleEngine::QwtScaleEngine( uint base )
+{
+    d_data = new PrivateData;
+    setBase( base );
+}
+
+
+//! Destructor
+QwtScaleEngine::~QwtScaleEngine ()
+{
+    delete d_data;
+}
+
+/*!
+   Assign a transformation
+
+   \param transform Transformation
+
+   The transformation object is used as factory for clones
+   that are returned by transformation()
+
+   The scale engine takes ownership of the transformation.
+
+   \sa QwtTransform::copy(), transformation()
+
+ */
+void QwtScaleEngine::setTransformation( QwtTransform *transform )
+{
+    if ( transform != d_data->transform )
+    {
+        delete d_data->transform;
+        d_data->transform = transform;
+    }
+}
+
+/*!
+   Create and return a clone of the transformation 
+   of the engine. When the engine has no special transformation
+   NULL is returned, indicating no transformation.
+
+   \return A clone of the transfomation
+   \sa setTransformation()
+ */
+QwtTransform *QwtScaleEngine::transformation() const
+{
+    QwtTransform *transform = NULL;
+    if ( d_data->transform )
+        transform = d_data->transform->copy();
+
+    return transform;
+}
+
+/*!
+    \return the margin at the lower end of the scale
+    The default margin is 0.
+
+    \sa setMargins()
+*/
+double QwtScaleEngine::lowerMargin() const
+{
+    return d_data->lowerMargin;
+}
+
+/*!
+    \return the margin at the upper end of the scale
+    The default margin is 0.
+
+    \sa setMargins()
+*/
+double QwtScaleEngine::upperMargin() const
+{
+    return d_data->upperMargin;
+}
+
+/*!
+  \brief Specify margins at the scale's endpoints
+  \param lower minimum distance between the scale's lower boundary and the
+             smallest enclosed value
+  \param upper minimum distance between the scale's upper boundary and the
+             greatest enclosed value
+
+  Margins can be used to leave a minimum amount of space between
+  the enclosed intervals and the boundaries of the scale.
+
+  \warning
+  \li QwtLogScaleEngine measures the margins in decades.
+
+  \sa upperMargin(), lowerMargin()
+*/
+
+void QwtScaleEngine::setMargins( double lower, double upper )
+{
+    d_data->lowerMargin = qMax( lower, 0.0 );
+    d_data->upperMargin = qMax( upper, 0.0 );
+}
+
+/*!
+  Calculate a step size for an interval size
+
+  \param intervalSize Interval size
+  \param numSteps Number of steps
+
+  \return Step size
+*/
+double QwtScaleEngine::divideInterval(
+    double intervalSize, int numSteps ) const
+{
+    return QwtScaleArithmetic::divideInterval( 
+        intervalSize, numSteps, d_data->base );
+}
+
+/*!
+  Check if an interval "contains" a value
+
+  \param interval Interval
+  \param value Value
+
+  \return True, when the value is inside the interval
+*/
+bool QwtScaleEngine::contains(
+    const QwtInterval &interval, double value ) const
+{
+    if ( !interval.isValid() )
+        return false;
+
+    if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 )
+        return false;
+
+    if ( qwtFuzzyCompare( value, interval.maxValue(), interval.width() ) > 0 )
+        return false;
+
+    return true;
+}
+
+/*!
+  Remove ticks from a list, that are not inside an interval
+
+  \param ticks Tick list
+  \param interval Interval
+
+  \return Stripped tick list
+*/
+QList<double> QwtScaleEngine::strip( const QList<double>& ticks,
+    const QwtInterval &interval ) const
+{
+    if ( !interval.isValid() || ticks.count() == 0 )
+        return QList<double>();
+
+    if ( contains( interval, ticks.first() )
+        && contains( interval, ticks.last() ) )
+    {
+        return ticks;
+    }
+
+    QList<double> strippedTicks;
+    for ( int i = 0; i < ticks.count(); i++ )
+    {
+        if ( contains( interval, ticks[i] ) )
+            strippedTicks += ticks[i];
+    }
+    return strippedTicks;
+}
+
+/*!
+  \brief Build an interval around a value
+
+  In case of v == 0.0 the interval is [-0.5, 0.5],
+  otherwide it is [0.5 * v, 1.5 * v]
+
+  \param value Initial value
+  \return Calculated interval
+*/
+
+QwtInterval QwtScaleEngine::buildInterval( double value ) const
+{
+    const double delta = ( value == 0.0 ) ? 0.5 : qAbs( 0.5 * value );
+
+    if ( DBL_MAX - delta < value )
+        return QwtInterval( DBL_MAX - delta, DBL_MAX );
+
+    if ( -DBL_MAX + delta > value )
+        return QwtInterval( -DBL_MAX, -DBL_MAX + delta );
+
+    return QwtInterval( value - delta, value + delta );
+}
+
+/*!
+  Change a scale attribute
+
+  \param attribute Attribute to change
+  \param on On/Off
+
+  \sa Attribute, testAttribute()
+*/
+void QwtScaleEngine::setAttribute( Attribute attribute, bool on )
+{
+    if ( on )
+        d_data->attributes |= attribute;
+    else
+        d_data->attributes &= ~attribute;
+}
+
+/*!
+  \return True, if attribute is enabled.
+
+  \param attribute Attribute to be tested
+  \sa Attribute, setAttribute()
+*/
+bool QwtScaleEngine::testAttribute( Attribute attribute ) const
+{
+    return ( d_data->attributes & attribute );
+}
+
+/*!
+  Change the scale attribute
+
+  \param attributes Set scale attributes
+  \sa Attribute, attributes()
+*/
+void QwtScaleEngine::setAttributes( Attributes attributes )
+{
+    d_data->attributes = attributes;
+}
+
+/*!
+  \return Scale attributes
+  \sa Attribute, setAttributes(), testAttribute()
+*/
+QwtScaleEngine::Attributes QwtScaleEngine::attributes() const
+{
+    return d_data->attributes;
+}
+
+/*!
+  \brief Specify a reference point
+  \param r new reference value
+
+  The reference point is needed if options IncludeReference or
+  Symmetric are active. Its default value is 0.0.
+
+  \sa Attribute
+*/
+void QwtScaleEngine::setReference( double r )
+{
+    d_data->referenceValue = r;
+}
+
+/*!
+  \return the reference value
+  \sa setReference(), setAttribute()
+*/
+double QwtScaleEngine::reference() const
+{
+    return d_data->referenceValue;
+}
+
+/*!
+  Set the base of the scale engine
+
+  While a base of 10 is what 99.9% of all applications need
+  certain scales might need a different base: f.e 2
+
+  The default setting is 10
+
+  \param base Base of the engine
+
+  \sa base()
+ */
+void QwtScaleEngine::setBase( uint base )
+{ 
+    d_data->base = qMax( base, 2U );
+}
+
+/*!
+  \return base Base of the scale engine
+  \sa setBase()
+ */
+uint QwtScaleEngine::base() const
+{
+    return d_data->base;
+}
+
+/*!
+  Constructor
+
+  \param base Base of the scale engine
+  \sa setBase()
+ */
+QwtLinearScaleEngine::QwtLinearScaleEngine( uint base ):
+    QwtScaleEngine( base )
+{
+}
+
+//! Destructor
+QwtLinearScaleEngine::~QwtLinearScaleEngine()
+{
+}
+
+/*!
+  Align and divide an interval
+
+  \param maxNumSteps Max. number of steps
+  \param x1 First limit of the interval (In/Out)
+  \param x2 Second limit of the interval (In/Out)
+  \param stepSize Step size (Out)
+
+  \sa setAttribute()
+*/
+void QwtLinearScaleEngine::autoScale( int maxNumSteps,
+    double &x1, double &x2, double &stepSize ) const
+{
+    QwtInterval interval( x1, x2 );
+    interval = interval.normalized();
+
+    interval.setMinValue( interval.minValue() - lowerMargin() );
+    interval.setMaxValue( interval.maxValue() + upperMargin() );
+
+    if ( testAttribute( QwtScaleEngine::Symmetric ) )
+        interval = interval.symmetrize( reference() );
+
+    if ( testAttribute( QwtScaleEngine::IncludeReference ) )
+        interval = interval.extend( reference() );
+
+    if ( interval.width() == 0.0 )
+        interval = buildInterval( interval.minValue() );
+
+    stepSize = QwtScaleArithmetic::divideInterval( 
+        interval.width(), qMax( maxNumSteps, 1 ), base() );
+
+    if ( !testAttribute( QwtScaleEngine::Floating ) )
+        interval = align( interval, stepSize );
+
+    x1 = interval.minValue();
+    x2 = interval.maxValue();
+
+    if ( testAttribute( QwtScaleEngine::Inverted ) )
+    {
+        qSwap( x1, x2 );
+        stepSize = -stepSize;
+    }
+}
+
+/*!
+   \brief Calculate a scale division for an interval
+
+   \param x1 First interval limit
+   \param x2 Second interval limit
+   \param maxMajorSteps Maximum for the number of major steps
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size. If stepSize == 0, the engine
+                   calculates one.
+
+   \return Calculated scale division
+*/
+QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2,
+    int maxMajorSteps, int maxMinorSteps, double stepSize ) const
+{
+    QwtInterval interval = QwtInterval( x1, x2 ).normalized();
+
+    if ( qwtIntervalWidthL( interval ) > std::numeric_limits<double>::max() )
+    {
+        qWarning() << "QwtLinearScaleEngine::divideScale: overflow";
+        return QwtScaleDiv();
+    }
+
+    if ( interval.width() <= 0 )
+        return QwtScaleDiv();
+
+    stepSize = qAbs( stepSize );
+    if ( stepSize == 0.0 )
+    {
+        if ( maxMajorSteps < 1 )
+            maxMajorSteps = 1;
+
+        stepSize = QwtScaleArithmetic::divideInterval( 
+            interval.width(), maxMajorSteps, base() );
+    }
+
+    QwtScaleDiv scaleDiv;
+
+    if ( stepSize != 0.0 )
+    {
+        QList<double> ticks[QwtScaleDiv::NTickTypes];
+        buildTicks( interval, stepSize, maxMinorSteps, ticks );
+
+        scaleDiv = QwtScaleDiv( interval, ticks );
+    }
+
+    if ( x1 > x2 )
+        scaleDiv.invert();
+
+    return scaleDiv;
+}
+
+/*!
+   \brief Calculate ticks for an interval
+
+   \param interval Interval
+   \param stepSize Step size
+   \param maxMinorSteps Maximum number of minor steps
+   \param ticks Arrays to be filled with the calculated ticks
+
+   \sa buildMajorTicks(), buildMinorTicks
+*/
+void QwtLinearScaleEngine::buildTicks(
+    const QwtInterval& interval, double stepSize, int maxMinorSteps,
+    QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
+{
+    const QwtInterval boundingInterval = align( interval, stepSize );
+
+    ticks[QwtScaleDiv::MajorTick] =
+        buildMajorTicks( boundingInterval, stepSize );
+
+    if ( maxMinorSteps > 0 )
+    {
+        buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
+            ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
+    }
+
+    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+    {
+        ticks[i] = strip( ticks[i], interval );
+
+        // ticks very close to 0.0 are
+        // explicitely set to 0.0
+
+        for ( int j = 0; j < ticks[i].count(); j++ )
+        {
+            if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 0 )
+                ticks[i][j] = 0.0;
+        }
+    }
+}
+
+/*!
+   \brief Calculate major ticks for an interval
+
+   \param interval Interval
+   \param stepSize Step size
+
+   \return Calculated ticks
+*/
+QList<double> QwtLinearScaleEngine::buildMajorTicks(
+    const QwtInterval &interval, double stepSize ) const
+{
+    int numTicks = qRound( interval.width() / stepSize ) + 1;
+    if ( numTicks > 10000 )
+        numTicks = 10000;
+
+    QList<double> ticks;
+
+    ticks += interval.minValue();
+    for ( int i = 1; i < numTicks - 1; i++ )
+        ticks += interval.minValue() + i * stepSize;
+    ticks += interval.maxValue();
+
+    return ticks;
+}
+
+/*!
+   \brief Calculate minor/medium ticks for major ticks
+
+   \param majorTicks Major ticks
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size
+   \param minorTicks Array to be filled with the calculated minor ticks
+   \param mediumTicks Array to be filled with the calculated medium ticks
+
+*/
+void QwtLinearScaleEngine::buildMinorTicks(
+    const QList<double>& majorTicks,
+    int maxMinorSteps, double stepSize,
+    QList<double> &minorTicks,
+    QList<double> &mediumTicks ) const
+{
+    double minStep = qwtStepSize( stepSize, maxMinorSteps, base() );
+    if ( minStep == 0.0 )
+        return;
+
+    // # ticks per interval
+    const int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1;
+
+    int medIndex = -1;
+    if ( numTicks % 2 )
+        medIndex = numTicks / 2;
+
+    // calculate minor ticks
+
+    for ( int i = 0; i < majorTicks.count(); i++ )
+    {
+        double val = majorTicks[i];
+        for ( int k = 0; k < numTicks; k++ )
+        {
+            val += minStep;
+
+            double alignedValue = val;
+            if ( qwtFuzzyCompare( val, 0.0, stepSize ) == 0 )
+                alignedValue = 0.0;
+
+            if ( k == medIndex )
+                mediumTicks += alignedValue;
+            else
+                minorTicks += alignedValue;
+        }
+    }
+}
+
+/*!
+  \brief Align an interval to a step size
+
+  The limits of an interval are aligned that both are integer
+  multiples of the step size.
+
+  \param interval Interval
+  \param stepSize Step size
+
+  \return Aligned interval
+*/
+QwtInterval QwtLinearScaleEngine::align(
+    const QwtInterval &interval, double stepSize ) const
+{
+    double x1 = interval.minValue();
+    double x2 = interval.maxValue();
+
+    // when there is no rounding beside some effect, when 
+    // calculating with doubles, we keep the original value
+
+    const double eps = 0.000000000001; // since Qt 4.8: qFuzzyIsNull
+    if ( -DBL_MAX + stepSize <= x1 )
+    {
+        const double x = QwtScaleArithmetic::floorEps( x1, stepSize );
+        if ( qAbs(x) <= eps || !qFuzzyCompare( x1, x ) )
+            x1 = x;
+    }
+
+    if ( DBL_MAX - stepSize >= x2 )
+    {
+        const double x = QwtScaleArithmetic::ceilEps( x2, stepSize );
+        if ( qAbs(x) <= eps || !qFuzzyCompare( x2, x ) )
+            x2 = x;
+    }
+
+    return QwtInterval( x1, x2 );
+}
+
+/*!
+  Constructor
+
+  \param base Base of the scale engine
+  \sa setBase()
+ */
+QwtLogScaleEngine::QwtLogScaleEngine( uint base ):
+    QwtScaleEngine( base )
+{
+    setTransformation( new QwtLogTransform() );
+}
+
+//! Destructor
+QwtLogScaleEngine::~QwtLogScaleEngine()
+{
+}
+
+/*!
+    Align and divide an interval
+
+   \param maxNumSteps Max. number of steps
+   \param x1 First limit of the interval (In/Out)
+   \param x2 Second limit of the interval (In/Out)
+   \param stepSize Step size (Out)
+
+   \sa QwtScaleEngine::setAttribute()
+*/
+void QwtLogScaleEngine::autoScale( int maxNumSteps,
+    double &x1, double &x2, double &stepSize ) const
+{
+    if ( x1 > x2 )
+        qSwap( x1, x2 );
+
+    const double logBase = base();
+
+    QwtInterval interval( x1 / qPow( logBase, lowerMargin() ),
+        x2 * qPow( logBase, upperMargin() ) );
+
+    if ( interval.maxValue() / interval.minValue() < logBase )
+    {
+        // scale width is less than one step -> try to build a linear scale
+
+        QwtLinearScaleEngine linearScaler;
+        linearScaler.setAttributes( attributes() );
+        linearScaler.setReference( reference() );
+        linearScaler.setMargins( lowerMargin(), upperMargin() );
+
+        linearScaler.autoScale( maxNumSteps, x1, x2, stepSize );
+
+        QwtInterval linearInterval = QwtInterval( x1, x2 ).normalized();
+        linearInterval = linearInterval.limited( LOG_MIN, LOG_MAX );
+
+        if ( linearInterval.maxValue() / linearInterval.minValue() < logBase )
+        {
+            // the aligned scale is still less than one step
+
+#if 1
+            // this code doesn't make any sense, but for compatibility
+            // reasons we keep it until 6.2. But it will be ignored
+            // in divideScale
+
+            if ( stepSize < 0.0 )
+                stepSize = -qwtLog( logBase, qAbs( stepSize ) );
+            else
+                stepSize = qwtLog( logBase, stepSize );
+#endif
+
+            return;
+        }
+    }
+
+    double logRef = 1.0;
+    if ( reference() > LOG_MIN / 2 )
+        logRef = qMin( reference(), LOG_MAX / 2 );
+
+    if ( testAttribute( QwtScaleEngine::Symmetric ) )
+    {
+        const double delta = qMax( interval.maxValue() / logRef,
+            logRef / interval.minValue() );
+        interval.setInterval( logRef / delta, logRef * delta );
+    }
+
+    if ( testAttribute( QwtScaleEngine::IncludeReference ) )
+        interval = interval.extend( logRef );
+
+    interval = interval.limited( LOG_MIN, LOG_MAX );
+
+    if ( interval.width() == 0.0 )
+        interval = buildInterval( interval.minValue() );
+
+    stepSize = divideInterval( qwtLogInterval( logBase, interval ).width(), 
+        qMax( maxNumSteps, 1 ) );
+    if ( stepSize < 1.0 )
+        stepSize = 1.0;
+
+    if ( !testAttribute( QwtScaleEngine::Floating ) )
+        interval = align( interval, stepSize );
+
+    x1 = interval.minValue();
+    x2 = interval.maxValue();
+
+    if ( testAttribute( QwtScaleEngine::Inverted ) )
+    {
+        qSwap( x1, x2 );
+        stepSize = -stepSize;
+    }
+}
+
+/*!
+   \brief Calculate a scale division for an interval
+
+   \param x1 First interval limit
+   \param x2 Second interval limit
+   \param maxMajorSteps Maximum for the number of major steps
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size. If stepSize == 0, the engine
+                   calculates one.
+
+   \return Calculated scale division
+*/
+QwtScaleDiv QwtLogScaleEngine::divideScale( double x1, double x2,
+    int maxMajorSteps, int maxMinorSteps, double stepSize ) const
+{
+    QwtInterval interval = QwtInterval( x1, x2 ).normalized();
+    interval = interval.limited( LOG_MIN, LOG_MAX );
+
+    if ( interval.width() <= 0 )
+        return QwtScaleDiv();
+
+    const double logBase = base();
+
+    if ( interval.maxValue() / interval.minValue() < logBase )
+    {
+        // scale width is less than one decade -> build linear scale
+
+        QwtLinearScaleEngine linearScaler;
+        linearScaler.setAttributes( attributes() );
+        linearScaler.setReference( reference() );
+        linearScaler.setMargins( lowerMargin(), upperMargin() );
+
+        return linearScaler.divideScale( x1, x2,
+            maxMajorSteps, maxMinorSteps, 0.0 );
+    }
+
+    stepSize = qAbs( stepSize );
+    if ( stepSize == 0.0 )
+    {
+        if ( maxMajorSteps < 1 )
+            maxMajorSteps = 1;
+
+        stepSize = divideInterval( 
+            qwtLogInterval( logBase, interval ).width(), maxMajorSteps );
+        if ( stepSize < 1.0 )
+            stepSize = 1.0; // major step must be >= 1 decade
+    }
+
+    QwtScaleDiv scaleDiv;
+    if ( stepSize != 0.0 )
+    {
+        QList<double> ticks[QwtScaleDiv::NTickTypes];
+        buildTicks( interval, stepSize, maxMinorSteps, ticks );
+
+        scaleDiv = QwtScaleDiv( interval, ticks );
+    }
+
+    if ( x1 > x2 )
+        scaleDiv.invert();
+
+    return scaleDiv;
+}
+
+/*!
+   \brief Calculate ticks for an interval
+
+   \param interval Interval
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size
+   \param ticks Arrays to be filled with the calculated ticks
+
+   \sa buildMajorTicks(), buildMinorTicks
+*/
+void QwtLogScaleEngine::buildTicks(
+    const QwtInterval& interval, double stepSize, int maxMinorSteps,
+    QList<double> ticks[QwtScaleDiv::NTickTypes] ) const
+{
+    const QwtInterval boundingInterval = align( interval, stepSize );
+
+    ticks[QwtScaleDiv::MajorTick] =
+        buildMajorTicks( boundingInterval, stepSize );
+
+    if ( maxMinorSteps > 0 )
+    {
+        buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinorSteps, stepSize,
+            ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] );
+    }
+
+    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
+        ticks[i] = strip( ticks[i], interval );
+}
+
+/*!
+   \brief Calculate major ticks for an interval
+
+   \param interval Interval
+   \param stepSize Step size
+
+   \return Calculated ticks
+*/
+QList<double> QwtLogScaleEngine::buildMajorTicks(
+    const QwtInterval &interval, double stepSize ) const
+{
+    double width = qwtLogInterval( base(), interval ).width();
+
+    int numTicks = qRound( width / stepSize ) + 1;
+    if ( numTicks > 10000 )
+        numTicks = 10000;
+
+    const double lxmin = ::log( interval.minValue() );
+    const double lxmax = ::log( interval.maxValue() );
+    const double lstep = ( lxmax - lxmin ) / double( numTicks - 1 );
+
+    QList<double> ticks;
+
+    ticks += interval.minValue();
+
+    for ( int i = 1; i < numTicks - 1; i++ )
+        ticks += qExp( lxmin + double( i ) * lstep );
+
+    ticks += interval.maxValue();
+
+    return ticks;
+}
+
+/*!
+   \brief Calculate minor/medium ticks for major ticks
+
+   \param majorTicks Major ticks
+   \param maxMinorSteps Maximum number of minor steps
+   \param stepSize Step size
+   \param minorTicks Array to be filled with the calculated minor ticks
+   \param mediumTicks Array to be filled with the calculated medium ticks
+*/
+void QwtLogScaleEngine::buildMinorTicks(
+    const QList<double> &majorTicks,
+    int maxMinorSteps, double stepSize,
+    QList<double> &minorTicks,
+    QList<double> &mediumTicks ) const
+{
+    const double logBase = base();
+
+    if ( stepSize < 1.1 )          // major step width is one base
+    {
+        double minStep = divideInterval( stepSize, maxMinorSteps + 1 );
+        if ( minStep == 0.0 )
+            return;
+        
+        const int numSteps = qRound( stepSize / minStep ); 
+
+        int mediumTickIndex = -1;
+        if ( ( numSteps > 2 ) && ( numSteps % 2 == 0 ) )
+            mediumTickIndex = numSteps / 2;
+
+        for ( int i = 0; i < majorTicks.count() - 1; i++ )
+        {
+            const double v = majorTicks[i];
+            const double s = logBase / numSteps;
+
+            if ( s >= 1.0 )
+            {
+                if ( !qFuzzyCompare( s, 1.0 ) )
+                    minorTicks += v * s;
+
+                for ( int j = 2; j < numSteps; j++ )
+                {
+                    minorTicks += v * j * s;
+                }
+            }
+            else
+            {
+                for ( int j = 1; j < numSteps; j++ )
+                {
+                    const double tick = v + j * v * ( logBase - 1 ) / numSteps;
+                    if ( j == mediumTickIndex )
+                        mediumTicks += tick;
+                    else
+                        minorTicks += tick;
+                }
+            }
+        }
+    }
+    else
+    {
+        double minStep = divideInterval( stepSize, maxMinorSteps );
+        if ( minStep == 0.0 )
+            return;
+
+        if ( minStep < 1.0 )
+            minStep = 1.0;
+
+        // # subticks per interval
+        int numTicks = qRound( stepSize / minStep ) - 1;
+
+        // Do the minor steps fit into the interval?
+        if ( qwtFuzzyCompare( ( numTicks +  1 ) * minStep,
+            stepSize, stepSize ) > 0 )
+        {
+            numTicks = 0;
+        }
+
+        if ( numTicks < 1 )
+            return; 
+
+        int mediumTickIndex = -1;
+        if ( ( numTicks > 2 ) && ( numTicks % 2 ) )
+            mediumTickIndex = numTicks / 2;
+
+        // substep factor = base^substeps
+        const qreal minFactor = qMax( qPow( logBase, minStep ), qreal( logBase ) );
+
+        for ( int i = 0; i < majorTicks.count(); i++ )
+        {
+            double tick = majorTicks[i];
+            for ( int j = 0; j < numTicks; j++ )
+            {
+                tick *= minFactor;
+
+                if ( j == mediumTickIndex )
+                    mediumTicks += tick;
+                else
+                    minorTicks += tick;
+            }
+        }
+    }
+}
+
+/*!
+  \brief Align an interval to a step size
+
+  The limits of an interval are aligned that both are integer
+  multiples of the step size.
+
+  \param interval Interval
+  \param stepSize Step size
+
+  \return Aligned interval
+*/
+QwtInterval QwtLogScaleEngine::align(
+    const QwtInterval &interval, double stepSize ) const
+{
+    const QwtInterval intv = qwtLogInterval( base(), interval );
+
+    double x1 = QwtScaleArithmetic::floorEps( intv.minValue(), stepSize );
+    if ( qwtFuzzyCompare( interval.minValue(), x1, stepSize ) == 0 )
+        x1 = interval.minValue();
+
+    double x2 = QwtScaleArithmetic::ceilEps( intv.maxValue(), stepSize );
+    if ( qwtFuzzyCompare( interval.maxValue(), x2, stepSize ) == 0 )
+        x2 = interval.maxValue();
+
+    return qwtPowInterval( base(), QwtInterval( x1, x2 ) );
+}
diff --git a/source/third_party/qwt/qwt_scale_map.cpp b/source/third_party/qwt/qwt_scale_map.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5eec915ff20817369502299cccf240a7bb045136
--- /dev/null
+++ b/source/third_party/qwt/qwt_scale_map.cpp
@@ -0,0 +1,248 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_math.h"
+#include <qrect.h>
+#include <qdebug.h>
+
+/*!
+  \brief Constructor
+
+  The scale and paint device intervals are both set to [0,1].
+*/
+QwtScaleMap::QwtScaleMap():
+    d_s1( 0.0 ),
+    d_s2( 1.0 ),
+    d_p1( 0.0 ),
+    d_p2( 1.0 ),
+    d_cnv( 1.0 ),
+    d_ts1( 0.0 ),
+    d_transform( NULL )
+{
+}
+
+//! Copy constructor
+QwtScaleMap::QwtScaleMap( const QwtScaleMap& other ):
+    d_s1( other.d_s1 ),
+    d_s2( other.d_s2 ),
+    d_p1( other.d_p1 ),
+    d_p2( other.d_p2 ),
+    d_cnv( other.d_cnv ),
+    d_ts1( other.d_ts1 ),
+    d_transform( NULL )
+{
+    if ( other.d_transform )
+        d_transform = other.d_transform->copy();
+}
+
+/*!
+  Destructor
+*/
+QwtScaleMap::~QwtScaleMap()
+{
+    delete d_transform;
+}
+
+//! Assignment operator
+QwtScaleMap &QwtScaleMap::operator=( const QwtScaleMap & other )
+{
+    d_s1 = other.d_s1;
+    d_s2 = other.d_s2;
+    d_p1 = other.d_p1;
+    d_p2 = other.d_p2;
+    d_cnv = other.d_cnv;
+    d_ts1 = other.d_ts1;
+
+    delete d_transform;
+    d_transform = NULL;
+
+    if ( other.d_transform )
+        d_transform = other.d_transform->copy();
+
+    return *this;
+}
+
+/*!
+   Initialize the map with a transformation
+*/
+void QwtScaleMap::setTransformation( QwtTransform *transform )
+{
+    if ( transform != d_transform )
+    {
+        delete d_transform;
+        d_transform = transform;
+    }
+
+    setScaleInterval( d_s1, d_s2 );
+}
+
+//! Get the transformation
+const QwtTransform *QwtScaleMap::transformation() const
+{
+    return d_transform;
+}
+
+/*!
+  \brief Specify the borders of the scale interval
+  \param s1 first border
+  \param s2 second border
+  \warning scales might be aligned to 
+           transformation depending boundaries
+*/
+void QwtScaleMap::setScaleInterval( double s1, double s2 )
+{
+    d_s1 = s1;
+    d_s2 = s2;
+
+    if ( d_transform )
+    {
+        d_s1 = d_transform->bounded( d_s1 );
+        d_s2 = d_transform->bounded( d_s2 );
+    }
+
+    updateFactor();
+}
+
+/*!
+  \brief Specify the borders of the paint device interval
+  \param p1 first border
+  \param p2 second border
+*/
+void QwtScaleMap::setPaintInterval( double p1, double p2 )
+{
+    d_p1 = p1;
+    d_p2 = p2;
+
+    updateFactor();
+}
+
+void QwtScaleMap::updateFactor()
+{
+    d_ts1 = d_s1;
+    double ts2 = d_s2;
+
+    if ( d_transform )
+    {
+        d_ts1 = d_transform->transform( d_ts1 );
+        ts2 = d_transform->transform( ts2 );
+    }
+
+    d_cnv = 1.0;
+    if ( d_ts1 != ts2 )
+        d_cnv = ( d_p2 - d_p1 ) / ( ts2 - d_ts1 );
+}
+
+/*!
+   Transform a rectangle from scale to paint coordinates
+
+   \param xMap X map
+   \param yMap Y map
+   \param rect Rectangle in scale coordinates
+   \return Rectangle in paint coordinates
+
+   \sa invTransform()
+*/
+QRectF QwtScaleMap::transform( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap, const QRectF &rect )
+{
+    double x1 = xMap.transform( rect.left() );
+    double x2 = xMap.transform( rect.right() );
+    double y1 = yMap.transform( rect.top() );
+    double y2 = yMap.transform( rect.bottom() );
+
+    if ( x2 < x1 )
+        qSwap( x1, x2 );
+    if ( y2 < y1 )
+        qSwap( y1, y2 );
+
+    if ( qwtFuzzyCompare( x1, 0.0, x2 - x1 ) == 0 )
+        x1 = 0.0;
+    if ( qwtFuzzyCompare( x2, 0.0, x2 - x1 ) == 0 )
+        x2 = 0.0;
+    if ( qwtFuzzyCompare( y1, 0.0, y2 - y1 ) == 0 )
+        y1 = 0.0;
+    if ( qwtFuzzyCompare( y2, 0.0, y2 - y1 ) == 0 )
+        y2 = 0.0;
+
+    return QRectF( x1, y1, x2 - x1 + 1, y2 - y1 + 1 );
+}
+
+/*!
+   Transform a rectangle from paint to scale coordinates
+
+   \param xMap X map
+   \param yMap Y map
+   \param pos Position in paint coordinates
+   \return Position in scale coordinates
+   \sa transform()
+*/
+QPointF QwtScaleMap::invTransform( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap, const QPointF &pos )
+{
+    return QPointF( 
+        xMap.invTransform( pos.x() ), 
+        yMap.invTransform( pos.y() ) 
+    );
+}
+
+/*!
+   Transform a point from scale to paint coordinates
+
+   \param xMap X map
+   \param yMap Y map
+   \param pos Position in scale coordinates
+   \return Position in paint coordinates
+
+   \sa invTransform()
+*/
+QPointF QwtScaleMap::transform( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap, const QPointF &pos )
+{
+    return QPointF( 
+        xMap.transform( pos.x() ), 
+        yMap.transform( pos.y() )
+    );
+}
+
+/*!
+   Transform a rectangle from paint to scale coordinates
+
+   \param xMap X map
+   \param yMap Y map
+   \param rect Rectangle in paint coordinates
+   \return Rectangle in scale coordinates
+   \sa transform()
+*/
+QRectF QwtScaleMap::invTransform( const QwtScaleMap &xMap,
+    const QwtScaleMap &yMap, const QRectF &rect )
+{
+    const double x1 = xMap.invTransform( rect.left() );
+    const double x2 = xMap.invTransform( rect.right() - 1 );
+    const double y1 = yMap.invTransform( rect.top() );
+    const double y2 = yMap.invTransform( rect.bottom() - 1 );
+
+    const QRectF r( x1, y1, x2 - x1, y2 - y1 );
+    return r.normalized();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<( QDebug debug, const QwtScaleMap &map )
+{
+    debug.nospace() << "QwtScaleMap("
+        << map.transformation()
+        << ", s:" << map.s1() << "->" << map.s2()
+        << ", p:" << map.p1() << "->" << map.p2()
+        << ")";
+
+    return debug.space();
+}
+
+#endif
diff --git a/source/third_party/qwt/qwt_scale_widget.cpp b/source/third_party/qwt/qwt_scale_widget.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..27b483d27a85cee181c6da3d4c6eeff057ba3482
--- /dev/null
+++ b/source/third_party/qwt/qwt_scale_widget.cpp
@@ -0,0 +1,942 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_scale_widget.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_color_map.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_scale_div.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_scale_engine.h"
+#include <qpainter.h>
+#include <qevent.h>
+#include <qmath.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+
+class QwtScaleWidget::PrivateData
+{
+public:
+    PrivateData():
+        scaleDraw( NULL )
+    {
+        colorBar.colorMap = NULL;
+    }
+
+    ~PrivateData()
+    {
+        delete scaleDraw;
+        delete colorBar.colorMap;
+    }
+
+    QwtScaleDraw *scaleDraw;
+
+    int borderDist[2];
+    int minBorderDist[2];
+    int scaleLength;
+    int margin;
+
+    int titleOffset;
+    int spacing;
+    QwtText title;
+
+    QwtScaleWidget::LayoutFlags layoutFlags;
+
+    struct t_colorBar
+    {
+        bool isEnabled;
+        int width;
+        QwtInterval interval;
+        QwtColorMap *colorMap;
+    } colorBar;
+};
+
+/*!
+  \brief Create a scale with the position QwtScaleWidget::Left
+  \param parent Parent widget
+*/
+QwtScaleWidget::QwtScaleWidget( QWidget *parent ):
+    QWidget( parent )
+{
+    initScale( QwtScaleDraw::LeftScale );
+}
+
+/*!
+  \brief Constructor
+  \param align Alignment.
+  \param parent Parent widget
+*/
+QwtScaleWidget::QwtScaleWidget(
+        QwtScaleDraw::Alignment align, QWidget *parent ):
+    QWidget( parent )
+{
+    initScale( align );
+}
+
+//! Destructor
+QwtScaleWidget::~QwtScaleWidget()
+{
+    delete d_data;
+}
+
+//! Initialize the scale
+void QwtScaleWidget::initScale( QwtScaleDraw::Alignment align )
+{
+    d_data = new PrivateData;
+
+    d_data->layoutFlags = 0;
+    if ( align == QwtScaleDraw::RightScale )
+        d_data->layoutFlags |= TitleInverted;
+
+    d_data->borderDist[0] = 0;
+    d_data->borderDist[1] = 0;
+    d_data->minBorderDist[0] = 0;
+    d_data->minBorderDist[1] = 0;
+    d_data->margin = 4;
+    d_data->titleOffset = 0;
+    d_data->spacing = 2;
+
+    d_data->scaleDraw = new QwtScaleDraw;
+    d_data->scaleDraw->setAlignment( align );
+    d_data->scaleDraw->setLength( 10 );
+
+    d_data->scaleDraw->setScaleDiv(
+        QwtLinearScaleEngine().divideScale( 0.0, 100.0, 10, 5 ) );
+
+    d_data->colorBar.colorMap = new QwtLinearColorMap();
+    d_data->colorBar.isEnabled = false;
+    d_data->colorBar.width = 10;
+
+    const int flags = Qt::AlignHCenter
+        | Qt::TextExpandTabs | Qt::TextWordWrap;
+    d_data->title.setRenderFlags( flags );
+    d_data->title.setFont( font() );
+
+    QSizePolicy policy( QSizePolicy::MinimumExpanding,
+        QSizePolicy::Fixed );
+    if ( d_data->scaleDraw->orientation() == Qt::Vertical )
+        policy.transpose();
+
+    setSizePolicy( policy );
+
+    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+}
+
+/*!
+   Toggle an layout flag
+
+   \param flag Layout flag
+   \param on true/false
+
+   \sa testLayoutFlag(), LayoutFlag
+*/
+void QwtScaleWidget::setLayoutFlag( LayoutFlag flag, bool on )
+{
+    if ( ( ( d_data->layoutFlags & flag ) != 0 ) != on )
+    {
+        if ( on )
+            d_data->layoutFlags |= flag;
+        else
+            d_data->layoutFlags &= ~flag;
+    }
+}
+
+/*!
+   Test a layout flag
+
+   \param flag Layout flag
+   \return true/false
+   \sa setLayoutFlag(), LayoutFlag
+*/
+bool QwtScaleWidget::testLayoutFlag( LayoutFlag flag ) const
+{
+    return ( d_data->layoutFlags & flag );
+}
+
+/*!
+  Give title new text contents
+
+  \param title New title
+  \sa title(), setTitle(const QwtText &);
+*/
+void QwtScaleWidget::setTitle( const QString &title )
+{
+    if ( d_data->title.text() != title )
+    {
+        d_data->title.setText( title );
+        layoutScale();
+    }
+}
+
+/*!
+  Give title new text contents
+
+  \param title New title
+  \sa title()
+  \warning The title flags are interpreted in
+               direction of the label, AlignTop, AlignBottom can't be set
+               as the title will always be aligned to the scale.
+*/
+void QwtScaleWidget::setTitle( const QwtText &title )
+{
+    QwtText t = title;
+    const int flags = title.renderFlags() & ~( Qt::AlignTop | Qt::AlignBottom );
+    t.setRenderFlags( flags );
+
+    if ( t != d_data->title )
+    {
+        d_data->title = t;
+        layoutScale();
+    }
+}
+
+/*!
+  Change the alignment
+
+  \param alignment New alignment
+  \sa alignment()
+*/
+void QwtScaleWidget::setAlignment( QwtScaleDraw::Alignment alignment )
+{
+    if ( d_data->scaleDraw )
+        d_data->scaleDraw->setAlignment( alignment );
+
+    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
+    {
+        QSizePolicy policy( QSizePolicy::MinimumExpanding,
+            QSizePolicy::Fixed );
+        if ( d_data->scaleDraw->orientation() == Qt::Vertical )
+            policy.transpose();
+
+        setSizePolicy( policy );
+
+        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    }
+
+    layoutScale();
+}
+
+
+/*!
+    \return position
+    \sa setPosition()
+*/
+QwtScaleDraw::Alignment QwtScaleWidget::alignment() const
+{
+    if ( !scaleDraw() )
+        return QwtScaleDraw::LeftScale;
+
+    return scaleDraw()->alignment();
+}
+
+/*!
+  Specify distances of the scale's endpoints from the
+  widget's borders. The actual borders will never be less
+  than minimum border distance.
+  \param dist1 Left or top Distance
+  \param dist2 Right or bottom distance
+  \sa borderDist()
+*/
+void QwtScaleWidget::setBorderDist( int dist1, int dist2 )
+{
+    if ( dist1 != d_data->borderDist[0] || dist2 != d_data->borderDist[1] )
+    {
+        d_data->borderDist[0] = dist1;
+        d_data->borderDist[1] = dist2;
+        layoutScale();
+    }
+}
+
+/*!
+  \brief Specify the margin to the colorBar/base line.
+  \param margin Margin
+  \sa margin()
+*/
+void QwtScaleWidget::setMargin( int margin )
+{
+    margin = qMax( 0, margin );
+    if ( margin != d_data->margin )
+    {
+        d_data->margin = margin;
+        layoutScale();
+    }
+}
+
+/*!
+  \brief Specify the distance between color bar, scale and title
+  \param spacing Spacing
+  \sa spacing()
+*/
+void QwtScaleWidget::setSpacing( int spacing )
+{
+    spacing = qMax( 0, spacing );
+    if ( spacing != d_data->spacing )
+    {
+        d_data->spacing = spacing;
+        layoutScale();
+    }
+}
+
+/*!
+  \brief Change the alignment for the labels.
+
+  \sa QwtScaleDraw::setLabelAlignment(), setLabelRotation()
+*/
+void QwtScaleWidget::setLabelAlignment( Qt::Alignment alignment )
+{
+    d_data->scaleDraw->setLabelAlignment( alignment );
+    layoutScale();
+}
+
+/*!
+  \brief Change the rotation for the labels.
+  See QwtScaleDraw::setLabelRotation().
+
+  \param rotation Rotation
+  \sa QwtScaleDraw::setLabelRotation(), setLabelFlags()
+*/
+void QwtScaleWidget::setLabelRotation( double rotation )
+{
+    d_data->scaleDraw->setLabelRotation( rotation );
+    layoutScale();
+}
+
+/*!
+  Set a scale draw
+
+  scaleDraw has to be created with new and will be deleted in
+  ~QwtScaleWidget() or the next call of setScaleDraw().
+  scaleDraw will be initialized with the attributes of
+  the previous scaleDraw object.
+
+  \param scaleDraw ScaleDraw object
+  \sa scaleDraw()
+*/
+void QwtScaleWidget::setScaleDraw( QwtScaleDraw *scaleDraw )
+{
+    if ( ( scaleDraw == NULL ) || ( scaleDraw == d_data->scaleDraw ) )
+        return;
+
+    const QwtScaleDraw* sd = d_data->scaleDraw;
+    if ( sd )
+    {
+        scaleDraw->setAlignment( sd->alignment() );
+        scaleDraw->setScaleDiv( sd->scaleDiv() );
+
+        QwtTransform *transform = NULL;
+        if ( sd->scaleMap().transformation() )
+            transform = sd->scaleMap().transformation()->copy();
+
+        scaleDraw->setTransformation( transform );
+    }
+
+    delete d_data->scaleDraw;
+    d_data->scaleDraw = scaleDraw;
+
+    layoutScale();
+}
+
+/*!
+    \return scaleDraw of this scale
+    \sa setScaleDraw(), QwtScaleDraw::setScaleDraw()
+*/
+const QwtScaleDraw *QwtScaleWidget::scaleDraw() const
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+    \return scaleDraw of this scale
+    \sa QwtScaleDraw::setScaleDraw()
+*/
+QwtScaleDraw *QwtScaleWidget::scaleDraw()
+{
+    return d_data->scaleDraw;
+}
+
+/*!
+    \return title
+    \sa setTitle()
+*/
+QwtText QwtScaleWidget::title() const
+{
+    return d_data->title;
+}
+
+/*!
+    \return start border distance
+    \sa setBorderDist()
+*/
+int QwtScaleWidget::startBorderDist() const
+{
+    return d_data->borderDist[0];
+}
+
+/*!
+    \return end border distance
+    \sa setBorderDist()
+*/
+int QwtScaleWidget::endBorderDist() const
+{
+    return d_data->borderDist[1];
+}
+
+/*!
+    \return margin
+    \sa setMargin()
+*/
+int QwtScaleWidget::margin() const
+{
+    return d_data->margin;
+}
+
+/*!
+    \return distance between scale and title
+    \sa setMargin()
+*/
+int QwtScaleWidget::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+  \brief paintEvent
+*/
+void QwtScaleWidget::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    draw( &painter );
+}
+
+/*!
+  \brief draw the scale
+*/
+void QwtScaleWidget::draw( QPainter *painter ) const
+{
+    d_data->scaleDraw->draw( painter, palette() );
+
+    if ( d_data->colorBar.isEnabled && d_data->colorBar.width > 0 &&
+        d_data->colorBar.interval.isValid() )
+    {
+        drawColorBar( painter, colorBarRect( contentsRect() ) );
+    }
+
+    QRect r = contentsRect();
+    if ( d_data->scaleDraw->orientation() == Qt::Horizontal )
+    {
+        r.setLeft( r.left() + d_data->borderDist[0] );
+        r.setWidth( r.width() - d_data->borderDist[1] );
+    }
+    else
+    {
+        r.setTop( r.top() + d_data->borderDist[0] );
+        r.setHeight( r.height() - d_data->borderDist[1] );
+    }
+
+    if ( !d_data->title.isEmpty() )
+        drawTitle( painter, d_data->scaleDraw->alignment(), r );
+}
+
+/*!
+  Calculate the the rectangle for the color bar
+
+  \param rect Bounding rectangle for all components of the scale
+  \return Rectangle for the color bar
+*/
+QRectF QwtScaleWidget::colorBarRect( const QRectF& rect ) const
+{
+    QRectF cr = rect;
+
+    if ( d_data->scaleDraw->orientation() == Qt::Horizontal )
+    {
+        cr.setLeft( cr.left() + d_data->borderDist[0] );
+        cr.setWidth( cr.width() - d_data->borderDist[1] + 1 );
+    }
+    else
+    {
+        cr.setTop( cr.top() + d_data->borderDist[0] );
+        cr.setHeight( cr.height() - d_data->borderDist[1] + 1 );
+    }
+
+    switch ( d_data->scaleDraw->alignment() )
+    {
+        case QwtScaleDraw::LeftScale:
+        {
+            cr.setLeft( cr.right() - d_data->margin
+                - d_data->colorBar.width );
+            cr.setWidth( d_data->colorBar.width );
+            break;
+        }
+
+        case QwtScaleDraw::RightScale:
+        {
+            cr.setLeft( cr.left() + d_data->margin );
+            cr.setWidth( d_data->colorBar.width );
+            break;
+        }
+
+        case QwtScaleDraw::BottomScale:
+        {
+            cr.setTop( cr.top() + d_data->margin );
+            cr.setHeight( d_data->colorBar.width );
+            break;
+        }
+
+        case QwtScaleDraw::TopScale:
+        {
+            cr.setTop( cr.bottom() - d_data->margin
+                - d_data->colorBar.width );
+            cr.setHeight( d_data->colorBar.width );
+            break;
+        }
+    }
+
+    return cr;
+}
+
+/*!
+  Event handler for resize events
+  \param event Resize event
+*/
+void QwtScaleWidget::resizeEvent( QResizeEvent *event )
+{
+    Q_UNUSED( event );
+    layoutScale( false );
+}
+
+/*!
+  Recalculate the scale's geometry and layout based on
+  the current geometry and fonts.
+
+  \param update_geometry Notify the layout system and call update
+                         to redraw the scale
+*/
+
+void QwtScaleWidget::layoutScale( bool update_geometry )
+{
+    int bd0, bd1;
+    getBorderDistHint( bd0, bd1 );
+    if ( d_data->borderDist[0] > bd0 )
+        bd0 = d_data->borderDist[0];
+    if ( d_data->borderDist[1] > bd1 )
+        bd1 = d_data->borderDist[1];
+
+    int colorBarWidth = 0;
+    if ( d_data->colorBar.isEnabled && d_data->colorBar.interval.isValid() )
+        colorBarWidth = d_data->colorBar.width + d_data->spacing;
+
+    const QRectF r = contentsRect();
+    double x, y, length;
+
+    if ( d_data->scaleDraw->orientation() == Qt::Vertical )
+    {
+        y = r.top() + bd0;
+        length = r.height() - ( bd0 + bd1 );
+
+        if ( d_data->scaleDraw->alignment() == QwtScaleDraw::LeftScale )
+            x = r.right() - 1.0 - d_data->margin - colorBarWidth;
+        else
+            x = r.left() + d_data->margin + colorBarWidth;
+    }
+    else
+    {
+        x = r.left() + bd0;
+        length = r.width() - ( bd0 + bd1 );
+
+        if ( d_data->scaleDraw->alignment() == QwtScaleDraw::BottomScale )
+            y = r.top() + d_data->margin + colorBarWidth;
+        else
+            y = r.bottom() - 1.0 - d_data->margin - colorBarWidth;
+    }
+
+    d_data->scaleDraw->move( x, y );
+    d_data->scaleDraw->setLength( length );
+
+    const int extent = qCeil( d_data->scaleDraw->extent( font() ) );
+
+    d_data->titleOffset =
+        d_data->margin + d_data->spacing + colorBarWidth + extent;
+
+    if ( update_geometry )
+    {
+        updateGeometry();
+        update();
+    }
+}
+
+/*!
+  Draw the color bar of the scale widget
+
+  \param painter Painter
+  \param rect Bounding rectangle for the color bar
+
+  \sa setColorBarEnabled()
+*/
+void QwtScaleWidget::drawColorBar( QPainter *painter, const QRectF& rect ) const
+{
+    if ( !d_data->colorBar.interval.isValid() )
+        return;
+
+    const QwtScaleDraw* sd = d_data->scaleDraw;
+
+    QwtPainter::drawColorBar( painter, *d_data->colorBar.colorMap,
+        d_data->colorBar.interval.normalized(), sd->scaleMap(),
+        sd->orientation(), rect );
+}
+
+/*!
+  Rotate and paint a title according to its position into a given rectangle.
+
+  \param painter Painter
+  \param align Alignment
+  \param rect Bounding rectangle
+*/
+
+void QwtScaleWidget::drawTitle( QPainter *painter,
+    QwtScaleDraw::Alignment align, const QRectF &rect ) const
+{
+    QRectF r = rect;
+    double angle;
+    int flags = d_data->title.renderFlags() &
+        ~( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter );
+
+    switch ( align )
+    {
+        case QwtScaleDraw::LeftScale:
+            angle = -90.0;
+            flags |= Qt::AlignTop;
+            r.setRect( r.left(), r.bottom(),
+                r.height(), r.width() - d_data->titleOffset );
+            break;
+
+        case QwtScaleDraw::RightScale:
+            angle = -90.0;
+            flags |= Qt::AlignTop;
+            r.setRect( r.left() + d_data->titleOffset, r.bottom(),
+                r.height(), r.width() - d_data->titleOffset );
+            break;
+
+        case QwtScaleDraw::BottomScale:
+            angle = 0.0;
+            flags |= Qt::AlignBottom;
+            r.setTop( r.top() + d_data->titleOffset );
+            break;
+
+        case QwtScaleDraw::TopScale:
+        default:
+            angle = 0.0;
+            flags |= Qt::AlignTop;
+            r.setBottom( r.bottom() - d_data->titleOffset );
+            break;
+    }
+
+    if ( d_data->layoutFlags & TitleInverted )
+    {
+        if ( align == QwtScaleDraw::LeftScale
+            || align == QwtScaleDraw::RightScale )
+        {
+            angle = -angle;
+            r.setRect( r.x() + r.height(), r.y() - r.width(),
+                r.width(), r.height() );
+        }
+    }
+
+    painter->save();
+    painter->setFont( font() );
+    painter->setPen( palette().color( QPalette::Text ) );
+
+    painter->translate( r.x(), r.y() );
+    if ( angle != 0.0 )
+        painter->rotate( angle );
+
+    QwtText title = d_data->title;
+    title.setRenderFlags( flags );
+    title.draw( painter, QRectF( 0.0, 0.0, r.width(), r.height() ) );
+
+    painter->restore();
+}
+
+/*!
+  \brief Notify a change of the scale
+
+  This virtual function can be overloaded by derived
+  classes. The default implementation updates the geometry
+  and repaints the widget.
+*/
+
+void QwtScaleWidget::scaleChange()
+{
+    layoutScale();
+}
+
+/*!
+  \return a size hint
+*/
+QSize QwtScaleWidget::sizeHint() const
+{
+    return minimumSizeHint();
+}
+
+/*!
+  \return a minimum size hint
+*/
+QSize QwtScaleWidget::minimumSizeHint() const
+{
+    const Qt::Orientation o = d_data->scaleDraw->orientation();
+
+    // Border Distance cannot be less than the scale borderDistHint
+    // Note, the borderDistHint is already included in minHeight/minWidth
+    int length = 0;
+    int mbd1, mbd2;
+    getBorderDistHint( mbd1, mbd2 );
+    length += qMax( 0, d_data->borderDist[0] - mbd1 );
+    length += qMax( 0, d_data->borderDist[1] - mbd2 );
+    length += d_data->scaleDraw->minLength( font() );
+
+    int dim = dimForLength( length, font() );
+    if ( length < dim )
+    {
+        // compensate for long titles
+        length = dim;
+        dim = dimForLength( length, font() );
+    }
+
+    QSize size( length + 2, dim );
+    if ( o == Qt::Vertical )
+        size.transpose();
+
+    int left, right, top, bottom;
+    getContentsMargins( &left, &top, &right, &bottom );
+    return size + QSize( left + right, top + bottom );
+}
+
+/*!
+  \brief Find the height of the title for a given width.
+  \param width Width
+  \return height Height
+ */
+
+int QwtScaleWidget::titleHeightForWidth( int width ) const
+{
+    return qCeil( d_data->title.heightForWidth( width, font() ) );
+}
+
+/*!
+  \brief Find the minimum dimension for a given length.
+         dim is the height, length the width seen in
+         direction of the title.
+  \param length width for horizontal, height for vertical scales
+  \param scaleFont Font of the scale
+  \return height for horizontal, width for vertical scales
+*/
+
+int QwtScaleWidget::dimForLength( int length, const QFont &scaleFont ) const
+{
+    const int extent = qCeil( d_data->scaleDraw->extent( scaleFont ) );
+
+    int dim = d_data->margin + extent + 1;
+
+    if ( !d_data->title.isEmpty() )
+        dim += titleHeightForWidth( length ) + d_data->spacing;
+
+    if ( d_data->colorBar.isEnabled && d_data->colorBar.interval.isValid() )
+        dim += d_data->colorBar.width + d_data->spacing;
+
+    return dim;
+}
+
+/*!
+  \brief Calculate a hint for the border distances.
+
+  This member function calculates the distance
+  of the scale's endpoints from the widget borders which
+  is required for the mark labels to fit into the widget.
+  The maximum of this distance an the minimum border distance
+  is returned.
+
+  \param start Return parameter for the border width at 
+               the beginning of the scale
+  \param end Return parameter for the border width at the 
+             end of the scale
+
+  \warning
+  <ul> <li>The minimum border distance depends on the font.</ul>
+  \sa setMinBorderDist(), getMinBorderDist(), setBorderDist()
+*/
+void QwtScaleWidget::getBorderDistHint( int &start, int &end ) const
+{
+    d_data->scaleDraw->getBorderDistHint( font(), start, end );
+
+    if ( start < d_data->minBorderDist[0] )
+        start = d_data->minBorderDist[0];
+
+    if ( end < d_data->minBorderDist[1] )
+        end = d_data->minBorderDist[1];
+}
+
+/*!
+  Set a minimum value for the distances of the scale's endpoints from
+  the widget borders. This is useful to avoid that the scales
+  are "jumping", when the tick labels or their positions change
+  often.
+
+  \param start Minimum for the start border
+  \param end Minimum for the end border
+  \sa getMinBorderDist(), getBorderDistHint()
+*/
+void QwtScaleWidget::setMinBorderDist( int start, int end )
+{
+    d_data->minBorderDist[0] = start;
+    d_data->minBorderDist[1] = end;
+}
+
+/*!
+  Get the minimum value for the distances of the scale's endpoints from
+  the widget borders.
+
+  \param start Return parameter for the border width at 
+               the beginning of the scale
+  \param end Return parameter for the border width at the 
+             end of the scale
+
+  \sa setMinBorderDist(), getBorderDistHint()
+*/
+void QwtScaleWidget::getMinBorderDist( int &start, int &end ) const
+{
+    start = d_data->minBorderDist[0];
+    end = d_data->minBorderDist[1];
+}
+
+/*!
+  \brief Assign a scale division
+
+  The scale division determines where to set the tick marks.
+
+  \param scaleDiv Scale Division
+  \sa For more information about scale divisions, see QwtScaleDiv.
+*/
+void QwtScaleWidget::setScaleDiv( const QwtScaleDiv &scaleDiv )
+{
+    QwtScaleDraw *sd = d_data->scaleDraw;
+    if ( sd->scaleDiv() != scaleDiv )
+    {
+        sd->setScaleDiv( scaleDiv );
+        layoutScale();
+
+        Q_EMIT scaleDivChanged();
+    }
+}
+
+/*!
+  Set the transformation
+
+  \param transformation Transformation
+  \sa QwtAbstractScaleDraw::scaleDraw(), QwtScaleMap
+ */
+void QwtScaleWidget::setTransformation( QwtTransform *transformation )
+{
+    d_data->scaleDraw->setTransformation( transformation );
+    layoutScale();
+}
+
+/*!
+  En/disable a color bar associated to the scale
+  \sa isColorBarEnabled(), setColorBarWidth()
+*/
+void QwtScaleWidget::setColorBarEnabled( bool on )
+{
+    if ( on != d_data->colorBar.isEnabled )
+    {
+        d_data->colorBar.isEnabled = on;
+        layoutScale();
+    }
+}
+
+/*!
+  \return true, when the color bar is enabled
+  \sa setColorBarEnabled(), setColorBarWidth()
+*/
+bool QwtScaleWidget::isColorBarEnabled() const
+{
+    return d_data->colorBar.isEnabled;
+}
+
+/*!
+  Set the width of the color bar
+
+  \param width Width
+  \sa colorBarWidth(), setColorBarEnabled()
+*/
+void QwtScaleWidget::setColorBarWidth( int width )
+{
+    if ( width != d_data->colorBar.width )
+    {
+        d_data->colorBar.width = width;
+        if ( isColorBarEnabled() )
+            layoutScale();
+    }
+}
+
+/*!
+  \return Width of the color bar
+  \sa setColorBarEnabled(), setColorBarEnabled()
+*/
+int QwtScaleWidget::colorBarWidth() const
+{
+    return d_data->colorBar.width;
+}
+
+/*!
+  \return Value interval for the color bar
+  \sa setColorMap(), colorMap()
+*/
+QwtInterval QwtScaleWidget::colorBarInterval() const
+{
+    return d_data->colorBar.interval;
+}
+
+/*!
+  Set the color map and value interval, that are used for displaying
+  the color bar.
+
+  \param interval Value interval
+  \param colorMap Color map
+
+  \sa colorMap(), colorBarInterval()
+*/
+void QwtScaleWidget::setColorMap(
+    const QwtInterval &interval, QwtColorMap *colorMap )
+{
+    d_data->colorBar.interval = interval;
+
+    if ( colorMap != d_data->colorBar.colorMap )
+    {
+        delete d_data->colorBar.colorMap;
+        d_data->colorBar.colorMap = colorMap;
+    }
+
+    if ( isColorBarEnabled() )
+        layoutScale();
+}
+
+/*!
+  \return Color map
+  \sa setColorMap(), colorBarInterval()
+*/
+const QwtColorMap *QwtScaleWidget::colorMap() const
+{
+    return d_data->colorBar.colorMap;
+}
diff --git a/source/third_party/qwt/qwt_series_data.cpp b/source/third_party/qwt/qwt_series_data.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..46661393d74045f53146ed530241c644e23b2279
--- /dev/null
+++ b/source/third_party/qwt/qwt_series_data.cpp
@@ -0,0 +1,346 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_series_data.h"
+#include "qwt/qwt_math.h"
+
+static inline QRectF qwtBoundingRect( const QPointF &sample )
+{
+    return QRectF( sample.x(), sample.y(), 0.0, 0.0 );
+}
+
+static inline QRectF qwtBoundingRect( const QwtPoint3D &sample )
+{
+    return QRectF( sample.x(), sample.y(), 0.0, 0.0 );
+}
+
+static inline QRectF qwtBoundingRect( const QwtPointPolar &sample )
+{
+    return QRectF( sample.azimuth(), sample.radius(), 0.0, 0.0 );
+}
+
+static inline QRectF qwtBoundingRect( const QwtIntervalSample &sample )
+{
+    return QRectF( sample.interval.minValue(), sample.value,
+        sample.interval.maxValue() - sample.interval.minValue(), 0.0 );
+}
+
+static inline QRectF qwtBoundingRect( const QwtSetSample &sample )
+{
+    double minY = sample.set[0];
+    double maxY = sample.set[0];
+
+    for ( int i = 1; i < sample.set.size(); i++ )
+    {
+        if ( sample.set[i] < minY )
+            minY = sample.set[i];
+        if ( sample.set[i] > maxY )
+            maxY = sample.set[i];
+    }
+
+    double minX = sample.value;
+    double maxX = sample.value;
+
+    return QRectF( minX, minY, maxX - minX, maxY - minY );
+}
+
+static inline QRectF qwtBoundingRect( const QwtOHLCSample &sample )
+{
+    const QwtInterval interval = sample.boundingInterval();
+    return QRectF( interval.minValue(), sample.time, interval.width(), 0.0 );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+
+template <class T>
+QRectF qwtBoundingRectT(
+    const QwtSeriesData<T>& series, int from, int to )
+{
+    QRectF boundingRect( 1.0, 1.0, -2.0, -2.0 ); // invalid;
+
+    if ( from < 0 )
+        from = 0;
+
+    if ( to < 0 )
+        to = series.size() - 1;
+
+    if ( to < from )
+        return boundingRect;
+
+    int i;
+    for ( i = from; i <= to; i++ )
+    {
+        const QRectF rect = qwtBoundingRect( series.sample( i ) );
+        if ( rect.width() >= 0.0 && rect.height() >= 0.0 )
+        {
+            boundingRect = rect;
+            i++;
+            break;
+        }
+    }
+
+    for ( ; i <= to; i++ )
+    {
+        const QRectF rect = qwtBoundingRect( series.sample( i ) );
+        if ( rect.width() >= 0.0 && rect.height() >= 0.0 )
+        {
+            boundingRect.setLeft( qMin( boundingRect.left(), rect.left() ) );
+            boundingRect.setRight( qMax( boundingRect.right(), rect.right() ) );
+            boundingRect.setTop( qMin( boundingRect.top(), rect.top() ) );
+            boundingRect.setBottom( qMax( boundingRect.bottom(), rect.bottom() ) );
+        }
+    }
+
+    return boundingRect;
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QPointF> &series, int from, int to )
+{
+    return qwtBoundingRectT<QPointF>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtPoint3D> &series, int from, int to )
+{
+    return qwtBoundingRectT<QwtPoint3D>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  The horizontal coordinates represent the azimuth, the
+  vertical coordinates the radius.
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtPointPolar> &series, int from, int to )
+{
+    return qwtBoundingRectT<QwtPointPolar>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtIntervalSample>& series, int from, int to )
+{
+    return qwtBoundingRectT<QwtIntervalSample>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtOHLCSample>& series, int from, int to )
+{
+    return qwtBoundingRectT<QwtOHLCSample>( series, from, to );
+}
+
+/*!
+  \brief Calculate the bounding rectangle of a series subset
+
+  Slow implementation, that iterates over the series.
+
+  \param series Series
+  \param from Index of the first sample, <= 0 means from the beginning
+  \param to Index of the last sample, < 0 means to the end
+
+  \return Bounding rectangle
+*/
+QRectF qwtBoundingRect(
+    const QwtSeriesData<QwtSetSample>& series, int from, int to )
+{
+    return qwtBoundingRectT<QwtSetSample>( series, from, to );
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtPointSeriesData::QwtPointSeriesData(
+        const QVector<QPointF> &samples ):
+    QwtArraySeriesData<QPointF>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtPointSeriesData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtPoint3DSeriesData::QwtPoint3DSeriesData(
+        const QVector<QwtPoint3D> &samples ):
+    QwtArraySeriesData<QwtPoint3D>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtPoint3DSeriesData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtIntervalSeriesData::QwtIntervalSeriesData(
+        const QVector<QwtIntervalSample> &samples ):
+    QwtArraySeriesData<QwtIntervalSample>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtIntervalSeriesData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtSetSeriesData::QwtSetSeriesData(
+        const QVector<QwtSetSample> &samples ):
+    QwtArraySeriesData<QwtSetSample>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtSetSeriesData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
+
+/*!
+   Constructor
+   \param samples Samples
+*/
+QwtTradingChartData::QwtTradingChartData(
+        const QVector<QwtOHLCSample> &samples ):
+    QwtArraySeriesData<QwtOHLCSample>( samples )
+{
+}
+
+/*!
+  \brief Calculate the bounding rectangle
+
+  The bounding rectangle is calculated once by iterating over all
+  points and is stored for all following requests.
+
+  \return Bounding rectangle
+*/
+QRectF QwtTradingChartData::boundingRect() const
+{
+    if ( d_boundingRect.width() < 0.0 )
+        d_boundingRect = qwtBoundingRect( *this );
+
+    return d_boundingRect;
+}
diff --git a/source/third_party/qwt/qwt_slider.cpp b/source/third_party/qwt/qwt_slider.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a399372c87681098cf48b4ea69c330d3b271da6
--- /dev/null
+++ b/source/third_party/qwt/qwt_slider.cpp
@@ -0,0 +1,1004 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_slider.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_scale_draw.h"
+#include "qwt/qwt_scale_map.h"
+#include <qevent.h>
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qalgorithms.h>
+#include <qmath.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+
+static QSize qwtHandleSize( const QSize &size, 
+    Qt::Orientation orientation, bool hasTrough )
+{
+    QSize handleSize = size;
+
+    if ( handleSize.isEmpty() )
+    {
+        const int handleThickness = 16;
+        handleSize.setWidth( 2 * handleThickness );
+        handleSize.setHeight( handleThickness );
+
+        if ( !hasTrough )
+            handleSize.transpose();
+
+        if ( orientation == Qt::Vertical )
+            handleSize.transpose();
+    }
+
+    return handleSize;
+}
+
+static QwtScaleDraw::Alignment qwtScaleDrawAlignment( 
+    Qt::Orientation orientation, QwtSlider::ScalePosition scalePos )
+{
+    QwtScaleDraw::Alignment align;
+
+    if ( orientation == Qt::Vertical )
+    {
+        // NoScale lays out like Left
+        if ( scalePos == QwtSlider::LeadingScale )
+            align = QwtScaleDraw::RightScale;
+        else
+            align = QwtScaleDraw::LeftScale;
+    }
+    else
+    {
+        // NoScale lays out like Bottom
+        if ( scalePos == QwtSlider::TrailingScale )
+            align = QwtScaleDraw::TopScale;
+        else
+            align = QwtScaleDraw::BottomScale;
+    }
+
+    return align;
+}
+
+class QwtSlider::PrivateData
+{
+public:
+    PrivateData():
+        repeatTimerId( 0 ),
+        updateInterval( 150 ),
+        stepsIncrement( 0 ),
+        pendingValueChange( false ),
+        borderWidth( 2 ),
+        spacing( 4 ),
+        scalePosition( QwtSlider::TrailingScale ),
+        hasTrough( true ),
+        hasGroove( false ),
+        mouseOffset( 0 )
+    {
+    }
+
+    int repeatTimerId;
+    bool timerTick;
+    int updateInterval;
+    int stepsIncrement;
+    bool pendingValueChange;
+
+    QRect sliderRect;
+
+    QSize handleSize;
+    int borderWidth;
+    int spacing;
+
+    Qt::Orientation orientation;
+    QwtSlider::ScalePosition scalePosition;
+
+    bool hasTrough;
+    bool hasGroove;
+
+    int mouseOffset;
+
+    mutable QSize sizeHintCache;
+};
+/*!
+  Construct vertical slider in QwtSlider::Trough style
+  with a scale to the left. 
+
+  The scale is initialized to [0.0, 100.0] and the value set to 0.0.
+
+  \param parent Parent widget
+
+  \sa setOrientation(), setScalePosition(), setBackgroundStyle()
+*/
+QwtSlider::QwtSlider( QWidget *parent ):
+    QwtAbstractSlider( parent )
+{
+    initSlider( Qt::Vertical );
+}
+
+/*!
+  Construct a slider in QwtSlider::Trough style
+
+  When orientation is Qt::Vertical the scale will be aligned to
+  the left - otherwise at the the top of the slider.
+
+  The scale is initialized to [0.0, 100.0] and the value set to 0.0.
+
+  \param parent Parent widget
+  \param orientation Orientation of the slider. 
+*/
+QwtSlider::QwtSlider( Qt::Orientation orientation, QWidget *parent ):
+    QwtAbstractSlider( parent )
+{
+    initSlider( orientation );
+}
+
+//! Destructor
+QwtSlider::~QwtSlider()
+{
+    delete d_data;
+}
+
+void QwtSlider::initSlider( Qt::Orientation orientation )
+{
+    if ( orientation == Qt::Vertical )
+        setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
+    else
+        setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
+
+    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+
+    d_data = new QwtSlider::PrivateData;
+
+    d_data->orientation = orientation;
+
+    scaleDraw()->setAlignment( 
+        qwtScaleDrawAlignment( orientation, d_data->scalePosition ) );
+    scaleDraw()->setLength( 100 );
+
+    setScale( 0.0, 100.0 );
+    setValue( 0.0 );
+}
+
+/*!
+  \brief Set the orientation.
+  \param orientation Allowed values are Qt::Horizontal and Qt::Vertical.
+
+  \sa orientation(), scalePosition()
+*/
+void QwtSlider::setOrientation( Qt::Orientation orientation )
+{
+    if ( orientation == d_data->orientation )
+        return;
+
+    d_data->orientation = orientation;
+
+    scaleDraw()->setAlignment( 
+        qwtScaleDrawAlignment( orientation, d_data->scalePosition ) );
+
+    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
+    {
+        QSizePolicy sp = sizePolicy();
+        sp.transpose();
+        setSizePolicy( sp );
+
+        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    }
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutSlider( true );
+}
+
+/*!
+  \return Orientation
+  \sa setOrientation()
+*/
+Qt::Orientation QwtSlider::orientation() const
+{
+    return d_data->orientation;
+}
+
+/*!
+  \brief Change the position of the scale
+  \param scalePosition Position of the scale.
+
+  \sa ScalePosition, scalePosition()
+*/
+void QwtSlider::setScalePosition( ScalePosition scalePosition )
+{
+    if ( d_data->scalePosition == scalePosition )
+        return;
+
+    d_data->scalePosition = scalePosition;
+    scaleDraw()->setAlignment( 
+        qwtScaleDrawAlignment( d_data->orientation, scalePosition ) );
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutSlider( true );
+}
+
+/*! 
+  \return Position of the scale
+  \sa setScalePosition()
+ */
+QwtSlider::ScalePosition QwtSlider::scalePosition() const
+{
+    return d_data->scalePosition;
+}
+
+/*!
+  \brief Change the slider's border width
+
+  The border width is used for drawing the slider handle and the
+  trough.
+
+  \param width Border width
+  \sa borderWidth()
+*/
+void QwtSlider::setBorderWidth( int width )
+{
+    if ( width < 0 )
+        width = 0;
+
+    if ( width != d_data->borderWidth )
+    {
+        d_data->borderWidth = width;
+
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return the border width.
+  \sa setBorderWidth()
+*/
+int QwtSlider::borderWidth() const
+{
+    return d_data->borderWidth;
+}
+
+/*!
+  \brief Change the spacing between trough and scale
+
+  A spacing of 0 means, that the backbone of the scale is covered
+  by the trough.
+
+  The default setting is 4 pixels.
+
+  \param spacing Number of pixels
+  \sa spacing();
+*/
+void QwtSlider::setSpacing( int spacing )
+{
+    if ( spacing <= 0 )
+        spacing = 0;
+
+    if ( spacing != d_data->spacing  )
+    {
+        d_data->spacing = spacing;
+
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return Number of pixels between slider and scale
+  \sa setSpacing()
+*/
+int QwtSlider::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+  \brief Set the slider's handle size
+
+  When the size is empty the slider handle will be painted with a
+  default size depending on its orientation() and backgroundStyle().
+
+  \param size New size
+
+  \sa handleSize()
+*/
+void QwtSlider::setHandleSize( const QSize &size )
+{
+    if ( size != d_data->handleSize )
+    {
+        d_data->handleSize = size;
+
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return Size of the handle.
+  \sa setHandleSize()
+*/
+QSize QwtSlider::handleSize() const
+{
+    return d_data->handleSize;
+}
+
+/*!
+  \brief Set a scale draw
+
+  For changing the labels of the scales, it
+  is necessary to derive from QwtScaleDraw and
+  overload QwtScaleDraw::label().
+
+  \param scaleDraw ScaleDraw object, that has to be created with
+                   new and will be deleted in ~QwtSlider() or the next
+                   call of setScaleDraw().
+
+  \sa scaleDraw()
+*/
+void QwtSlider::setScaleDraw( QwtScaleDraw *scaleDraw )
+{
+    const QwtScaleDraw *previousScaleDraw = this->scaleDraw();
+    if ( scaleDraw == NULL || scaleDraw == previousScaleDraw )
+        return;
+
+    if ( previousScaleDraw )
+        scaleDraw->setAlignment( previousScaleDraw->alignment() );
+
+    setAbstractScaleDraw( scaleDraw );
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutSlider( true );
+}
+
+/*!
+  \return the scale draw of the slider
+  \sa setScaleDraw()
+*/
+const QwtScaleDraw *QwtSlider::scaleDraw() const
+{
+    return static_cast<const QwtScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+  \return the scale draw of the slider
+  \sa setScaleDraw()
+*/
+QwtScaleDraw *QwtSlider::scaleDraw()
+{
+    return static_cast<QwtScaleDraw *>( abstractScaleDraw() );
+}
+
+//! Notify changed scale
+void QwtSlider::scaleChange()
+{
+    QwtAbstractSlider::scaleChange();
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutSlider( true );
+}
+
+/*!
+  \brief Specify the update interval for automatic scrolling
+
+  The minimal accepted value is 50 ms.
+
+  \param interval Update interval in milliseconds
+
+  \sa setUpdateInterval()
+*/
+void QwtSlider::setUpdateInterval( int interval )
+{
+    d_data->updateInterval = qMax( interval, 50 );
+}
+
+/*!
+  \return Update interval in milliseconds for automatic scrolling
+  \sa setUpdateInterval()
+ */
+int QwtSlider::updateInterval() const
+{
+    return d_data->updateInterval;
+}
+
+/*!
+   Draw the slider into the specified rectangle.
+
+   \param painter Painter
+   \param sliderRect Bounding rectangle of the slider
+*/
+void QwtSlider::drawSlider( 
+    QPainter *painter, const QRect &sliderRect ) const
+{
+    QRect innerRect( sliderRect );
+
+    if ( d_data->hasTrough )
+    {
+        const int bw = d_data->borderWidth;
+        innerRect = sliderRect.adjusted( bw, bw, -bw, -bw );
+
+        painter->fillRect( innerRect, palette().brush( QPalette::Mid ) );
+        qDrawShadePanel( painter, sliderRect, palette(), true, bw, NULL );
+    }
+
+    const QSize handleSize = qwtHandleSize( d_data->handleSize,
+        d_data->orientation, d_data->hasTrough );
+
+    if ( d_data->hasGroove )
+    {
+        const int slotExtent = 4;
+        const int slotMargin = 4;
+
+        QRect slotRect; 
+        if ( orientation() == Qt::Horizontal )
+        {
+            int slotOffset = qMax( 1, handleSize.width() / 2 - slotMargin );
+            int slotHeight = slotExtent + ( innerRect.height() % 2 );
+
+            slotRect.setWidth( innerRect.width() - 2 * slotOffset );
+            slotRect.setHeight( slotHeight );
+        }
+        else
+        {
+            int slotOffset = qMax( 1, handleSize.height() / 2 - slotMargin );
+            int slotWidth = slotExtent + ( innerRect.width() % 2 );
+
+            slotRect.setWidth( slotWidth );
+            slotRect.setHeight( innerRect.height() - 2 * slotOffset );
+
+        }
+
+        slotRect.moveCenter( innerRect.center() );
+
+        QBrush brush = palette().brush( QPalette::Dark );
+        qDrawShadePanel( painter, slotRect, palette(), true, 1 , &brush );
+    }
+
+    if ( isValid() )
+        drawHandle( painter, handleRect(), transform( value() ) );
+}
+
+/*!
+  Draw the thumb at a position
+
+  \param painter Painter
+  \param handleRect Bounding rectangle of the handle
+  \param pos Position of the handle marker in widget coordinates
+*/
+void QwtSlider::drawHandle( QPainter *painter, 
+    const QRect &handleRect, int pos ) const
+{
+    const int bw = d_data->borderWidth;
+
+    qDrawShadePanel( painter, 
+        handleRect, palette(), false, bw,
+        &palette().brush( QPalette::Button ) );
+
+    pos++; // shade line points one pixel below
+    if ( orientation() == Qt::Horizontal )
+    {
+        qDrawShadeLine( painter, pos, handleRect.top() + bw,
+            pos, handleRect.bottom() - bw, palette(), true, 1 );
+    }
+    else // Vertical
+    {
+        qDrawShadeLine( painter, handleRect.left() + bw, pos,
+            handleRect.right() - bw, pos, palette(), true, 1 );
+    }
+}
+
+/*!
+  \brief Determine what to do when the user presses a mouse button.
+
+  \param pos Mouse position
+
+  \retval True, when handleRect() contains pos 
+  \sa scrolledTo()
+*/
+bool QwtSlider::isScrollPosition( const QPoint &pos ) const
+{
+    if ( handleRect().contains( pos ) )
+    {
+        const double v = ( orientation() == Qt::Horizontal ) 
+            ? pos.x() : pos.y();
+
+        d_data->mouseOffset = v - transform( value() );
+        return true;
+    }
+
+    return false;
+}
+
+/*!
+  \brief Determine the value for a new position of the
+         slider handle.
+
+  \param pos Mouse position
+
+  \return Value for the mouse position
+  \sa isScrollPosition()
+*/
+double QwtSlider::scrolledTo( const QPoint &pos ) const
+{
+    int p = ( orientation() == Qt::Horizontal ) 
+        ? pos.x() : pos.y();
+
+    p -= d_data->mouseOffset;
+
+    int min = transform( lowerBound() );
+    int max = transform( upperBound() );
+    if ( min > max )
+        qSwap( min, max );
+
+    p = qBound( min, p, max );
+
+    return scaleMap().invTransform( p );
+}
+
+/*!
+   Mouse press event handler
+   \param event Mouse event
+*/
+void QwtSlider::mousePressEvent( QMouseEvent *event )
+{
+    if ( isReadOnly() )
+    {
+        event->ignore();
+        return;
+    }
+
+    const QPoint pos = event->pos();
+
+    if ( isValid() && d_data->sliderRect.contains( pos ) )
+    {
+        if ( !handleRect().contains( pos ) )
+        {
+            const int markerPos = transform( value() );
+
+            d_data->stepsIncrement = pageSteps();
+
+            if ( d_data->orientation == Qt::Horizontal )
+            {
+                if ( pos.x() < markerPos )
+                    d_data->stepsIncrement = -d_data->stepsIncrement;
+            }
+            else
+            {
+                if ( pos.y() < markerPos )
+                    d_data->stepsIncrement = -d_data->stepsIncrement;
+            }
+
+            if ( isInverted() )
+                d_data->stepsIncrement = -d_data->stepsIncrement;
+
+            const double v = value();
+            incrementValue( d_data->stepsIncrement );
+
+            if ( v != value() )
+            {
+                if ( isTracking() )
+                    Q_EMIT valueChanged( value() );
+                else
+                    d_data->pendingValueChange = true;
+
+                Q_EMIT sliderMoved( value() );
+            }
+
+            d_data->timerTick = false;
+            d_data->repeatTimerId = startTimer( qMax( 250, 2 * updateInterval() ) );
+
+            return;
+        }
+    }
+
+    QwtAbstractSlider::mousePressEvent( event );
+}
+
+/*!
+   Mouse release event handler
+   \param event Mouse event
+*/
+void QwtSlider::mouseReleaseEvent( QMouseEvent *event )
+{
+    if ( d_data->repeatTimerId > 0 )
+    {
+        killTimer( d_data->repeatTimerId );
+        d_data->repeatTimerId = 0;
+        d_data->timerTick = false;
+        d_data->stepsIncrement = 0;
+    }
+
+    if ( d_data->pendingValueChange )
+    {
+        d_data->pendingValueChange = false;
+        Q_EMIT valueChanged( value() );
+    }
+
+    QwtAbstractSlider::mouseReleaseEvent( event );
+}
+
+/*!
+   Timer event handler
+
+   Handles the timer, when the mouse stays pressed
+   inside the sliderRect().
+
+   \param event Mouse event
+*/  
+void QwtSlider::timerEvent( QTimerEvent *event )
+{
+    if ( event->timerId() != d_data->repeatTimerId )
+    {
+        QwtAbstractSlider::timerEvent( event );
+        return;
+    }
+
+    if ( !isValid() )
+    {
+        killTimer( d_data->repeatTimerId );
+        d_data->repeatTimerId = 0;
+        return;
+    }
+
+    const double v = value();
+    incrementValue( d_data->stepsIncrement );
+
+    if ( v != value() )
+    {
+        if ( isTracking() )
+            Q_EMIT valueChanged( value() );
+        else
+            d_data->pendingValueChange = true;
+
+        Q_EMIT sliderMoved( value() );
+    }
+
+    if ( !d_data->timerTick )
+    {
+        // restart the timer with a shorter interval
+        killTimer( d_data->repeatTimerId );
+        d_data->repeatTimerId = startTimer( updateInterval() );
+        
+        d_data->timerTick = true;
+    }   
+}
+
+/*!
+   Qt paint event handler
+   \param event Paint event
+*/
+void QwtSlider::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    if ( d_data->scalePosition != QwtSlider::NoScale )
+    {
+        if ( !d_data->sliderRect.contains( event->rect() ) )
+            scaleDraw()->draw( &painter, palette() );
+    }
+
+    drawSlider( &painter, d_data->sliderRect );
+
+    if ( hasFocus() )
+        QwtPainter::drawFocusRect( &painter, this, d_data->sliderRect );
+}
+
+/*!
+   Qt resize event handler
+   \param event Resize event
+*/
+void QwtSlider::resizeEvent( QResizeEvent *event )
+{
+    Q_UNUSED( event );
+
+    layoutSlider( false );
+}
+
+/*!
+   Handles QEvent::StyleChange and QEvent::FontChange events
+   \param event Change event
+*/
+void QwtSlider::changeEvent( QEvent *event )
+{
+    if ( event->type() == QEvent::StyleChange || 
+        event->type() == QEvent::FontChange )
+    {
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+
+    QwtAbstractSlider::changeEvent( event );
+}
+
+/*!
+  Recalculate the slider's geometry and layout based on
+  the current geometry and fonts.
+
+  \param update_geometry  notify the layout system and call update
+         to redraw the scale
+*/
+void QwtSlider::layoutSlider( bool update_geometry )
+{
+    int bw = 0;
+    if ( d_data->hasTrough )
+        bw = d_data->borderWidth;
+
+    const QSize handleSize = qwtHandleSize( d_data->handleSize,
+        d_data->orientation, d_data->hasTrough );
+
+    QRect sliderRect = contentsRect();
+
+    /*
+       The marker line of the handle needs to be aligned to
+       the scale. But the marker is in the center 
+       and we need space enough to display the rest of the handle.
+
+       But the scale itself usually needs margins for displaying
+       the tick labels, that also might needs space beyond the
+       backbone.
+
+       Now it depends on what needs more margins. If it is the
+       slider the scale gets shrunk, otherwise the slider.
+     */
+
+    int scaleMargin = 0;
+    if ( d_data->scalePosition != QwtSlider::NoScale )
+    {
+        int d1, d2;
+        scaleDraw()->getBorderDistHint( font(), d1, d2 );
+
+        scaleMargin = qMax( d1, d2 ) - bw;
+    }
+
+    int scaleX, scaleY, scaleLength;
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        const int handleMargin = handleSize.width() / 2 - 1;
+        if ( scaleMargin > handleMargin )
+        {
+            int off = scaleMargin - handleMargin;
+            sliderRect.adjust( off, 0, -off, 0 );
+        }
+
+        scaleX = sliderRect.left() + bw + handleSize.width() / 2 - 1;
+        scaleLength = sliderRect.width() - handleSize.width();
+    }
+    else
+    {
+        int handleMargin = handleSize.height() / 2 - 1;
+        if ( scaleMargin > handleMargin )
+        {
+            int off = scaleMargin - handleMargin;
+            sliderRect.adjust( 0, off, 0, -off );
+        }
+
+        scaleY = sliderRect.top() + bw + handleSize.height() / 2 - 1;
+        scaleLength = sliderRect.height() - handleSize.height();
+    }
+
+    scaleLength -= 2 * bw;
+
+    // now align slider and scale according to the ScalePosition
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        const int h = handleSize.height() + 2 * bw;
+
+        if ( d_data->scalePosition == QwtSlider::TrailingScale )
+        {
+            sliderRect.setTop( sliderRect.bottom() + 1 - h );
+            scaleY = sliderRect.top() - d_data->spacing;
+        }
+        else
+        {
+            sliderRect.setHeight( h );
+            scaleY = sliderRect.bottom() + 1 + d_data->spacing;
+        }
+    }
+    else // Qt::Vertical
+    {
+        const int w = handleSize.width() + 2 * bw;
+
+        if ( d_data->scalePosition == QwtSlider::LeadingScale )
+        {
+            sliderRect.setWidth( w );
+            scaleX = sliderRect.right() + 1 + d_data->spacing;
+        }
+        else
+        {
+            sliderRect.setLeft( sliderRect.right() + 1 - w );
+            scaleX = sliderRect.left() - d_data->spacing;
+        }
+    }
+
+    d_data->sliderRect = sliderRect;
+
+    scaleDraw()->move( scaleX, scaleY );
+    scaleDraw()->setLength( scaleLength );
+
+    if ( update_geometry )
+    {
+        d_data->sizeHintCache = QSize(); // invalidate
+        updateGeometry();
+        update();
+    }
+}
+
+/*!
+  En/Disable the trough
+
+  The slider can be cutomized by showing a trough for the
+  handle.
+
+  \param on When true, the groove is visible
+  \sa hasTrough(), setGroove()
+ */
+void QwtSlider::setTrough( bool on )
+{
+    if ( d_data->hasTrough != on )
+    {
+        d_data->hasTrough = on;
+
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return True, when the trough is visisble
+  \sa setTrough(), hasGroove()
+ */
+bool QwtSlider::hasTrough() const
+{
+    return d_data->hasTrough;
+}
+
+/*!
+  En/Disable the groove
+
+  The slider can be cutomized by showing a groove for the
+  handle.
+
+  \param on When true, the groove is visible
+  \sa hasGroove(), setThrough()
+ */
+void QwtSlider::setGroove( bool on )
+{
+    if ( d_data->hasGroove != on )
+    {
+        d_data->hasGroove = on;
+        
+        if ( testAttribute( Qt::WA_WState_Polished ) )
+            layoutSlider( true );
+    }
+}
+
+/*!
+  \return True, when the groove is visisble
+  \sa setGroove(), hasTrough()
+ */
+bool QwtSlider::hasGroove() const
+{
+    return d_data->hasGroove;
+} 
+
+/*!
+  \return minimumSizeHint()
+*/
+QSize QwtSlider::sizeHint() const
+{
+    const QSize hint = minimumSizeHint();
+    return hint.expandedTo( QApplication::globalStrut() );
+}
+
+/*!
+  \return Minimum size hint
+  \sa sizeHint()
+*/
+QSize QwtSlider::minimumSizeHint() const
+{
+    if ( !d_data->sizeHintCache.isEmpty() )
+        return d_data->sizeHintCache;
+
+    const QSize handleSize = qwtHandleSize( d_data->handleSize,
+        d_data->orientation, d_data->hasTrough );
+
+    int bw = 0;
+    if ( d_data->hasTrough )
+        bw = d_data->borderWidth;
+
+    int sliderLength = 0; 
+    int scaleExtent = 0;
+
+    if ( d_data->scalePosition != QwtSlider::NoScale )
+    {
+        int d1, d2;
+        scaleDraw()->getBorderDistHint( font(), d1, d2 );
+
+        const int scaleBorderDist = 2 * ( qMax( d1, d2 ) - bw );
+
+        int handleBorderDist;
+        if ( d_data->orientation == Qt::Horizontal )
+            handleBorderDist = handleSize.width();
+        else
+            handleBorderDist = handleSize.height();
+
+        sliderLength = scaleDraw()->minLength( font() );
+        if ( handleBorderDist > scaleBorderDist )
+        {
+            // We need additional space for the overlapping handle
+            sliderLength += handleBorderDist - scaleBorderDist;
+        }
+
+        scaleExtent += d_data->spacing;
+        scaleExtent += qCeil( scaleDraw()->extent( font() ) );
+    }
+
+    sliderLength = qMax( sliderLength, 84 ); // from QSlider
+
+    int w = 0;
+    int h = 0;
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        w = sliderLength;
+        h = handleSize.height() + 2 * bw + scaleExtent;
+    }
+    else
+    {
+        w = handleSize.width() + 2 * bw + scaleExtent;
+        h = sliderLength;
+    }
+
+    // finally add margins
+    int left, right, top, bottom;
+    getContentsMargins( &left, &top, &right, &bottom );
+
+    w += left + right;
+    h += top + bottom;
+
+    d_data->sizeHintCache = QSize( w, h );
+    return d_data->sizeHintCache;
+}
+
+/*!
+   \return Bounding rectangle of the slider handle
+ */
+QRect QwtSlider::handleRect() const
+{
+    if ( !isValid() )
+        return QRect();
+
+    const int markerPos = transform( value() );
+
+    QPoint center = d_data->sliderRect.center();
+    if ( d_data->orientation == Qt::Horizontal )
+        center.setX( markerPos );
+    else
+        center.setY( markerPos );
+
+    QRect rect;
+    rect.setSize( qwtHandleSize( d_data->handleSize,
+        d_data->orientation, d_data->hasTrough ) );
+    rect.moveCenter( center );
+
+    return rect;
+}
+
+/*!
+ \return Bounding rectangle of the slider - without the scale
+ */
+QRect QwtSlider::sliderRect() const
+{
+    return d_data->sliderRect;
+}
diff --git a/source/third_party/qwt/qwt_spline.cpp b/source/third_party/qwt/qwt_spline.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0ca92253d81dcf2c90075bf042879d7581e17d9d
--- /dev/null
+++ b/source/third_party/qwt/qwt_spline.cpp
@@ -0,0 +1,384 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_spline.h"
+#include "qwt/qwt_math.h"
+
+class QwtSpline::PrivateData
+{
+public:
+    PrivateData():
+        splineType( QwtSpline::Natural )
+    {
+    }
+
+    QwtSpline::SplineType splineType;
+
+    // coefficient vectors
+    QVector<double> a;
+    QVector<double> b;
+    QVector<double> c;
+
+    // control points
+    QPolygonF points;
+};
+
+static int lookup( double x, const QPolygonF &values )
+{
+#if 0
+//qLowerBound/qHigherBound ???
+#endif
+    int i1;
+    const int size = values.size();
+
+    if ( x <= values[0].x() )
+        i1 = 0;
+    else if ( x >= values[size - 2].x() )
+        i1 = size - 2;
+    else
+    {
+        i1 = 0;
+        int i2 = size - 2;
+        int i3 = 0;
+
+        while ( i2 - i1 > 1 )
+        {
+            i3 = i1 + ( ( i2 - i1 ) >> 1 );
+
+            if ( values[i3].x() > x )
+                i2 = i3;
+            else
+                i1 = i3;
+        }
+    }
+    return i1;
+}
+
+//! Constructor
+QwtSpline::QwtSpline()
+{
+    d_data = new PrivateData;
+}
+
+/*!
+   Copy constructor
+   \param other Spline used for initialization
+*/
+QwtSpline::QwtSpline( const QwtSpline& other )
+{
+    d_data = new PrivateData( *other.d_data );
+}
+
+/*!
+   Assignment operator
+   \param other Spline used for initialization
+   \return *this
+*/
+QwtSpline &QwtSpline::operator=( const QwtSpline & other )
+{
+    *d_data = *other.d_data;
+    return *this;
+}
+
+//! Destructor
+QwtSpline::~QwtSpline()
+{
+    delete d_data;
+}
+
+/*!
+   Select the algorithm used for calculating the spline
+
+   \param splineType Spline type
+   \sa splineType()
+*/
+void QwtSpline::setSplineType( SplineType splineType )
+{
+    d_data->splineType = splineType;
+}
+
+/*!
+   \return the spline type
+   \sa setSplineType()
+*/
+QwtSpline::SplineType QwtSpline::splineType() const
+{
+    return d_data->splineType;
+}
+
+/*!
+  \brief Calculate the spline coefficients
+
+  Depending on the value of \a periodic, this function
+  will determine the coefficients for a natural or a periodic
+  spline and store them internally.
+
+  \param points Points
+  \return true if successful
+  \warning The sequence of x (but not y) values has to be strictly monotone
+           increasing, which means <code>points[i].x() < points[i+1].x()</code>.
+       If this is not the case, the function will return false
+*/
+bool QwtSpline::setPoints( const QPolygonF& points )
+{
+    const int size = points.size();
+    if ( size <= 2 )
+    {
+        reset();
+        return false;
+    }
+
+    d_data->points = points;
+
+    d_data->a.resize( size - 1 );
+    d_data->b.resize( size - 1 );
+    d_data->c.resize( size - 1 );
+
+    bool ok;
+    if ( d_data->splineType == Periodic )
+        ok = buildPeriodicSpline( points );
+    else
+        ok = buildNaturalSpline( points );
+
+    if ( !ok )
+        reset();
+
+    return ok;
+}
+
+/*!
+   \return Points, that have been by setPoints()
+*/
+QPolygonF QwtSpline::points() const
+{
+    return d_data->points;
+}
+
+//! \return A coefficients
+const QVector<double> &QwtSpline::coefficientsA() const
+{
+    return d_data->a;
+}
+
+//! \return B coefficients
+const QVector<double> &QwtSpline::coefficientsB() const
+{
+    return d_data->b;
+}
+
+//! \return C coefficients
+const QVector<double> &QwtSpline::coefficientsC() const
+{
+    return d_data->c;
+}
+
+
+//! Free allocated memory and set size to 0
+void QwtSpline::reset()
+{
+    d_data->a.resize( 0 );
+    d_data->b.resize( 0 );
+    d_data->c.resize( 0 );
+    d_data->points.resize( 0 );
+}
+
+//! True if valid
+bool QwtSpline::isValid() const
+{
+    return d_data->a.size() > 0;
+}
+
+/*!
+  Calculate the interpolated function value corresponding
+  to a given argument x.
+
+  \param x Coordinate
+  \return Interpolated coordinate
+*/
+double QwtSpline::value( double x ) const
+{
+    if ( d_data->a.size() == 0 )
+        return 0.0;
+
+    const int i = lookup( x, d_data->points );
+
+    const double delta = x - d_data->points[i].x();
+    return( ( ( ( d_data->a[i] * delta ) + d_data->b[i] )
+        * delta + d_data->c[i] ) * delta + d_data->points[i].y() );
+}
+
+/*!
+  \brief Determines the coefficients for a natural spline
+  \return true if successful
+*/
+bool QwtSpline::buildNaturalSpline( const QPolygonF &points )
+{
+    int i;
+
+    const QPointF *p = points.data();
+    const int size = points.size();
+
+    double *a = d_data->a.data();
+    double *b = d_data->b.data();
+    double *c = d_data->c.data();
+
+    //  set up tridiagonal equation system; use coefficient
+    //  vectors as temporary buffers
+    QVector<double> h( size - 1 );
+    for ( i = 0; i < size - 1; i++ )
+    {
+        h[i] = p[i+1].x() - p[i].x();
+        if ( h[i] <= 0 )
+            return false;
+    }
+
+    QVector<double> d( size - 1 );
+    double dy1 = ( p[1].y() - p[0].y() ) / h[0];
+    for ( i = 1; i < size - 1; i++ )
+    {
+        b[i] = c[i] = h[i];
+        a[i] = 2.0 * ( h[i-1] + h[i] );
+
+        const double dy2 = ( p[i+1].y() - p[i].y() ) / h[i];
+        d[i] = 6.0 * ( dy1 - dy2 );
+        dy1 = dy2;
+    }
+
+    //
+    // solve it
+    //
+
+    // L-U Factorization
+    for ( i = 1; i < size - 2; i++ )
+    {
+        c[i] /= a[i];
+        a[i+1] -= b[i] * c[i];
+    }
+
+    // forward elimination
+    QVector<double> s( size );
+    s[1] = d[1];
+    for ( i = 2; i < size - 1; i++ )
+        s[i] = d[i] - c[i-1] * s[i-1];
+
+    // backward elimination
+    s[size - 2] = - s[size - 2] / a[size - 2];
+    for ( i = size - 3; i > 0; i-- )
+        s[i] = - ( s[i] + b[i] * s[i+1] ) / a[i];
+    s[size - 1] = s[0] = 0.0;
+
+    //
+    // Finally, determine the spline coefficients
+    //
+    for ( i = 0; i < size - 1; i++ )
+    {
+        a[i] = ( s[i+1] - s[i] ) / ( 6.0 * h[i] );
+        b[i] = 0.5 * s[i];
+        c[i] = ( p[i+1].y() - p[i].y() ) / h[i]
+            - ( s[i+1] + 2.0 * s[i] ) * h[i] / 6.0;
+    }
+
+    return true;
+}
+
+/*!
+  \brief Determines the coefficients for a periodic spline
+  \return true if successful
+*/
+bool QwtSpline::buildPeriodicSpline( const QPolygonF &points )
+{
+    int i;
+
+    const QPointF *p = points.data();
+    const int size = points.size();
+
+    double *a = d_data->a.data();
+    double *b = d_data->b.data();
+    double *c = d_data->c.data();
+
+    QVector<double> d( size - 1 );
+    QVector<double> h( size - 1 );
+    QVector<double> s( size );
+
+    //
+    //  setup equation system; use coefficient
+    //  vectors as temporary buffers
+    //
+    for ( i = 0; i < size - 1; i++ )
+    {
+        h[i] = p[i+1].x() - p[i].x();
+        if ( h[i] <= 0.0 )
+            return false;
+    }
+
+    const int imax = size - 2;
+    double htmp = h[imax];
+    double dy1 = ( p[0].y() - p[imax].y() ) / htmp;
+    for ( i = 0; i <= imax; i++ )
+    {
+        b[i] = c[i] = h[i];
+        a[i] = 2.0 * ( htmp + h[i] );
+        const double dy2 = ( p[i+1].y() - p[i].y() ) / h[i];
+        d[i] = 6.0 * ( dy1 - dy2 );
+        dy1 = dy2;
+        htmp = h[i];
+    }
+
+    //
+    // solve it
+    //
+
+    // L-U Factorization
+    a[0] = qSqrt( a[0] );
+    c[0] = h[imax] / a[0];
+    double sum = 0;
+
+    for ( i = 0; i < imax - 1; i++ )
+    {
+        b[i] /= a[i];
+        if ( i > 0 )
+            c[i] = - c[i-1] * b[i-1] / a[i];
+        a[i+1] = qSqrt( a[i+1] - qwtSqr( b[i] ) );
+        sum += qwtSqr( c[i] );
+    }
+    b[imax-1] = ( b[imax-1] - c[imax-2] * b[imax-2] ) / a[imax-1];
+    a[imax] = qSqrt( a[imax] - qwtSqr( b[imax-1] ) - sum );
+
+
+    // forward elimination
+    s[0] = d[0] / a[0];
+    sum = 0;
+    for ( i = 1; i < imax; i++ )
+    {
+        s[i] = ( d[i] - b[i-1] * s[i-1] ) / a[i];
+        sum += c[i-1] * s[i-1];
+    }
+    s[imax] = ( d[imax] - b[imax-1] * s[imax-1] - sum ) / a[imax];
+
+
+    // backward elimination
+    s[imax] = - s[imax] / a[imax];
+    s[imax-1] = -( s[imax-1] + b[imax-1] * s[imax] ) / a[imax-1];
+    for ( i = imax - 2; i >= 0; i-- )
+        s[i] = - ( s[i] + b[i] * s[i+1] + c[i] * s[imax] ) / a[i];
+
+    //
+    // Finally, determine the spline coefficients
+    //
+    s[size-1] = s[0];
+    for ( i = 0; i < size - 1; i++ )
+    {
+        a[i] = ( s[i+1] - s[i] ) / ( 6.0 * h[i] );
+        b[i] = 0.5 * s[i];
+        c[i] = ( p[i+1].y() - p[i].y() )
+            / h[i] - ( s[i+1] + 2.0 * s[i] ) * h[i] / 6.0;
+    }
+
+    return true;
+}
diff --git a/source/third_party/qwt/qwt_symbol.cpp b/source/third_party/qwt/qwt_symbol.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eae8b0538898fe1cf3189f340aae7a54a20dd27c
--- /dev/null
+++ b/source/third_party/qwt/qwt_symbol.cpp
@@ -0,0 +1,1770 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_symbol.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_graphic.h"
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qpainterpath.h>
+#include <qpixmap.h>
+#include <qpaintengine.h>
+#include <qmath.h>
+#ifndef QWT_NO_SVG
+#include <QtSvg/qsvgrenderer.h>
+#endif
+
+namespace QwtTriangle
+{
+    enum Type
+    {
+        Left,
+        Right,
+        Up,
+        Down
+    };
+}
+
+static QwtGraphic qwtPathGraphic( const QPainterPath &path, 
+    const QPen &pen, const QBrush& brush )
+{
+    QwtGraphic graphic;
+    graphic.setRenderHint( QwtGraphic::RenderPensUnscaled );
+
+    QPainter painter( &graphic );
+    painter.setPen( pen );
+    painter.setBrush( brush );
+    painter.drawPath( path );
+    painter.end();
+
+    return graphic;
+}
+
+static inline QRectF qwtScaledBoundingRect( 
+    const QwtGraphic &graphic, const QSizeF size )
+{
+    QSizeF scaledSize = size;
+    if ( scaledSize.isEmpty() )
+        scaledSize = graphic.defaultSize();
+        
+    const QSizeF sz = graphic.controlPointRect().size();
+
+    double sx = 1.0;
+    if ( sz.width() > 0.0 )
+        sx = scaledSize.width() / sz.width();
+    
+    double sy = 1.0;
+    if ( sz.height() > 0.0 )
+        sy = scaledSize.height() / sz.height();
+
+    return graphic.scaledBoundingRect( sx, sy );
+}
+
+static inline void qwtDrawPixmapSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    QSize size = symbol.size();
+    if ( size.isEmpty() )
+        size = symbol.pixmap().size();
+
+    const QTransform transform = painter->transform();
+    if ( transform.isScaling() )
+    {
+        const QRect r( 0, 0, size.width(), size.height() );
+        size = transform.mapRect( r ).size();
+    }
+
+    QPixmap pm = symbol.pixmap();
+    if ( pm.size() != size )
+        pm = pm.scaled( size );
+    
+    QPointF pinPoint( 0.5 * size.width(), 0.5 * size.height() );
+    if ( symbol.isPinPointEnabled() )
+        pinPoint = symbol.pinPoint();
+
+    painter->resetTransform();
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        const QPointF pos = transform.map( points[i] ) - pinPoint;
+
+        QwtPainter::drawPixmap( painter, 
+            QRect( pos.toPoint(), pm.size() ), pm );
+    }
+}
+
+#ifndef QWT_NO_SVG
+
+static inline void qwtDrawSvgSymbols( QPainter *painter, 
+    const QPointF *points, int numPoints, 
+    QSvgRenderer *renderer, const QwtSymbol &symbol )
+{
+    if ( renderer == NULL || !renderer->isValid() )
+        return;
+
+    const QRectF viewBox = renderer->viewBoxF();
+    if ( viewBox.isEmpty() )
+        return;
+
+    QSizeF sz = symbol.size();
+    if ( !sz.isValid() )
+        sz = viewBox.size();
+
+    const double sx = sz.width() / viewBox.width();
+    const double sy = sz.height() / viewBox.height();
+
+    QPointF pinPoint = viewBox.center();
+    if ( symbol.isPinPointEnabled() )
+        pinPoint = symbol.pinPoint();
+
+    const double dx = sx * ( pinPoint.x() - viewBox.left() );
+    const double dy = sy * ( pinPoint.y() - viewBox.top() );
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        const double x = points[i].x() - dx;
+        const double y = points[i].y() - dy;
+
+        renderer->render( painter, 
+            QRectF( x, y, sz.width(), sz.height() ) );
+    }
+}
+
+#endif
+
+static inline void qwtDrawGraphicSymbols( QPainter *painter, 
+    const QPointF *points, int numPoints, const QwtGraphic &graphic,
+    const QwtSymbol &symbol )
+{
+    const QRectF pointRect = graphic.controlPointRect();
+    if ( pointRect.isEmpty() )
+        return;
+
+    double sx = 1.0;
+    double sy = 1.0;
+
+    const QSize sz = symbol.size();
+    if ( sz.isValid() )
+    {
+        sx = sz.width() / pointRect.width();
+        sy = sz.height() / pointRect.height();
+    }
+
+    QPointF pinPoint = pointRect.center();
+    if ( symbol.isPinPointEnabled() )
+        pinPoint = symbol.pinPoint();
+
+    const QTransform transform = painter->transform();
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        QTransform tr = transform;
+        tr.translate( points[i].x(), points[i].y() );
+        tr.scale( sx, sy );
+        tr.translate( -pinPoint.x(), -pinPoint.y() );
+
+        painter->setTransform( tr );
+
+        graphic.render( painter );
+    }
+
+    painter->setTransform( transform );
+}
+
+static inline void qwtDrawEllipseSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    painter->setBrush( symbol.brush() );
+    painter->setPen( symbol.pen() );
+
+    const QSize size = symbol.size();
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        const int sw = size.width();
+        const int sh = size.height();
+        const int sw2 = size.width() / 2;
+        const int sh2 = size.height() / 2;
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const int x = qRound( points[i].x() );
+            const int y = qRound( points[i].y() );
+
+            const QRectF r( x - sw2, y - sh2, sw, sh );
+            QwtPainter::drawEllipse( painter, r );
+        }
+    }
+    else
+    {
+        const double sw = size.width();
+        const double sh = size.height();
+        const double sw2 = 0.5 * size.width();
+        const double sh2 = 0.5 * size.height();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const double x = points[i].x();
+            const double y = points[i].y();
+
+            const QRectF r( x - sw2, y - sh2, sw, sh );
+            QwtPainter::drawEllipse( painter, r );
+        }
+    }
+}
+
+static inline void qwtDrawRectSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+
+    QPen pen = symbol.pen();
+    pen.setJoinStyle( Qt::MiterJoin );
+    painter->setPen( pen );
+    painter->setBrush( symbol.brush() );
+    painter->setRenderHint( QPainter::Antialiasing, false );
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        const int sw = size.width();
+        const int sh = size.height();
+        const int sw2 = size.width() / 2;
+        const int sh2 = size.height() / 2;
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const int x = qRound( points[i].x() );
+            const int y = qRound( points[i].y() );
+
+            const QRect r( x - sw2, y - sh2, sw, sh );
+            QwtPainter::drawRect( painter, r );
+        }
+    }
+    else
+    {
+        const double sw = size.width();
+        const double sh = size.height();
+        const double sw2 = 0.5 * size.width();
+        const double sh2 = 0.5 * size.height();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const double x = points[i].x();
+            const double y = points[i].y();
+
+            const QRectF r( x - sw2, y - sh2, sw, sh );
+            QwtPainter::drawRect( painter, r );
+        }
+    }
+}
+
+static inline void qwtDrawDiamondSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+
+    QPen pen = symbol.pen();
+    pen.setJoinStyle( Qt::MiterJoin );
+    painter->setPen( pen );
+    painter->setBrush( symbol.brush() );
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const int x = qRound( points[i].x() );
+            const int y = qRound( points[i].y() );
+
+            const int x1 = x - size.width() / 2;
+            const int y1 = y - size.height() / 2;
+            const int x2 = x1 + size.width();
+            const int y2 = y1 + size.height();
+
+            QPolygonF polygon;
+            polygon += QPointF( x, y1 );
+            polygon += QPointF( x1, y );
+            polygon += QPointF( x, y2 );
+            polygon += QPointF( x2, y );
+
+            QwtPainter::drawPolygon( painter, polygon );
+        }
+    }
+    else
+    {
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const QPointF &pos = points[i];
+
+            const double x1 = pos.x() - 0.5 * size.width();
+            const double y1 = pos.y() - 0.5 * size.height();
+            const double x2 = x1 + size.width();
+            const double y2 = y1 + size.height();
+
+            QPolygonF polygon;
+            polygon += QPointF( pos.x(), y1 );
+            polygon += QPointF( x2, pos.y() );
+            polygon += QPointF( pos.x(), y2 );
+            polygon += QPointF( x1, pos.y() );
+
+            QwtPainter::drawPolygon( painter, polygon );
+        }
+    }
+}
+
+static inline void qwtDrawTriangleSymbols(
+    QPainter *painter, QwtTriangle::Type type,
+    const QPointF *points, int numPoints,
+    const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+
+    QPen pen = symbol.pen();
+    pen.setJoinStyle( Qt::MiterJoin );
+    painter->setPen( pen );
+
+    painter->setBrush( symbol.brush() );
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    double sw2 = 0.5 * size.width();
+    double sh2 = 0.5 * size.height();
+
+    if ( doAlign )
+    {
+        sw2 = qFloor( sw2 );
+        sh2 = qFloor( sh2 );
+    }
+
+    QPolygonF triangle( 3 );
+    QPointF *trianglePoints = triangle.data();
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        const QPointF &pos = points[i];
+
+        double x = pos.x();
+        double y = pos.y();
+
+        if ( doAlign )
+        {
+            x = qRound( x );
+            y = qRound( y );
+        }
+
+        const double x1 = x - sw2;
+        const double x2 = x1 + size.width();
+        const double y1 = y - sh2;
+        const double y2 = y1 + size.height();
+
+        switch ( type )
+        {
+            case QwtTriangle::Left:
+            {
+                trianglePoints[0].rx() = x2;
+                trianglePoints[0].ry() = y1;
+
+                trianglePoints[1].rx() = x1;
+                trianglePoints[1].ry() = y;
+
+                trianglePoints[2].rx() = x2;
+                trianglePoints[2].ry() = y2;
+
+                break;
+            }
+            case QwtTriangle::Right:
+            {
+                trianglePoints[0].rx() = x1;
+                trianglePoints[0].ry() = y1;
+
+                trianglePoints[1].rx() = x2;
+                trianglePoints[1].ry() = y;
+
+                trianglePoints[2].rx() = x1;
+                trianglePoints[2].ry() = y2;
+
+                break;
+            }
+            case QwtTriangle::Up:
+            {
+                trianglePoints[0].rx() = x1;
+                trianglePoints[0].ry() = y2;
+
+                trianglePoints[1].rx() = x;
+                trianglePoints[1].ry() = y1;
+
+                trianglePoints[2].rx() = x2;
+                trianglePoints[2].ry() = y2;
+
+                break;
+            }
+            case QwtTriangle::Down:
+            {
+                trianglePoints[0].rx() = x1;
+                trianglePoints[0].ry() = y1;
+
+                trianglePoints[1].rx() = x;
+                trianglePoints[1].ry() = y2;
+
+                trianglePoints[2].rx() = x2;
+                trianglePoints[2].ry() = y1;
+
+                break;
+            }
+        }
+        QwtPainter::drawPolygon( painter, triangle );
+    }
+}
+
+static inline void qwtDrawLineSymbols(
+    QPainter *painter, int orientations,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+
+    int off = 0;
+
+    QPen pen = symbol.pen();
+    if ( pen.width() > 1 )
+    {
+        pen.setCapStyle( Qt::FlatCap );
+        off = 1;
+    }
+
+    painter->setPen( pen );
+    painter->setRenderHint( QPainter::Antialiasing, false );
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        const int sw = qFloor( size.width() );
+        const int sh = qFloor( size.height() );
+        const int sw2 = size.width() / 2;
+        const int sh2 = size.height() / 2;
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            if ( orientations & Qt::Horizontal )
+            {
+                const int x = qRound( points[i].x() ) - sw2;
+                const int y = qRound( points[i].y() );
+
+                QwtPainter::drawLine( painter, x, y, x + sw + off, y );
+            }
+            if ( orientations & Qt::Vertical )
+            {
+                const int x = qRound( points[i].x() );
+                const int y = qRound( points[i].y() ) - sh2;
+
+                QwtPainter::drawLine( painter, x, y, x, y + sh + off );
+            }
+        }
+    }
+    else
+    {
+        const double sw = size.width();
+        const double sh = size.height();
+        const double sw2 = 0.5 * size.width();
+        const double sh2 = 0.5 * size.height();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            if ( orientations & Qt::Horizontal )
+            {
+                const double x = points[i].x() - sw2;
+                const double y = points[i].y();
+
+                QwtPainter::drawLine( painter, x, y, x + sw, y );
+            }
+            if ( orientations & Qt::Vertical )
+            {
+                const double y = points[i].y() - sh2;
+                const double x = points[i].x();
+
+                QwtPainter::drawLine( painter, x, y, x, y + sh );
+            }
+        }
+    }
+}
+
+static inline void qwtDrawXCrossSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+    int off = 0;
+
+    QPen pen = symbol.pen();
+    if ( pen.width() > 1 )
+    {
+        pen.setCapStyle( Qt::FlatCap );
+        off = 1;
+    }
+    painter->setPen( pen );
+
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        const int sw = size.width();
+        const int sh = size.height();
+        const int sw2 = size.width() / 2;
+        const int sh2 = size.height() / 2;
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const QPointF &pos = points[i];
+
+            const int x = qRound( pos.x() );
+            const int y = qRound( pos.y() );
+
+            const int x1 = x - sw2;
+            const int x2 = x1 + sw + off;
+            const int y1 = y - sh2;
+            const int y2 = y1 + sh + off;
+
+            QwtPainter::drawLine( painter, x1, y1, x2, y2 );
+            QwtPainter::drawLine( painter, x2, y1, x1, y2 );
+        }
+    }
+    else
+    {
+        const double sw = size.width();
+        const double sh = size.height();
+        const double sw2 = 0.5 * size.width();
+        const double sh2 = 0.5 * size.height();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const QPointF &pos = points[i];
+
+            const double x1 = pos.x() - sw2;
+            const double x2 = x1 + sw;
+            const double y1 = pos.y() - sh2;
+            const double y2 = y1 + sh;
+
+            QwtPainter::drawLine( painter, x1, y1, x2, y2 );
+            QwtPainter::drawLine( painter, x1, y2, x2, y1 );
+        }
+    }
+}
+
+static inline void qwtDrawStar1Symbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    const QSize size = symbol.size();
+    painter->setPen( symbol.pen() );
+
+    if ( QwtPainter::roundingAlignment( painter ) )
+    {
+        QRect r( 0, 0, size.width(), size.height() );
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            r.moveCenter( points[i].toPoint() );
+
+            const double sqrt1_2 = 0.70710678118654752440; /* 1/sqrt(2) */
+
+            const double d1 = r.width() / 2.0 * ( 1.0 - sqrt1_2 );
+
+            QwtPainter::drawLine( painter,
+                qRound( r.left() + d1 ), qRound( r.top() + d1 ),
+                qRound( r.right() - d1 ), qRound( r.bottom() - d1 ) );
+            QwtPainter::drawLine( painter,
+                qRound( r.left() + d1 ), qRound( r.bottom() - d1 ),
+                qRound( r .right() - d1), qRound( r.top() + d1 ) );
+
+            const QPoint c = r.center();
+
+            QwtPainter::drawLine( painter,
+                c.x(), r.top(), c.x(), r.bottom() );
+            QwtPainter::drawLine( painter,
+                r.left(), c.y(), r.right(), c.y() );
+        }
+    }
+    else
+    {
+        QRectF r( 0, 0, size.width(), size.height() );
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            r.moveCenter( points[i] );
+
+            const double sqrt1_2 = 0.70710678118654752440; /* 1/sqrt(2) */
+
+            const QPointF c = r.center();
+            const double d1  = r.width() / 2.0 * ( 1.0 - sqrt1_2 );
+
+            QwtPainter::drawLine( painter,
+                r.left() + d1, r.top() + d1,
+                r.right() - d1, r.bottom() - d1 );
+            QwtPainter::drawLine( painter,
+                r.left() + d1, r.bottom() - d1,
+                r.right() - d1, r.top() + d1 );
+            QwtPainter::drawLine( painter,
+                c.x(), r.top(),
+                c.x(), r.bottom() );
+            QwtPainter::drawLine( painter,
+                r.left(), c.y(),
+                r.right(), c.y() );
+        }
+    }
+}
+
+static inline void qwtDrawStar2Symbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    QPen pen = symbol.pen();
+    if ( pen.width() > 1 )
+        pen.setCapStyle( Qt::FlatCap );
+    pen.setJoinStyle( Qt::MiterJoin );
+    painter->setPen( pen );
+
+    painter->setBrush( symbol.brush() );
+
+    const double cos30 = 0.866025; // cos(30°)
+
+    const double dy = 0.25 * symbol.size().height();
+    const double dx = 0.5 * symbol.size().width() * cos30 / 3.0;
+
+    QPolygonF star( 12 );
+    QPointF *starPoints = star.data();
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        double x = points[i].x();
+        double y = points[i].y();
+        if ( doAlign )
+        {
+            x = qRound( x );
+            y = qRound( y );
+        }
+
+        double x1 = x - 3 * dx;
+        double y1 = y - 2 * dy;
+        if ( doAlign )
+        {
+            x1 = qRound( x - 3 * dx );
+            y1 = qRound( y - 2 * dy );
+        }
+
+        const double x2 = x1 + 1 * dx;
+        const double x3 = x1 + 2 * dx;
+        const double x4 = x1 + 3 * dx;
+        const double x5 = x1 + 4 * dx;
+        const double x6 = x1 + 5 * dx;
+        const double x7 = x1 + 6 * dx;
+
+        const double y2 = y1 + 1 * dy;
+        const double y3 = y1 + 2 * dy;
+        const double y4 = y1 + 3 * dy;
+        const double y5 = y1 + 4 * dy;
+
+        starPoints[0].rx() = x4;
+        starPoints[0].ry() = y1;
+
+        starPoints[1].rx() = x5;
+        starPoints[1].ry() = y2;
+
+        starPoints[2].rx() = x7;
+        starPoints[2].ry() = y2;
+
+        starPoints[3].rx() = x6;
+        starPoints[3].ry() = y3;
+
+        starPoints[4].rx() = x7;
+        starPoints[4].ry() = y4;
+
+        starPoints[5].rx() = x5;
+        starPoints[5].ry() = y4;
+
+        starPoints[6].rx() = x4;
+        starPoints[6].ry() = y5;
+
+        starPoints[7].rx() = x3;
+        starPoints[7].ry() = y4;
+
+        starPoints[8].rx() = x1;
+        starPoints[8].ry() = y4;
+
+        starPoints[9].rx() = x2;
+        starPoints[9].ry() = y3;
+
+        starPoints[10].rx() = x1;
+        starPoints[10].ry() = y2;
+
+        starPoints[11].rx() = x3;
+        starPoints[11].ry() = y2;
+
+        QwtPainter::drawPolygon( painter, star );
+    }
+}
+
+static inline void qwtDrawHexagonSymbols( QPainter *painter,
+    const QPointF *points, int numPoints, const QwtSymbol &symbol )
+{
+    painter->setBrush( symbol.brush() );
+    painter->setPen( symbol.pen() );
+
+    const double cos30 = 0.866025; // cos(30°)
+    const double dx = 0.5 * ( symbol.size().width() - cos30 );
+
+    const double dy = 0.25 * symbol.size().height();
+
+    QPolygonF hexaPolygon( 6 );
+    QPointF *hexaPoints = hexaPolygon.data();
+
+    const bool doAlign = QwtPainter::roundingAlignment( painter );
+
+    for ( int i = 0; i < numPoints; i++ )
+    {
+        double x = points[i].x();
+        double y = points[i].y();
+        if ( doAlign )
+        {
+            x = qRound( x );
+            y = qRound( y );
+        }
+
+        double x1 = x - dx;
+        double y1 = y - 2 * dy;
+        if ( doAlign )
+        {
+            x1 = qCeil( x1 );
+            y1 = qCeil( y1 );
+        }
+
+        const double x2 = x1 + 1 * dx;
+        const double x3 = x1 + 2 * dx;
+
+        const double y2 = y1 + 1 * dy;
+        const double y3 = y1 + 3 * dy;
+        const double y4 = y1 + 4 * dy;
+
+        hexaPoints[0].rx() = x2;
+        hexaPoints[0].ry() = y1;
+
+        hexaPoints[1].rx() = x3;
+        hexaPoints[1].ry() = y2;
+
+        hexaPoints[2].rx() = x3;
+        hexaPoints[2].ry() = y3;
+
+        hexaPoints[3].rx() = x2;
+        hexaPoints[3].ry() = y4;
+
+        hexaPoints[4].rx() = x1;
+        hexaPoints[4].ry() = y3;
+
+        hexaPoints[5].rx() = x1;
+        hexaPoints[5].ry() = y2;
+
+        QwtPainter::drawPolygon( painter, hexaPolygon );
+    }
+}
+
+class QwtSymbol::PrivateData
+{
+public:
+    PrivateData( QwtSymbol::Style st, const QBrush &br,
+            const QPen &pn, const QSize &sz ):
+        style( st ),
+        size( sz ),
+        brush( br ),
+        pen( pn ),
+        isPinPointEnabled( false )
+    {
+        cache.policy = QwtSymbol::AutoCache;
+#ifndef QWT_NO_SVG
+        svg.renderer = NULL;
+#endif
+    }
+
+    ~PrivateData()
+    {
+#ifndef QWT_NO_SVG
+        delete svg.renderer;
+#endif
+    }
+
+    Style style;
+    QSize size;
+    QBrush brush;
+    QPen pen;
+
+    bool isPinPointEnabled;
+    QPointF pinPoint;
+
+    struct Path
+    {
+        QPainterPath path;
+        QwtGraphic graphic;
+
+    } path;
+
+    struct Pixmap
+    {
+        QPixmap pixmap;
+
+    } pixmap;
+
+    struct Graphic
+    {
+        QwtGraphic graphic;
+
+    } graphic;
+
+#ifndef QWT_NO_SVG
+    struct SVG
+    {
+        QSvgRenderer *renderer;
+    } svg;
+#endif
+
+    struct PaintCache
+    {
+        QwtSymbol::CachePolicy policy;
+        QPixmap pixmap;
+
+    } cache;
+};
+
+/*!
+  Default Constructor
+  \param style Symbol Style
+
+  The symbol is constructed with gray interior,
+  black outline with zero width, no size and style 'NoSymbol'.
+*/
+QwtSymbol::QwtSymbol( Style style )
+{
+    d_data = new PrivateData( style, QBrush( Qt::gray ),
+        QPen( Qt::black, 0 ), QSize() );
+}
+
+/*!
+  \brief Constructor
+  \param style Symbol Style
+  \param brush brush to fill the interior
+  \param pen outline pen
+  \param size size
+
+  \sa setStyle(), setBrush(), setPen(), setSize()
+*/
+QwtSymbol::QwtSymbol( QwtSymbol::Style style, const QBrush &brush,
+    const QPen &pen, const QSize &size )
+{
+    d_data = new PrivateData( style, brush, pen, size );
+}
+
+/*!
+  \brief Constructor
+
+  The symbol gets initialized by a painter path. The style is
+  set to QwtSymbol::Path, the size is set to empty ( the path
+  is displayed unscaled ).
+
+  \param path painter path
+  \param brush brush to fill the interior
+  \param pen outline pen
+
+  \sa setPath(), setBrush(), setPen(), setSize()
+*/
+
+QwtSymbol::QwtSymbol( const QPainterPath &path, 
+    const QBrush &brush, const QPen &pen )
+{
+    d_data = new PrivateData( QwtSymbol::Path, brush, pen, QSize() );
+    setPath( path );
+}
+
+//! Destructor
+QwtSymbol::~QwtSymbol()
+{
+    delete d_data;
+}
+
+/*!
+  Change the cache policy
+
+  The default policy is AutoCache
+
+  \param policy Cache policy
+  \sa CachePolicy, cachePolicy()
+*/
+void QwtSymbol::setCachePolicy(
+    QwtSymbol::CachePolicy policy )
+{
+    if ( d_data->cache.policy != policy )
+    {
+        d_data->cache.policy = policy;
+        invalidateCache();
+    }
+}
+
+/*!
+  \return Cache policy
+  \sa CachePolicy, setCachePolicy()
+*/
+QwtSymbol::CachePolicy QwtSymbol::cachePolicy() const
+{
+    return d_data->cache.policy;
+}
+
+/*!
+  \brief Set a painter path as symbol
+
+  The symbol is represented by a painter path, where the 
+  origin ( 0, 0 ) of the path coordinate system is mapped to
+  the position of the symbol.
+
+  When the symbol has valid size the painter path gets scaled
+  to fit into the size. Otherwise the symbol size depends on
+  the bounding rectangle of the path.
+
+  The following code defines a symbol drawing an arrow:
+
+  \verbatim
+#include <qwt_symbol.h>
+
+QwtSymbol *symbol = new QwtSymbol();
+
+QPen pen( Qt::black, 2 );
+pen.setJoinStyle( Qt::MiterJoin );
+
+symbol->setPen( pen );
+symbol->setBrush( Qt::red );
+
+QPainterPath path;
+path.moveTo( 0, 8 );
+path.lineTo( 0, 5 );
+path.lineTo( -3, 5 );
+path.lineTo( 0, 0 );
+path.lineTo( 3, 5 );
+path.lineTo( 0, 5 );
+
+QTransform transform;
+transform.rotate( -30.0 );
+path = transform.map( path );
+
+symbol->setPath( path );
+symbol->setPinPoint( QPointF( 0.0, 0.0 ) );
+
+setSize( 10, 14 );
+\endverbatim
+
+  \param path Painter path
+
+  \note The style is implicitely set to QwtSymbol::Path.
+  \sa path(), setSize()
+ */
+void QwtSymbol::setPath( const QPainterPath &path )
+{
+    d_data->style = QwtSymbol::Path;
+    d_data->path.path = path;
+    d_data->path.graphic.reset();
+}
+
+/*!
+   \return Painter path for displaying the symbol
+   \sa setPath()
+*/
+const QPainterPath &QwtSymbol::path() const
+{
+    return d_data->path.path;
+}
+
+/*!
+  Set a pixmap as symbol
+
+  \param pixmap Pixmap
+
+  \sa pixmap(), setGraphic()
+
+  \note the style() is set to QwtSymbol::Pixmap
+  \note brush() and pen() have no effect
+ */
+void QwtSymbol::setPixmap( const QPixmap &pixmap )
+{
+    d_data->style = QwtSymbol::Pixmap;
+    d_data->pixmap.pixmap = pixmap;
+}
+
+/*!
+  \return Assigned pixmap
+  \sa setPixmap()
+ */
+const QPixmap &QwtSymbol::pixmap() const
+{
+    return d_data->pixmap.pixmap;
+}
+
+/*!
+  Set a graphic as symbol
+
+  \param graphic Graphic
+
+  \sa graphic(), setPixmap()
+
+  \note the style() is set to QwtSymbol::Graphic
+  \note brush() and pen() have no effect
+ */
+void QwtSymbol::setGraphic( const QwtGraphic &graphic )
+{
+    d_data->style = QwtSymbol::Graphic;
+    d_data->graphic.graphic = graphic;
+}
+
+/*!
+  \return Assigned graphic
+  \sa setGraphic()
+ */
+const QwtGraphic &QwtSymbol::graphic() const
+{
+    return d_data->graphic.graphic;
+}
+
+#ifndef QWT_NO_SVG
+
+/*!
+  Set a SVG icon as symbol
+
+  \param svgDocument SVG icon
+
+  \sa setGraphic(), setPixmap()
+
+  \note the style() is set to QwtSymbol::SvgDocument
+  \note brush() and pen() have no effect
+ */
+void QwtSymbol::setSvgDocument( const QByteArray &svgDocument )
+{
+    d_data->style = QwtSymbol::SvgDocument;
+    if ( d_data->svg.renderer == NULL )
+        d_data->svg.renderer = new QSvgRenderer();
+
+    d_data->svg.renderer->load( svgDocument );
+}
+
+#endif
+
+/*!
+  \brief Specify the symbol's size
+
+  If the 'h' parameter is left out or less than 0,
+  and the 'w' parameter is greater than or equal to 0,
+  the symbol size will be set to (w,w).
+
+  \param width Width
+  \param height Height (defaults to -1)
+
+  \sa size()
+*/
+void QwtSymbol::setSize( int width, int height )
+{
+    if ( ( width >= 0 ) && ( height < 0 ) )
+        height = width;
+
+    setSize( QSize( width, height ) );
+}
+
+/*!
+   Set the symbol's size
+   \param size Size
+
+   \sa size()
+*/
+void QwtSymbol::setSize( const QSize &size )
+{
+    if ( size.isValid() && size != d_data->size )
+    {
+        d_data->size = size;
+        invalidateCache();
+    }
+}
+
+/*!
+   \return Size
+   \sa setSize()
+*/
+const QSize& QwtSymbol::size() const
+{
+    return d_data->size;
+}
+
+/*!
+  \brief Assign a brush
+
+  The brush is used to draw the interior of the symbol.
+  \param brush Brush
+
+  \sa brush()
+*/
+void QwtSymbol::setBrush( const QBrush &brush )
+{
+    if ( brush != d_data->brush )
+    {
+        d_data->brush = brush;
+        invalidateCache();
+
+        if ( d_data->style == QwtSymbol::Path )
+            d_data->path.graphic.reset();
+    }
+}
+
+/*!
+  \return Brush
+  \sa setBrush()
+*/
+const QBrush& QwtSymbol::brush() const
+{
+    return d_data->brush;
+}
+
+/*!
+  Build and assign a pen
+
+  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 )
+  what makes it non cosmetic ( see QPen::isCosmetic() ).
+  This method has been introduced to hide this incompatibility.
+
+  \param color Pen color
+  \param width Pen width
+  \param style Pen style
+
+  \sa pen(), brush()
+ */
+void QwtSymbol::setPen( const QColor &color,
+    qreal width, Qt::PenStyle style )
+{
+    setPen( QPen( color, width, style ) );
+}
+
+/*!
+  Assign a pen
+
+  The pen is used to draw the symbol's outline.
+
+  \param pen Pen
+  \sa pen(), setBrush()
+*/
+void QwtSymbol::setPen( const QPen &pen )
+{
+    if ( pen != d_data->pen )
+    {
+        d_data->pen = pen;
+        invalidateCache();
+
+        if ( d_data->style == QwtSymbol::Path )
+            d_data->path.graphic.reset();
+    }
+}
+
+/*!
+  \return Pen
+  \sa setPen(), brush()
+*/
+const QPen& QwtSymbol::pen() const
+{
+    return d_data->pen;
+}
+
+/*!
+  \brief Set the color of the symbol
+
+  Change the color of the brush for symbol types with a filled area.
+  For all other symbol types the color will be assigned to the pen.
+
+  \param color Color
+
+  \sa setBrush(), setPen(), brush(), pen()
+*/
+void QwtSymbol::setColor( const QColor &color )
+{
+    switch ( d_data->style )
+    {
+        case QwtSymbol::Ellipse:
+        case QwtSymbol::Rect:
+        case QwtSymbol::Diamond:
+        case QwtSymbol::Triangle:
+        case QwtSymbol::UTriangle:
+        case QwtSymbol::DTriangle:
+        case QwtSymbol::RTriangle:
+        case QwtSymbol::LTriangle:
+        case QwtSymbol::Star2:
+        case QwtSymbol::Hexagon:
+        {
+            if ( d_data->brush.color() != color )
+            {
+                d_data->brush.setColor( color );
+                invalidateCache();
+            }
+            break;
+        }
+        case QwtSymbol::Cross:
+        case QwtSymbol::XCross:
+        case QwtSymbol::HLine:
+        case QwtSymbol::VLine:
+        case QwtSymbol::Star1:
+        {
+            if ( d_data->pen.color() != color )
+            {
+                d_data->pen.setColor( color );
+                invalidateCache();
+            }
+            break;
+        }
+        default:
+        {
+            if ( d_data->brush.color() != color ||
+                d_data->pen.color() != color )
+            {
+                invalidateCache();
+            }
+
+            d_data->brush.setColor( color );
+            d_data->pen.setColor( color );
+        }
+    }
+}
+
+/*!
+  \brief Set and enable a pin point
+
+  The position of a complex symbol is not always aligned to its center
+  ( f.e an arrow, where the peak points to a position ). The pin point
+  defines the position inside of a Pixmap, Graphic, SvgDocument 
+  or PainterPath symbol where the represented point has to
+  be aligned to.
+  
+  \param pos Position
+  \param enable En/Disable the pin point alignment
+
+  \sa pinPoint(), setPinPointEnabled()
+ */
+void QwtSymbol::setPinPoint( const QPointF &pos, bool enable )
+{
+    if ( d_data->pinPoint != pos )
+    {
+        d_data->pinPoint = pos;
+        if ( d_data->isPinPointEnabled )
+        {
+            invalidateCache();
+        }
+    }
+
+    setPinPointEnabled( enable );
+}
+
+/*!
+  \return Pin point
+  \sa setPinPoint(), setPinPointEnabled()
+ */
+QPointF QwtSymbol::pinPoint() const
+{
+    return d_data->pinPoint;
+}
+
+/*!
+  En/Disable the pin point alignment
+
+  \param on Enabled, when on is true
+  \sa setPinPoint(), isPinPointEnabled()
+ */
+void QwtSymbol::setPinPointEnabled( bool on )
+{
+    if ( d_data->isPinPointEnabled != on )
+    {
+        d_data->isPinPointEnabled = on;
+        invalidateCache();
+    }
+}
+
+/*!
+  \return True, when the pin point translation is enabled
+  \sa setPinPoint(), setPinPointEnabled()
+ */
+bool QwtSymbol::isPinPointEnabled() const
+{
+    return d_data->isPinPointEnabled;
+}
+
+/*!
+  Render an array of symbols
+
+  Painting several symbols is more effective than drawing symbols
+  one by one, as a couple of layout calculations and setting of pen/brush
+  can be done once for the complete array.
+
+  \param painter Painter
+  \param points Array of points
+  \param numPoints Number of points
+*/
+void QwtSymbol::drawSymbols( QPainter *painter,
+    const QPointF *points, int numPoints ) const
+{
+    if ( numPoints <= 0 )
+        return;
+
+    bool useCache = false;
+
+    // Don't use the pixmap, when the paint device
+    // could generate scalable vectors
+
+    if ( QwtPainter::roundingAlignment( painter ) &&
+        !painter->transform().isScaling() )
+    {
+        if ( d_data->cache.policy == QwtSymbol::Cache )
+        {
+            useCache = true;
+        }
+        else if ( d_data->cache.policy == QwtSymbol::AutoCache )
+        {
+            if ( painter->paintEngine()->type() == QPaintEngine::Raster )
+            {
+                useCache = true;
+            }
+            else
+            {
+                switch( d_data->style )
+                {
+                    case QwtSymbol::XCross:
+                    case QwtSymbol::HLine:
+                    case QwtSymbol::VLine:
+                    case QwtSymbol::Cross:
+                        break;
+
+                    case QwtSymbol::Pixmap:
+                    {
+                        if ( !d_data->size.isEmpty() &&
+                            d_data->size != d_data->pixmap.pixmap.size() ) 
+                        {
+                            useCache = true;
+                        }
+                        break;
+                    }                       
+                    default:
+                        useCache = true;
+                }
+            }
+        }
+    }
+
+    if ( useCache )
+    {
+        const QRect br = boundingRect();
+
+        const QRect rect( 0, 0, br.width(), br.height() );
+        
+        if ( d_data->cache.pixmap.isNull() )
+        {
+            d_data->cache.pixmap = QwtPainter::backingStore( NULL, br.size() );
+            d_data->cache.pixmap.fill( Qt::transparent );
+
+            QPainter p( &d_data->cache.pixmap );
+            p.setRenderHints( painter->renderHints() );
+            p.translate( -br.topLeft() );
+
+            const QPointF pos;
+            renderSymbols( &p, &pos, 1 );
+        }
+
+        const int dx = br.left();
+        const int dy = br.top();
+
+        for ( int i = 0; i < numPoints; i++ )
+        {
+            const int left = qRound( points[i].x() ) + dx;
+            const int top = qRound( points[i].y() ) + dy;
+
+            painter->drawPixmap( left, top, d_data->cache.pixmap );
+        }
+    }
+    else
+    {
+        painter->save();
+        renderSymbols( painter, points, numPoints );
+        painter->restore();
+    }
+}
+
+/*!
+  \brief Draw the symbol into a rectangle
+
+  The symbol is painted centered and scaled into the target rectangle.
+  It is always painted uncached and the pin point is ignored.
+
+  This method is primarily intended for drawing a symbol to
+  the legend.
+
+  \param painter Painter
+  \param rect Target rectangle for the symbol 
+*/
+void QwtSymbol::drawSymbol( QPainter *painter, const QRectF &rect ) const
+{
+    if ( d_data->style == QwtSymbol::NoSymbol )
+        return;
+
+    if ( d_data->style == QwtSymbol::Graphic )
+    {
+        d_data->graphic.graphic.render( 
+            painter, rect, Qt::KeepAspectRatio );
+    }
+    else if ( d_data->style == QwtSymbol::Path )
+    {
+        if ( d_data->path.graphic.isNull() )
+        {
+            d_data->path.graphic = qwtPathGraphic( 
+                d_data->path.path, d_data->pen, d_data->brush );
+        }
+
+        d_data->path.graphic.render( 
+            painter, rect, Qt::KeepAspectRatio );
+        return;
+    }
+    else if ( d_data->style == QwtSymbol::SvgDocument )
+    {
+#ifndef QWT_NO_SVG
+        if ( d_data->svg.renderer )
+        {
+            QRectF scaledRect;
+
+            QSizeF sz = d_data->svg.renderer->viewBoxF().size();
+            if ( !sz.isEmpty() )
+            {
+                sz.scale( rect.size(), Qt::KeepAspectRatio );
+                scaledRect.setSize( sz );
+                scaledRect.moveCenter( rect.center() );
+            }
+            else
+            {
+                scaledRect = rect;
+            }
+
+            d_data->svg.renderer->render( 
+                painter, scaledRect );
+        }
+#endif
+    }
+    else
+    {
+        const QRect br = boundingRect();
+
+        // scale the symbol size to fit into rect.
+
+        const double ratio = qMin( rect.width() / br.width(), 
+            rect.height() / br.height() );
+
+        painter->save();
+
+        painter->translate( rect.center() );
+        painter->scale( ratio, ratio );
+
+        const bool isPinPointEnabled = d_data->isPinPointEnabled;
+        d_data->isPinPointEnabled = false;
+
+        const QPointF pos;
+        renderSymbols( painter, &pos, 1 );
+    
+        d_data->isPinPointEnabled = isPinPointEnabled;
+
+        painter->restore();
+    }
+}
+
+/*!
+  Render the symbol to series of points
+
+  \param painter Qt painter
+  \param points Positions of the symbols
+  \param numPoints Number of points
+ */
+void QwtSymbol::renderSymbols( QPainter *painter,
+    const QPointF *points, int numPoints ) const
+{
+    switch ( d_data->style )
+    {
+        case QwtSymbol::Ellipse:
+        {
+            qwtDrawEllipseSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Rect:
+        {
+            qwtDrawRectSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Diamond:
+        {
+            qwtDrawDiamondSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Cross:
+        {
+            qwtDrawLineSymbols( painter, Qt::Horizontal | Qt::Vertical,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::XCross:
+        {
+            qwtDrawXCrossSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Triangle:
+        case QwtSymbol::UTriangle:
+        {
+            qwtDrawTriangleSymbols( painter, QwtTriangle::Up,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::DTriangle:
+        {
+            qwtDrawTriangleSymbols( painter, QwtTriangle::Down,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::RTriangle:
+        {
+            qwtDrawTriangleSymbols( painter, QwtTriangle::Right,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::LTriangle:
+        {
+            qwtDrawTriangleSymbols( painter, QwtTriangle::Left,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::HLine:
+        {
+            qwtDrawLineSymbols( painter, Qt::Horizontal,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::VLine:
+        {
+            qwtDrawLineSymbols( painter, Qt::Vertical,
+                points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Star1:
+        {
+            qwtDrawStar1Symbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Star2:
+        {
+            qwtDrawStar2Symbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Hexagon:
+        {
+            qwtDrawHexagonSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Path:
+        {
+            if ( d_data->path.graphic.isNull() )
+            {
+                d_data->path.graphic = qwtPathGraphic( d_data->path.path, 
+                    d_data->pen, d_data->brush );
+            }
+
+            qwtDrawGraphicSymbols( painter, points, numPoints, 
+                d_data->path.graphic, *this );
+            break;
+        }
+        case QwtSymbol::Pixmap:
+        {
+            qwtDrawPixmapSymbols( painter, points, numPoints, *this );
+            break;
+        }
+        case QwtSymbol::Graphic:
+        {
+            qwtDrawGraphicSymbols( painter, points, numPoints, 
+                d_data->graphic.graphic, *this );
+            break;
+        }
+        case QwtSymbol::SvgDocument:
+        {
+#ifndef QWT_NO_SVG
+            qwtDrawSvgSymbols( painter, points, numPoints, 
+                d_data->svg.renderer, *this );
+#endif
+            break;
+        }
+        default:;
+    }
+}
+
+/*!
+  Calculate the bounding rectangle for a symbol
+  at position (0,0).
+
+  \return Bounding rectangle
+ */
+QRect QwtSymbol::boundingRect() const
+{
+    QRectF rect;
+
+    bool pinPointTranslation = false;
+
+    switch ( d_data->style )
+    {
+        case QwtSymbol::Ellipse:
+        case QwtSymbol::Rect:
+        case QwtSymbol::Hexagon:
+        {
+            qreal pw = 0.0;
+            if ( d_data->pen.style() != Qt::NoPen )
+                pw = qMax( d_data->pen.widthF(), qreal( 1.0 ) );
+
+            rect.setSize( d_data->size + QSizeF( pw, pw ) );
+            rect.moveCenter( QPointF( 0.0, 0.0 ) );
+
+            break;
+        }
+        case QwtSymbol::XCross:
+        case QwtSymbol::Diamond:
+        case QwtSymbol::Triangle:
+        case QwtSymbol::UTriangle:
+        case QwtSymbol::DTriangle:
+        case QwtSymbol::RTriangle:
+        case QwtSymbol::LTriangle:
+        case QwtSymbol::Star1:
+        case QwtSymbol::Star2:
+        {
+            qreal pw = 0.0;
+            if ( d_data->pen.style() != Qt::NoPen )
+                pw = qMax( d_data->pen.widthF(), qreal( 1.0 ) );
+
+            rect.setSize( d_data->size + QSizeF( 2 * pw, 2 * pw ) );
+            rect.moveCenter( QPointF( 0.0, 0.0 ) );
+            break;
+        }
+        case QwtSymbol::Path:
+        {
+            if ( d_data->path.graphic.isNull() )
+            {
+                d_data->path.graphic = qwtPathGraphic(
+                    d_data->path.path, d_data->pen, d_data->brush );
+            }
+
+            rect = qwtScaledBoundingRect( 
+                d_data->path.graphic, d_data->size );
+            pinPointTranslation = true;
+
+            break;
+        }
+        case QwtSymbol::Pixmap:
+        {
+            if ( d_data->size.isEmpty() )
+                rect.setSize( d_data->pixmap.pixmap.size() );
+            else
+                rect.setSize( d_data->size );
+            
+            pinPointTranslation = true;
+
+            break;
+        }
+        case QwtSymbol::Graphic:
+        {
+            rect = qwtScaledBoundingRect( 
+                d_data->graphic.graphic, d_data->size );
+            pinPointTranslation = true;
+
+            break;
+        }
+#ifndef QWT_NO_SVG
+        case QwtSymbol::SvgDocument:
+        {
+            if ( d_data->svg.renderer )
+                rect = d_data->svg.renderer->viewBoxF();
+
+            if ( d_data->size.isValid() && !rect.isEmpty() )
+            {
+                QSizeF sz = rect.size();
+
+                const double sx = d_data->size.width() / sz.width();
+                const double sy = d_data->size.height() / sz.height();
+
+                QTransform transform;
+                transform.scale( sx, sy );
+
+                rect = transform.mapRect( rect );
+            }
+            pinPointTranslation = true;
+            break;
+        }
+#endif
+        default:
+        {
+            rect.setSize( d_data->size );
+            rect.moveCenter( QPointF( 0.0, 0.0 ) );
+        }
+    }
+
+    if ( pinPointTranslation )
+    {
+        QPointF pinPoint( 0.0, 0.0 );
+        if ( d_data->isPinPointEnabled )
+            pinPoint = rect.center() - d_data->pinPoint;
+
+        rect.moveCenter( pinPoint );
+    }
+
+    QRect r;
+    r.setLeft( qFloor( rect.left() ) );
+    r.setTop( qFloor( rect.top() ) );
+    r.setRight( qCeil( rect.right() ) );
+    r.setBottom( qCeil( rect.bottom() ) );
+
+    if ( d_data->style != QwtSymbol::Pixmap )
+        r.adjust( -1, -1, 1, 1 ); // for antialiasing
+
+    return r;
+}
+
+/*!
+  Invalidate the cached symbol pixmap
+
+  The symbol invalidates its cache, whenever an attribute is changed
+  that has an effect ob how to display a symbol. In case of derived
+  classes with individual styles ( >= QwtSymbol::UserStyle ) it
+  might be necessary to call invalidateCache() for attributes
+  that are relevant for this style.
+
+  \sa CachePolicy, setCachePolicy(), drawSymbols()
+ */
+void QwtSymbol::invalidateCache()
+{
+    if ( !d_data->cache.pixmap.isNull() )
+        d_data->cache.pixmap = QPixmap();
+}
+
+/*!
+  Specify the symbol style
+
+  \param style Style
+  \sa style()
+*/
+void QwtSymbol::setStyle( QwtSymbol::Style style )
+{
+    if ( d_data->style != style )
+    {
+        d_data->style = style;
+        invalidateCache();
+    }
+}
+
+/*!
+  \return Current symbol style
+  \sa setStyle()
+*/
+QwtSymbol::Style QwtSymbol::style() const
+{
+    return d_data->style;
+}
diff --git a/source/third_party/qwt/qwt_system_clock.cpp b/source/third_party/qwt/qwt_system_clock.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d31b065e20bd1fdec18b0bc9439bbd454faf88b
--- /dev/null
+++ b/source/third_party/qwt/qwt_system_clock.cpp
@@ -0,0 +1,396 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_system_clock.h"
+
+#if QT_VERSION >= 0x040800
+#define USE_ELAPSED_TIMER 1
+#endif
+
+#if USE_ELAPSED_TIMER
+
+#include <qelapsedtimer.h>
+
+class QwtSystemClock::PrivateData
+{
+public:
+    QElapsedTimer timer;
+};
+
+QwtSystemClock::QwtSystemClock()
+{
+    d_data = new PrivateData();
+}
+
+QwtSystemClock::~QwtSystemClock()
+{
+    delete d_data;
+}
+    
+bool QwtSystemClock::isNull() const
+{
+    return d_data->timer.isValid();
+}
+        
+void QwtSystemClock::start()
+{
+    d_data->timer.start();
+}
+
+double QwtSystemClock::restart()
+{
+    const qint64 nsecs = d_data->timer.restart();
+    return nsecs / 1e6;
+}
+
+double QwtSystemClock::elapsed() const
+{
+    const qint64 nsecs = d_data->timer.nsecsElapsed();
+    return nsecs / 1e6;
+}
+    
+#else // !USE_ELAPSED_TIMER
+
+#include <qdatetime.h>
+
+#if !defined(Q_OS_WIN)
+#include <unistd.h>
+#endif
+
+#if defined(Q_OS_MAC)
+#include <stdint.h>
+#include <mach/mach_time.h>
+#define QWT_HIGH_RESOLUTION_CLOCK
+#elif defined(_POSIX_TIMERS)
+#include <time.h>
+#define QWT_HIGH_RESOLUTION_CLOCK
+#elif defined(Q_OS_WIN)
+#define QWT_HIGH_RESOLUTION_CLOCK
+#include <qt_windows.h>
+#endif
+
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+
+class QwtHighResolutionClock
+{
+public:
+    QwtHighResolutionClock();
+
+    void start();
+    double restart();
+    double elapsed() const;
+
+    bool isNull() const;
+
+    static double precision();
+
+private:
+
+#if defined(Q_OS_MAC)
+    static double msecsTo( uint64_t, uint64_t );
+
+    uint64_t d_timeStamp;
+#elif defined(_POSIX_TIMERS)
+
+    static double msecsTo( const struct timespec &,
+        const struct timespec & );
+
+    static bool isMonotonic();
+
+    struct timespec d_timeStamp;
+    clockid_t d_clockId;
+
+#elif defined(Q_OS_WIN)
+
+    LARGE_INTEGER d_startTicks;
+    LARGE_INTEGER d_ticksPerSecond;
+#endif
+};
+
+#if defined(Q_OS_MAC)
+QwtHighResolutionClock::QwtHighResolutionClock():
+    d_timeStamp( 0 )
+{
+}
+
+double QwtHighResolutionClock::precision()
+{
+    return 1e-6;
+}
+
+void QwtHighResolutionClock::start()
+{
+    d_timeStamp = mach_absolute_time();
+}
+
+double QwtHighResolutionClock::restart()
+{
+    const uint64_t timeStamp = mach_absolute_time();
+    const double elapsed = msecsTo( d_timeStamp, timeStamp );
+    d_timeStamp = timeStamp;
+
+    return elapsed;
+}
+
+double QwtHighResolutionClock::elapsed() const
+{
+    return msecsTo( d_timeStamp, mach_absolute_time() );
+}
+
+bool QwtHighResolutionClock::isNull() const
+{
+    return d_timeStamp == 0;
+}
+
+double QwtHighResolutionClock::msecsTo(
+    uint64_t from, uint64_t to )
+{
+    const uint64_t difference = to - from;
+
+    static double conversion = 0.0;
+    if ( conversion == 0.0 )
+    {
+        mach_timebase_info_data_t info;
+        kern_return_t err = mach_timebase_info( &info );
+
+        // convert the timebase into ms
+        if ( err == 0  )
+            conversion = 1e-6 * ( double ) info.numer / ( double ) info.denom;
+    }
+
+    return conversion * ( double ) difference;
+}
+
+#elif defined(_POSIX_TIMERS)
+
+QwtHighResolutionClock::QwtHighResolutionClock()
+{
+    d_clockId = isMonotonic() ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+    d_timeStamp.tv_sec = d_timeStamp.tv_nsec = 0;
+}
+
+double QwtHighResolutionClock::precision()
+{
+    struct timespec resolution;
+
+    int clockId = isMonotonic() ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+    ::clock_getres( clockId, &resolution );
+
+    return resolution.tv_nsec / 1e3;
+}
+
+inline bool QwtHighResolutionClock::isNull() const
+{
+    return d_timeStamp.tv_sec <= 0 && d_timeStamp.tv_nsec <= 0;
+}
+
+inline void QwtHighResolutionClock::start()
+{
+    ::clock_gettime( d_clockId, &d_timeStamp );
+}
+
+double QwtHighResolutionClock::restart()
+{
+    struct timespec timeStamp;
+    ::clock_gettime( d_clockId, &timeStamp );
+
+    const double elapsed = msecsTo( d_timeStamp, timeStamp );
+
+    d_timeStamp = timeStamp;
+    return elapsed;
+}
+
+inline double QwtHighResolutionClock::elapsed() const
+{
+    struct timespec timeStamp;
+    ::clock_gettime( d_clockId, &timeStamp );
+
+    return msecsTo( d_timeStamp, timeStamp );
+}
+
+inline double QwtHighResolutionClock::msecsTo(
+    const struct timespec &t1, const struct timespec &t2 )
+{
+    return ( t2.tv_sec - t1.tv_sec ) * 1e3
+        + ( t2.tv_nsec - t1.tv_nsec ) * 1e-6;
+}
+
+bool QwtHighResolutionClock::isMonotonic()
+{
+    // code copied from qcore_unix.cpp
+
+#if (_POSIX_MONOTONIC_CLOCK-0 > 0)
+    return true;
+#else
+    static int returnValue = 0;
+
+    if ( returnValue == 0 )
+    {
+#if (_POSIX_MONOTONIC_CLOCK-0 < 0) || !defined(_SC_MONOTONIC_CLOCK)
+        returnValue = -1;
+#elif (_POSIX_MONOTONIC_CLOCK == 0)
+        // detect if the system support monotonic timers
+        const long x = sysconf( _SC_MONOTONIC_CLOCK );
+        returnValue = ( x >= 200112L ) ? 1 : -1;
+#endif
+    }
+
+    return returnValue != -1;
+#endif
+}
+
+#elif defined(Q_OS_WIN)
+
+QwtHighResolutionClock::QwtHighResolutionClock()
+{
+    d_startTicks.QuadPart = 0;
+    QueryPerformanceFrequency( &d_ticksPerSecond );
+}
+
+double QwtHighResolutionClock::precision()
+{
+    LARGE_INTEGER ticks;
+    if ( QueryPerformanceFrequency( &ticks ) && ticks.QuadPart > 0 )
+        return 1e3 / ticks.QuadPart;
+
+    return 0.0;
+}
+
+inline bool QwtHighResolutionClock::isNull() const
+{
+    return d_startTicks.QuadPart <= 0;
+}
+
+inline void QwtHighResolutionClock::start()
+{
+    QueryPerformanceCounter( &d_startTicks );
+}
+
+inline double QwtHighResolutionClock::restart()
+{
+    LARGE_INTEGER ticks;
+    QueryPerformanceCounter( &ticks );
+
+    const double dt = ticks.QuadPart - d_startTicks.QuadPart;
+    d_startTicks = ticks;
+
+    return dt / d_ticksPerSecond.QuadPart * 1e3;
+}
+
+inline double QwtHighResolutionClock::elapsed() const
+{
+    LARGE_INTEGER ticks;
+    QueryPerformanceCounter( &ticks );
+
+    const double dt = ticks.QuadPart - d_startTicks.QuadPart;
+    return dt / d_ticksPerSecond.QuadPart * 1e3;
+}
+
+#endif
+
+#endif // QWT_HIGH_RESOLUTION_CLOCK
+
+class QwtSystemClock::PrivateData
+{
+public:
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    QwtHighResolutionClock *clock;
+#endif
+    QTime time;
+};
+
+//!  Constructs a null clock object.
+QwtSystemClock::QwtSystemClock()
+{
+    d_data = new PrivateData;
+
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    d_data->clock = NULL;
+    if ( QwtHighResolutionClock::precision() > 0.0 )
+        d_data->clock = new QwtHighResolutionClock;
+#endif
+}
+
+//! Destructor
+QwtSystemClock::~QwtSystemClock()
+{
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    delete d_data->clock;
+#endif
+    delete d_data;
+}
+
+/*!
+  \return true if the clock has never been started.
+*/
+bool QwtSystemClock::isNull() const
+{
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    if ( d_data->clock )
+        return d_data->clock->isNull();
+#endif
+
+    return d_data->time.isNull();
+}
+
+/*!
+  Sets the start time to the current time.
+*/
+void QwtSystemClock::start()
+{
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    if ( d_data->clock )
+    {
+        d_data->clock->start();
+        return;
+    }
+#endif
+
+    d_data->time.start();
+}
+
+/*!
+  Set the start time to the current time 
+  \return Time, that is elapsed since the previous start time.
+*/
+double QwtSystemClock::restart()
+{
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    if ( d_data->clock )
+        return d_data->clock->restart();
+#endif
+
+    return d_data->time.restart();
+}
+
+/*!
+  \return Number of milliseconds that have elapsed since the last time
+          start() or restart() was called or 0.0 for null clocks.
+*/
+double QwtSystemClock::elapsed() const
+{
+    double elapsed = 0.0;
+
+#if defined(QWT_HIGH_RESOLUTION_CLOCK)
+    if ( d_data->clock )
+    {
+        if ( !d_data->clock->isNull() )
+            elapsed = d_data->clock->elapsed();
+
+        return elapsed;
+    }
+#endif
+
+    if ( !d_data->time.isNull() )
+        elapsed = d_data->time.elapsed();
+
+    return elapsed;
+}
+
+#endif
diff --git a/source/third_party/qwt/qwt_text.cpp b/source/third_party/qwt/qwt_text.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0fff644f260800f30521690ea14ee1ad98b1222a
--- /dev/null
+++ b/source/third_party/qwt/qwt_text.cpp
@@ -0,0 +1,676 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_painter.h"
+#include "qwt/qwt_text_engine.h"
+#include <qmap.h>
+#include <qfont.h>
+#include <qcolor.h>
+#include <qpen.h>
+#include <qbrush.h>
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qmath.h>
+
+class QwtTextEngineDict
+{
+public:
+    static QwtTextEngineDict &dict();
+
+    void setTextEngine( QwtText::TextFormat, QwtTextEngine * );
+
+    const QwtTextEngine *textEngine( QwtText::TextFormat ) const;
+    const QwtTextEngine *textEngine( const QString &,
+        QwtText::TextFormat ) const;
+
+private:
+    QwtTextEngineDict();
+    ~QwtTextEngineDict();
+
+    typedef QMap<int, QwtTextEngine *> EngineMap;
+
+    inline const QwtTextEngine *engine( EngineMap::const_iterator &it ) const
+    {
+        return it.value();
+    }
+
+    EngineMap d_map;
+};
+
+QwtTextEngineDict &QwtTextEngineDict::dict()
+{
+    static QwtTextEngineDict engineDict;
+    return engineDict;
+}
+
+QwtTextEngineDict::QwtTextEngineDict()
+{
+    d_map.insert( QwtText::PlainText, new QwtPlainTextEngine() );
+#ifndef QT_NO_RICHTEXT
+    d_map.insert( QwtText::RichText, new QwtRichTextEngine() );
+#endif
+}
+
+QwtTextEngineDict::~QwtTextEngineDict()
+{
+    for ( EngineMap::const_iterator it = d_map.begin();
+        it != d_map.end(); ++it )
+    {
+        const QwtTextEngine *textEngine = engine( it );
+        delete textEngine;
+    }
+}
+
+const QwtTextEngine *QwtTextEngineDict::textEngine( const QString& text,
+    QwtText::TextFormat format ) const
+{
+    if ( format == QwtText::AutoText )
+    {
+        for ( EngineMap::const_iterator it = d_map.begin();
+            it != d_map.end(); ++it )
+        {
+            if ( it.key() != QwtText::PlainText )
+            {
+                const QwtTextEngine *e = engine( it );
+                if ( e && e->mightRender( text ) )
+                    return e;
+            }
+        }
+    }
+
+    EngineMap::const_iterator it = d_map.find( format );
+    if ( it != d_map.end() )
+    {
+        const QwtTextEngine *e = engine( it );
+        if ( e )
+            return e;
+    }
+
+    it = d_map.find( QwtText::PlainText );
+    return engine( it );
+}
+
+void QwtTextEngineDict::setTextEngine( QwtText::TextFormat format,
+    QwtTextEngine *engine )
+{
+    if ( format == QwtText::AutoText )
+        return;
+
+    if ( format == QwtText::PlainText && engine == NULL )
+        return;
+
+    EngineMap::const_iterator it = d_map.find( format );
+    if ( it != d_map.end() )
+    {
+        const QwtTextEngine *e = this->engine( it );
+        if ( e )
+            delete e;
+
+        d_map.remove( format );
+    }
+
+    if ( engine != NULL )
+        d_map.insert( format, engine );
+}
+
+const QwtTextEngine *QwtTextEngineDict::textEngine(
+    QwtText::TextFormat format ) const
+{
+    const QwtTextEngine *e = NULL;
+
+    EngineMap::const_iterator it = d_map.find( format );
+    if ( it != d_map.end() )
+        e = engine( it );
+
+    return e;
+}
+
+class QwtText::PrivateData
+{
+public:
+    PrivateData():
+        renderFlags( Qt::AlignCenter ),
+        borderRadius( 0 ),
+        borderPen( Qt::NoPen ),
+        backgroundBrush( Qt::NoBrush ),
+        paintAttributes( 0 ),
+        layoutAttributes( 0 ),
+        textEngine( NULL )
+    {
+    }
+
+    int renderFlags;
+    QString text;
+    QFont font;
+    QColor color;
+    double borderRadius;
+    QPen borderPen;
+    QBrush backgroundBrush;
+
+    QwtText::PaintAttributes paintAttributes;
+    QwtText::LayoutAttributes layoutAttributes;
+
+    const QwtTextEngine *textEngine;
+};
+
+class QwtText::LayoutCache
+{
+public:
+    void invalidate()
+    {
+        textSize = QSizeF();
+    }
+
+    QFont font;
+    QSizeF textSize;
+};
+
+/*!
+   Constructor
+
+   \param text Text content
+   \param textFormat Text format
+*/
+QwtText::QwtText( const QString &text, QwtText::TextFormat textFormat )
+{
+    d_data = new PrivateData;
+    d_data->text = text;
+    d_data->textEngine = textEngine( text, textFormat );
+
+    d_layoutCache = new LayoutCache;
+}
+
+//! Copy constructor
+QwtText::QwtText( const QwtText &other )
+{
+    d_data = new PrivateData;
+    *d_data = *other.d_data;
+
+    d_layoutCache = new LayoutCache;
+    *d_layoutCache = *other.d_layoutCache;
+}
+
+//! Destructor
+QwtText::~QwtText()
+{
+    delete d_data;
+    delete d_layoutCache;
+}
+
+//! Assignment operator
+QwtText &QwtText::operator=( const QwtText & other )
+{
+    *d_data = *other.d_data;
+    *d_layoutCache = *other.d_layoutCache;
+    return *this;
+}
+
+//! Relational operator
+bool QwtText::operator==( const QwtText &other ) const
+{
+    return d_data->renderFlags == other.d_data->renderFlags &&
+        d_data->text == other.d_data->text &&
+        d_data->font == other.d_data->font &&
+        d_data->color == other.d_data->color &&
+        d_data->borderRadius == other.d_data->borderRadius &&
+        d_data->borderPen == other.d_data->borderPen &&
+        d_data->backgroundBrush == other.d_data->backgroundBrush &&
+        d_data->paintAttributes == other.d_data->paintAttributes &&
+        d_data->textEngine == other.d_data->textEngine;
+}
+
+//! Relational operator
+bool QwtText::operator!=( const QwtText &other ) const // invalidate
+{
+    return !( other == *this );
+}
+
+/*!
+   Assign a new text content
+
+   \param text Text content
+   \param textFormat Text format
+
+   \sa text()
+*/
+void QwtText::setText( const QString &text,
+    QwtText::TextFormat textFormat )
+{
+    d_data->text = text;
+    d_data->textEngine = textEngine( text, textFormat );
+    d_layoutCache->invalidate();
+}
+
+/*!
+   \return Text as QString.
+   \sa setText()
+*/
+QString QwtText::text() const
+{
+    return d_data->text;
+}
+
+/*!
+   \brief Change the render flags
+
+   The default setting is Qt::AlignCenter
+
+   \param renderFlags Bitwise OR of the flags used like in QPainter::drawText()
+
+   \sa renderFlags(), QwtTextEngine::draw()
+   \note Some renderFlags might have no effect, depending on the text format.
+*/
+void QwtText::setRenderFlags( int renderFlags )
+{
+    if ( renderFlags != d_data->renderFlags )
+    {
+        d_data->renderFlags = renderFlags;
+        d_layoutCache->invalidate();
+    }
+}
+
+/*!
+   \return Render flags
+   \sa setRenderFlags()
+*/
+int QwtText::renderFlags() const
+{
+    return d_data->renderFlags;
+}
+
+/*!
+   Set the font.
+
+   \param font Font
+   \note Setting the font might have no effect, when
+         the text contains control sequences for setting fonts.
+*/
+void QwtText::setFont( const QFont &font )
+{
+    d_data->font = font;
+    setPaintAttribute( PaintUsingTextFont );
+}
+
+//! Return the font.
+QFont QwtText::font() const
+{
+    return d_data->font;
+}
+
+/*!
+   Return the font of the text, if it has one.
+   Otherwise return defaultFont.
+
+   \param defaultFont Default font
+   \return Font used for drawing the text
+
+   \sa setFont(), font(), PaintAttributes
+*/
+QFont QwtText::usedFont( const QFont &defaultFont ) const
+{
+    if ( d_data->paintAttributes & PaintUsingTextFont )
+        return d_data->font;
+
+    return defaultFont;
+}
+
+/*!
+   Set the pen color used for drawing the text.
+
+   \param color Color
+   \note Setting the color might have no effect, when
+         the text contains control sequences for setting colors.
+*/
+void QwtText::setColor( const QColor &color )
+{
+    d_data->color = color;
+    setPaintAttribute( PaintUsingTextColor );
+}
+
+//! Return the pen color, used for painting the text
+QColor QwtText::color() const
+{
+    return d_data->color;
+}
+
+/*!
+  Return the color of the text, if it has one.
+  Otherwise return defaultColor.
+
+  \param defaultColor Default color
+  \return Color used for drawing the text
+
+  \sa setColor(), color(), PaintAttributes
+*/
+QColor QwtText::usedColor( const QColor &defaultColor ) const
+{
+    if ( d_data->paintAttributes & PaintUsingTextColor )
+        return d_data->color;
+
+    return defaultColor;
+}
+
+/*!
+  Set the radius for the corners of the border frame
+
+  \param radius Radius of a rounded corner
+  \sa borderRadius(), setBorderPen(), setBackgroundBrush()
+*/
+void QwtText::setBorderRadius( double radius )
+{
+    d_data->borderRadius = qMax( 0.0, radius );
+}
+
+/*!
+  \return Radius for the corners of the border frame
+  \sa setBorderRadius(), borderPen(), backgroundBrush()
+*/
+double QwtText::borderRadius() const
+{
+    return d_data->borderRadius;
+}
+
+/*!
+   Set the background pen
+
+   \param pen Background pen
+   \sa borderPen(), setBackgroundBrush()
+*/
+void QwtText::setBorderPen( const QPen &pen )
+{
+    d_data->borderPen = pen;
+    setPaintAttribute( PaintBackground );
+}
+
+/*!
+   \return Background pen
+   \sa setBorderPen(), backgroundBrush()
+*/
+QPen QwtText::borderPen() const
+{
+    return d_data->borderPen;
+}
+
+/*!
+   Set the background brush
+
+   \param brush Background brush
+   \sa backgroundBrush(), setBorderPen()
+*/
+void QwtText::setBackgroundBrush( const QBrush &brush )
+{
+    d_data->backgroundBrush = brush;
+    setPaintAttribute( PaintBackground );
+}
+
+/*!
+   \return Background brush
+   \sa setBackgroundBrush(), borderPen()
+*/
+QBrush QwtText::backgroundBrush() const
+{
+    return d_data->backgroundBrush;
+}
+
+/*!
+   Change a paint attribute
+
+   \param attribute Paint attribute
+   \param on On/Off
+
+   \note Used by setFont(), setColor(),
+         setBorderPen() and setBackgroundBrush()
+   \sa testPaintAttribute()
+*/
+void QwtText::setPaintAttribute( PaintAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->paintAttributes |= attribute;
+    else
+        d_data->paintAttributes &= ~attribute;
+}
+
+/*!
+   Test a paint attribute
+
+   \param attribute Paint attribute
+   \return true, if attribute is enabled
+
+   \sa setPaintAttribute()
+*/
+bool QwtText::testPaintAttribute( PaintAttribute attribute ) const
+{
+    return d_data->paintAttributes & attribute;
+}
+
+/*!
+   Change a layout attribute
+
+   \param attribute Layout attribute
+   \param on On/Off
+   \sa testLayoutAttribute()
+*/
+void QwtText::setLayoutAttribute( LayoutAttribute attribute, bool on )
+{
+    if ( on )
+        d_data->layoutAttributes |= attribute;
+    else
+        d_data->layoutAttributes &= ~attribute;
+}
+
+/*!
+   Test a layout attribute
+
+   \param attribute Layout attribute
+   \return true, if attribute is enabled
+
+   \sa setLayoutAttribute()
+*/
+bool QwtText::testLayoutAttribute( LayoutAttribute attribute ) const
+{
+    return d_data->layoutAttributes | attribute;
+}
+
+/*!
+   Find the height for a given width
+
+   \param defaultFont Font, used for the calculation if the text has no font
+   \param width Width
+
+   \return Calculated height
+*/
+double QwtText::heightForWidth( double width, const QFont &defaultFont ) const
+{
+    // We want to calculate in screen metrics. So
+    // we need a font that uses screen metrics
+
+    const QFont font( usedFont( defaultFont ), QApplication::desktop() );
+
+    double h = 0;
+
+    if ( d_data->layoutAttributes & MinimumLayout )
+    {
+        double left, right, top, bottom;
+        d_data->textEngine->textMargins( font, d_data->text,
+            left, right, top, bottom );
+
+        h = d_data->textEngine->heightForWidth(
+            font, d_data->renderFlags, d_data->text,
+            width + left + right );
+
+        h -= top + bottom;
+    }
+    else
+    {
+        h = d_data->textEngine->heightForWidth(
+            font, d_data->renderFlags, d_data->text, width );
+    }
+
+    return h;
+}
+
+/*!
+   Returns the size, that is needed to render text
+
+   \param defaultFont Font of the text
+   \return Calculated size
+*/
+QSizeF QwtText::textSize( const QFont &defaultFont ) const
+{
+    // We want to calculate in screen metrics. So
+    // we need a font that uses screen metrics
+
+    const QFont font( usedFont( defaultFont ), QApplication::desktop() );
+
+    if ( !d_layoutCache->textSize.isValid()
+        || d_layoutCache->font != font )
+    {
+        d_layoutCache->textSize = d_data->textEngine->textSize(
+            font, d_data->renderFlags, d_data->text );
+        d_layoutCache->font = font;
+    }
+
+    QSizeF sz = d_layoutCache->textSize;
+
+    if ( d_data->layoutAttributes & MinimumLayout )
+    {
+        double left, right, top, bottom;
+        d_data->textEngine->textMargins( font, d_data->text,
+            left, right, top, bottom );
+        sz -= QSizeF( left + right, top + bottom );
+    }
+
+    return sz;
+}
+
+/*!
+   Draw a text into a rectangle
+
+   \param painter Painter
+   \param rect Rectangle
+*/
+void QwtText::draw( QPainter *painter, const QRectF &rect ) const
+{
+    if ( d_data->paintAttributes & PaintBackground )
+    {
+        if ( d_data->borderPen != Qt::NoPen ||
+            d_data->backgroundBrush != Qt::NoBrush )
+        {
+            painter->save();
+
+            painter->setPen( d_data->borderPen );
+            painter->setBrush( d_data->backgroundBrush );
+
+            if ( d_data->borderRadius == 0 )
+            {
+                QwtPainter::drawRect( painter, rect );
+            }
+            else
+            {
+                painter->setRenderHint( QPainter::Antialiasing, true );
+                painter->drawRoundedRect( rect,
+                    d_data->borderRadius, d_data->borderRadius );
+            }
+
+            painter->restore();
+        }
+    }
+
+    painter->save();
+
+    if ( d_data->paintAttributes & PaintUsingTextFont )
+    {
+        painter->setFont( d_data->font );
+    }
+
+    if ( d_data->paintAttributes & PaintUsingTextColor )
+    {
+        if ( d_data->color.isValid() )
+            painter->setPen( d_data->color );
+    }
+
+    QRectF expandedRect = rect;
+    if ( d_data->layoutAttributes & MinimumLayout )
+    {
+        // We want to calculate in screen metrics. So
+        // we need a font that uses screen metrics
+
+        const QFont font( painter->font(), QApplication::desktop() );
+
+        double left, right, top, bottom;
+        d_data->textEngine->textMargins(
+            font, d_data->text, left, right, top, bottom );
+
+        expandedRect.setTop( rect.top() - top );
+        expandedRect.setBottom( rect.bottom() + bottom );
+        expandedRect.setLeft( rect.left() - left );
+        expandedRect.setRight( rect.right() + right );
+    }
+
+    d_data->textEngine->draw( painter, expandedRect,
+        d_data->renderFlags, d_data->text );
+
+    painter->restore();
+}
+
+/*!
+   Find the text engine for a text format
+
+   In case of QwtText::AutoText the first text engine
+   (beside QwtPlainTextEngine) is returned, where QwtTextEngine::mightRender
+   returns true. If there is none QwtPlainTextEngine is returned.
+
+   If no text engine is registered for the format QwtPlainTextEngine
+   is returnd.
+
+   \param text Text, needed in case of AutoText
+   \param format Text format
+
+   \return Corresponding text engine
+*/
+const QwtTextEngine *QwtText::textEngine( const QString &text,
+    QwtText::TextFormat format )
+{
+    return QwtTextEngineDict::dict().textEngine( text, format );
+}
+
+/*!
+   Assign/Replace a text engine for a text format
+
+   With setTextEngine it is possible to extend Qwt with
+   other types of text formats.
+
+   For QwtText::PlainText it is not allowed to assign a engine == NULL.
+
+   \param format Text format
+   \param engine Text engine
+
+   \sa QwtMathMLTextEngine
+   \warning Using QwtText::AutoText does nothing.
+*/
+void QwtText::setTextEngine( QwtText::TextFormat format,
+    QwtTextEngine *engine )
+{
+    QwtTextEngineDict::dict().setTextEngine( format, engine );
+}
+
+/*!
+   \brief Find the text engine for a text format
+
+   textEngine can be used to find out if a text format is supported.
+
+   \param format Text format
+   \return The text engine, or NULL if no engine is available.
+*/
+const QwtTextEngine *QwtText::textEngine( QwtText::TextFormat format )
+{
+    return  QwtTextEngineDict::dict().textEngine( format );
+}
diff --git a/source/third_party/qwt/qwt_text_engine.cpp b/source/third_party/qwt/qwt_text_engine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2e244b880a7bb14850281715ddbbb3e9e60d18d
--- /dev/null
+++ b/source/third_party/qwt/qwt_text_engine.cpp
@@ -0,0 +1,345 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_text_engine.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qmap.h>
+#include <qwidget.h>
+#include <qtextobject.h>
+#include <qtextdocument.h>
+#include <qabstracttextdocumentlayout.h>
+
+static QString taggedRichText( const QString &text, int flags )
+{
+    QString richText = text;
+
+    // By default QSimpleRichText is Qt::AlignLeft
+    if ( flags & Qt::AlignJustify )
+    {
+        richText.prepend( QString::fromLatin1( "<div align=\"justify\">" ) );
+        richText.append( QString::fromLatin1( "</div>" ) );
+    }
+    else if ( flags & Qt::AlignRight )
+    {
+        richText.prepend( QString::fromLatin1( "<div align=\"right\">" ) );
+        richText.append( QString::fromLatin1( "</div>" ) );
+    }
+    else if ( flags & Qt::AlignHCenter )
+    {
+        richText.prepend( QString::fromLatin1( "<div align=\"center\">" ) );
+        richText.append( QString::fromLatin1( "</div>" ) );
+    }
+
+    return richText;
+}
+
+class QwtRichTextDocument: public QTextDocument
+{
+public:
+    QwtRichTextDocument( const QString &text, int flags, const QFont &font )
+    {
+        setUndoRedoEnabled( false );
+        setDefaultFont( font );
+        setHtml( text );
+
+        // make sure we have a document layout
+        ( void )documentLayout();
+
+        QTextOption option = defaultTextOption();
+        if ( flags & Qt::TextWordWrap )
+            option.setWrapMode( QTextOption::WordWrap );
+        else
+            option.setWrapMode( QTextOption::NoWrap );
+
+        option.setAlignment( static_cast<Qt::Alignment>( flags ) );
+        setDefaultTextOption( option );
+
+        QTextFrame *root = rootFrame();
+        QTextFrameFormat fm = root->frameFormat();
+        fm.setBorder( 0 );
+        fm.setMargin( 0 );
+        fm.setPadding( 0 );
+        fm.setBottomMargin( 0 );
+        fm.setLeftMargin( 0 );
+        root->setFrameFormat( fm );
+
+        adjustSize();
+    }
+};
+
+class QwtPlainTextEngine::PrivateData
+{
+public:
+    int effectiveAscent( const QFont &font ) const
+    {
+        const QString fontKey = font.key();
+
+        QMap<QString, int>::const_iterator it =
+            d_ascentCache.find( fontKey );
+        if ( it == d_ascentCache.end() )
+        {
+            int ascent = findAscent( font );
+            it = d_ascentCache.insert( fontKey, ascent );
+        }
+
+        return ( *it );
+    }
+
+private:
+    int findAscent( const QFont &font ) const
+    {
+        static const QString dummy( "E" );
+        static const QColor white( Qt::white );
+
+        const QFontMetrics fm( font );
+        QPixmap pm( fm.width( dummy ), fm.height() );
+        pm.fill( white );
+
+        QPainter p( &pm );
+        p.setFont( font );
+        p.drawText( 0, 0,  pm.width(), pm.height(), 0, dummy );
+        p.end();
+
+        const QImage img = pm.toImage();
+
+        int row = 0;
+        for ( row = 0; row < img.height(); row++ )
+        {
+            const QRgb *line = reinterpret_cast<const QRgb *>( 
+                img.scanLine( row ) );
+
+            const int w = pm.width();
+            for ( int col = 0; col < w; col++ )
+            {
+                if ( line[col] != white.rgb() )
+                    return fm.ascent() - row + 1;
+            }
+        }
+
+        return fm.ascent();
+    }
+
+    mutable QMap<QString, int> d_ascentCache;
+};
+
+//! Constructor
+QwtTextEngine::QwtTextEngine()
+{
+}
+
+//! Destructor
+QwtTextEngine::~QwtTextEngine()
+{
+}
+
+//! Constructor
+QwtPlainTextEngine::QwtPlainTextEngine()
+{
+    d_data = new PrivateData;
+}
+
+//! Destructor
+QwtPlainTextEngine::~QwtPlainTextEngine()
+{
+    delete d_data;
+}
+
+/*!
+   Find the height for a given width
+
+   \param font Font of the text
+   \param flags Bitwise OR of the flags used like in QPainter::drawText
+   \param text Text to be rendered
+   \param width Width
+
+   \return Calculated height
+*/
+double QwtPlainTextEngine::heightForWidth( const QFont& font, int flags,
+        const QString& text, double width ) const
+{
+    const QFontMetricsF fm( font );
+    const QRectF rect = fm.boundingRect(
+        QRectF( 0, 0, width, QWIDGETSIZE_MAX ), flags, text );
+
+    return rect.height();
+}
+
+/*!
+  Returns the size, that is needed to render text
+
+  \param font Font of the text
+  \param flags Bitwise OR of the flags used like in QPainter::drawText
+  \param text Text to be rendered
+
+  \return Calculated size
+*/
+QSizeF QwtPlainTextEngine::textSize( const QFont &font,
+    int flags, const QString& text ) const
+{
+    const QFontMetricsF fm( font );
+    const QRectF rect = fm.boundingRect(
+        QRectF( 0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ), flags, text );
+
+    return rect.size();
+}
+
+/*!
+  Return margins around the texts
+
+  \param font Font of the text
+  \param left Return 0
+  \param right Return 0
+  \param top Return value for the top margin
+  \param bottom Return value for the bottom margin
+*/
+void QwtPlainTextEngine::textMargins( const QFont &font, const QString &,
+    double &left, double &right, double &top, double &bottom ) const
+{
+    left = right = top = 0;
+
+    const QFontMetricsF fm( font );
+    top = fm.ascent() - d_data->effectiveAscent( font );
+    bottom = fm.descent();
+}
+
+/*!
+  \brief Draw the text in a clipping rectangle
+
+  A wrapper for QPainter::drawText.
+
+  \param painter Painter
+  \param rect Clipping rectangle
+  \param flags Bitwise OR of the flags used like in QPainter::drawText
+  \param text Text to be rendered
+*/
+void QwtPlainTextEngine::draw( QPainter *painter, const QRectF &rect,
+    int flags, const QString& text ) const
+{
+    QwtPainter::drawText( painter, rect, flags, text );
+}
+
+/*!
+  Test if a string can be rendered by this text engine.
+  \return Always true. All texts can be rendered by QwtPlainTextEngine
+*/
+bool QwtPlainTextEngine::mightRender( const QString & ) const
+{
+    return true;
+}
+
+#ifndef QT_NO_RICHTEXT
+
+//! Constructor
+QwtRichTextEngine::QwtRichTextEngine()
+{
+}
+
+/*!
+   Find the height for a given width
+
+   \param font Font of the text
+   \param flags Bitwise OR of the flags used like in QPainter::drawText()
+   \param text Text to be rendered
+   \param width Width
+
+   \return Calculated height
+*/
+double QwtRichTextEngine::heightForWidth( const QFont& font, int flags,
+        const QString& text, double width ) const
+{
+    QwtRichTextDocument doc( text, flags, font );
+
+    doc.setPageSize( QSizeF( width, QWIDGETSIZE_MAX ) );
+    return doc.documentLayout()->documentSize().height();
+}
+
+/*!
+  Returns the size, that is needed to render text
+
+  \param font Font of the text
+  \param flags Bitwise OR of the flags used like in QPainter::drawText()
+  \param text Text to be rendered
+
+  \return Calculated size
+*/
+
+QSizeF QwtRichTextEngine::textSize( const QFont &font,
+    int flags, const QString& text ) const
+{
+    QwtRichTextDocument doc( text, flags, font );
+
+    QTextOption option = doc.defaultTextOption();
+    if ( option.wrapMode() != QTextOption::NoWrap )
+    {
+        option.setWrapMode( QTextOption::NoWrap );
+        doc.setDefaultTextOption( option );
+        doc.adjustSize();
+    }
+
+    return doc.size();
+}
+
+/*!
+  Draw the text in a clipping rectangle
+
+  \param painter Painter
+  \param rect Clipping rectangle
+  \param flags Bitwise OR of the flags like in for QPainter::drawText()
+  \param text Text to be rendered
+*/
+void QwtRichTextEngine::draw( QPainter *painter, const QRectF &rect,
+    int flags, const QString& text ) const
+{
+    QwtRichTextDocument doc( text, flags, painter->font() );
+    QwtPainter::drawSimpleRichText( painter, rect, flags, doc );
+}
+
+/*!
+   Wrap text into <div align=...> </div> tags according flags
+
+   \param text Text
+   \param flags Bitwise OR of the flags like in for QPainter::drawText()
+
+   \return Tagged text
+*/
+QString QwtRichTextEngine::taggedText( const QString &text, int flags ) const
+{
+    return taggedRichText( text, flags );
+}
+
+/*!
+  Test if a string can be rendered by this text engine
+
+  \param text Text to be tested
+  \return Qt::mightBeRichText(text);
+*/
+bool QwtRichTextEngine::mightRender( const QString &text ) const
+{
+    return Qt::mightBeRichText( text );
+}
+
+/*!
+  Return margins around the texts
+
+  \param left Return 0
+  \param right Return 0
+  \param top Return 0
+  \param bottom Return 0
+*/
+void QwtRichTextEngine::textMargins( const QFont &, const QString &,
+    double &left, double &right, double &top, double &bottom ) const
+{
+    left = right = top = bottom = 0;
+}
+
+#endif // !QT_NO_RICHTEXT
diff --git a/source/third_party/qwt/qwt_text_label.cpp b/source/third_party/qwt/qwt_text_label.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e77a3ea67bb19c5652d5fcb757430f567dd489e6
--- /dev/null
+++ b/source/third_party/qwt/qwt_text_label.cpp
@@ -0,0 +1,324 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_text_label.h"
+#include "qwt/qwt_text.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+#include <qevent.h>
+#include <qmath.h>
+
+class QwtTextLabel::PrivateData
+{
+public:
+    PrivateData():
+        indent( 4 ),
+        margin( 0 )
+    {
+    }
+
+    int indent;
+    int margin;
+    QwtText text;
+};
+
+/*!
+  Constructs an empty label.
+  \param parent Parent widget
+*/
+QwtTextLabel::QwtTextLabel( QWidget *parent ):
+    QFrame( parent )
+{
+    init();
+}
+
+/*!
+  Constructs a label that displays the text, text
+  \param parent Parent widget
+  \param text Text
+*/
+QwtTextLabel::QwtTextLabel( const QwtText &text, QWidget *parent ):
+    QFrame( parent )
+{
+    init();
+    d_data->text = text;
+}
+
+//! Destructor
+QwtTextLabel::~QwtTextLabel()
+{
+    delete d_data;
+}
+
+void QwtTextLabel::init()
+{
+    d_data = new PrivateData();
+    setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
+}
+
+/*!
+   Interface for the designer plugin - does the same as setText()
+   \sa plainText()
+ */
+void QwtTextLabel::setPlainText( const QString &text )
+{
+    setText( QwtText( text ) );
+}
+
+/*!
+   Interface for the designer plugin
+
+   \return Text as plain text
+   \sa setPlainText(), text()
+ */
+QString QwtTextLabel::plainText() const
+{
+    return d_data->text.text();
+}
+
+/*!
+   Change the label's text, keeping all other QwtText attributes
+   \param text New text
+   \param textFormat Format of text
+
+  \sa QwtText
+*/
+void QwtTextLabel::setText( const QString &text, 
+    QwtText::TextFormat textFormat )
+{
+    d_data->text.setText( text, textFormat );
+
+    update();
+    updateGeometry();
+}
+
+/*!
+   Change the label's text
+   \param text New text
+*/
+void QwtTextLabel::setText( const QwtText &text )
+{
+    d_data->text = text;
+
+    update();
+    updateGeometry();
+}
+
+//! Return the text
+const QwtText &QwtTextLabel::text() const
+{
+    return d_data->text;
+}
+
+//! Clear the text and all QwtText attributes
+void QwtTextLabel::clear()
+{
+    d_data->text = QwtText();
+
+    update();
+    updateGeometry();
+}
+
+//! Return label's text indent in pixels
+int QwtTextLabel::indent() const
+{
+    return d_data->indent;
+}
+
+/*!
+  Set label's text indent in pixels
+  \param indent Indentation in pixels
+*/
+void QwtTextLabel::setIndent( int indent )
+{
+    if ( indent < 0 )
+        indent = 0;
+
+    d_data->indent = indent;
+
+    update();
+    updateGeometry();
+}
+
+//! Return label's text margin in pixels
+int QwtTextLabel::margin() const
+{
+    return d_data->margin;
+}
+
+/*!
+  Set label's margin in pixels
+  \param margin Margin in pixels
+*/
+void QwtTextLabel::setMargin( int margin )
+{
+    d_data->margin = margin;
+
+    update();
+    updateGeometry();
+}
+
+//! Return a size hint
+QSize QwtTextLabel::sizeHint() const
+{
+    return minimumSizeHint();
+}
+
+//! Return a minimum size hint
+QSize QwtTextLabel::minimumSizeHint() const
+{
+    QSizeF sz = d_data->text.textSize( font() );
+
+    int mw = 2 * ( frameWidth() + d_data->margin );
+    int mh = mw;
+
+    int indent = d_data->indent;
+    if ( indent <= 0 )
+        indent = defaultIndent();
+
+    if ( indent > 0 )
+    {
+        const int align = d_data->text.renderFlags();
+        if ( align & Qt::AlignLeft || align & Qt::AlignRight )
+            mw += d_data->indent;
+        else if ( align & Qt::AlignTop || align & Qt::AlignBottom )
+            mh += d_data->indent;
+    }
+
+    sz += QSizeF( mw, mh );
+
+    return QSize( qCeil( sz.width() ), qCeil( sz.height() ) );
+}
+
+/*!
+   \param width Width
+   \return Preferred height for this widget, given the width.
+*/
+int QwtTextLabel::heightForWidth( int width ) const
+{
+    const int renderFlags = d_data->text.renderFlags();
+
+    int indent = d_data->indent;
+    if ( indent <= 0 )
+        indent = defaultIndent();
+
+    width -= 2 * frameWidth();
+    if ( renderFlags & Qt::AlignLeft || renderFlags & Qt::AlignRight )
+        width -= indent;
+
+    int height = qCeil( d_data->text.heightForWidth( width, font() ) );
+    if ( ( renderFlags & Qt::AlignTop ) || ( renderFlags & Qt::AlignBottom ) )
+        height += indent;
+
+    height += 2 * frameWidth();
+
+    return height;
+}
+
+/*!
+   Qt paint event
+   \param event Paint event
+*/
+void QwtTextLabel::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+
+    if ( !contentsRect().contains( event->rect() ) )
+    {
+        painter.save();
+        painter.setClipRegion( event->region() & frameRect() );
+        drawFrame( &painter );
+        painter.restore();
+    }
+
+    painter.setClipRegion( event->region() & contentsRect() );
+
+    drawContents( &painter );
+}
+
+//! Redraw the text and focus indicator
+void QwtTextLabel::drawContents( QPainter *painter )
+{
+    const QRect r = textRect();
+    if ( r.isEmpty() )
+        return;
+
+    painter->setFont( font() );
+    painter->setPen( palette().color( QPalette::Active, QPalette::Text ) );
+
+    drawText( painter, QRectF( r ) );
+
+    if ( hasFocus() )
+    {
+        const int m = 2;
+
+        QRect focusRect = contentsRect().adjusted( m, m, -m + 1, -m + 1);
+
+        QwtPainter::drawFocusRect( painter, this, focusRect );
+    }
+}
+
+//! Redraw the text
+void QwtTextLabel::drawText( QPainter *painter, const QRectF &textRect )
+{
+    d_data->text.draw( painter, textRect );
+}
+
+/*!
+  Calculate geometry for the text in widget coordinates
+  \return Geometry for the text
+*/
+QRect QwtTextLabel::textRect() const
+{
+    QRect r = contentsRect();
+
+    if ( !r.isEmpty() && d_data->margin > 0 )
+    {
+        r.setRect( r.x() + d_data->margin, r.y() + d_data->margin,
+            r.width() - 2 * d_data->margin, r.height() - 2 * d_data->margin );
+    }
+
+    if ( !r.isEmpty() )
+    {
+        int indent = d_data->indent;
+        if ( indent <= 0 )
+            indent = defaultIndent();
+
+        if ( indent > 0 )
+        {
+            const int renderFlags = d_data->text.renderFlags();
+
+            if ( renderFlags & Qt::AlignLeft )
+                r.setX( r.x() + indent );
+            else if ( renderFlags & Qt::AlignRight )
+                r.setWidth( r.width() - indent );
+            else if ( renderFlags & Qt::AlignTop )
+                r.setY( r.y() + indent );
+            else if ( renderFlags & Qt::AlignBottom )
+                r.setHeight( r.height() - indent );
+        }
+    }
+
+    return r;
+}
+
+int QwtTextLabel::defaultIndent() const
+{
+    if ( frameWidth() <= 0 )
+        return 0;
+
+    QFont fnt;
+    if ( d_data->text.testPaintAttribute( QwtText::PaintUsingTextFont ) )
+        fnt = d_data->text.font();
+    else
+        fnt = font();
+
+    return QFontMetrics( fnt ).width( 'x' ) / 2;
+}
+
diff --git a/source/third_party/qwt/qwt_thermo.cpp b/source/third_party/qwt/qwt_thermo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..71eb188fa8136b700a4b60fb086be271824c69e8
--- /dev/null
+++ b/source/third_party/qwt/qwt_thermo.cpp
@@ -0,0 +1,1005 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_thermo.h"
+#include "qwt/qwt_scale_engine.h"
+#include "qwt/qwt_scale_draw.h"
+#include "qwt/qwt_scale_map.h"
+#include "qwt/qwt_color_map.h"
+#include <qpainter.h>
+#include <qevent.h>
+#include <qdrawutil.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qmath.h>
+
+static inline void qwtDrawLine( QPainter *painter, int pos, 
+    const QColor &color, const QRect &pipeRect, const QRect &liquidRect,
+    Qt::Orientation orientation )
+{
+    painter->setPen( color );
+    if ( orientation == Qt::Horizontal )
+    {
+        if ( pos >= liquidRect.left() && pos < liquidRect.right() )
+            painter->drawLine( pos, pipeRect.top(), pos, pipeRect.bottom() );
+    }
+    else
+    {
+        if ( pos >= liquidRect.top() && pos < liquidRect.bottom() )
+            painter->drawLine( pipeRect.left(), pos, pipeRect.right(), pos );
+    }
+}
+
+QVector<double> qwtTickList( const QwtScaleDiv &scaleDiv )
+{
+    QVector<double> values;
+
+    double lowerLimit = scaleDiv.interval().minValue();
+    double upperLimit = scaleDiv.interval().maxValue();
+
+    if ( upperLimit < lowerLimit )
+        qSwap( lowerLimit, upperLimit );
+
+    values += lowerLimit;
+
+    for ( int tickType = QwtScaleDiv::MinorTick;
+        tickType < QwtScaleDiv::NTickTypes; tickType++ )
+    {
+        const QList<double> ticks = scaleDiv.ticks( tickType );
+
+        for ( int i = 0; i < ticks.count(); i++ )
+        {
+            const double v = ticks[i];
+            if ( v > lowerLimit && v < upperLimit )
+                values += v;
+        }       
+    }   
+
+    values += upperLimit;
+    
+    return values;
+}
+
+class QwtThermo::PrivateData
+{
+public:
+    PrivateData():
+        orientation( Qt::Vertical ),
+        scalePosition( QwtThermo::TrailingScale ),
+        spacing( 3 ),
+        borderWidth( 2 ),
+        pipeWidth( 10 ),
+        alarmLevel( 0.0 ),
+        alarmEnabled( false ),
+        autoFillPipe( true ),
+        originMode( QwtThermo::OriginMinimum ),
+        origin( 0.0 ),
+        colorMap( NULL ),
+        value( 0.0 )
+    {
+        rangeFlags = QwtInterval::IncludeBorders;
+    }
+
+    ~PrivateData()
+    {
+        delete colorMap;
+    }
+
+    Qt::Orientation orientation;
+    QwtThermo::ScalePosition scalePosition;
+
+    int spacing;
+    int borderWidth;
+    int pipeWidth;
+
+    QwtInterval::BorderFlags rangeFlags;
+    double alarmLevel;
+    bool alarmEnabled;
+    bool autoFillPipe;
+    QwtThermo::OriginMode originMode;
+    double origin;
+
+    QwtColorMap *colorMap;
+
+    double value;
+};
+
+/*!
+  Constructor
+  \param parent Parent widget
+*/
+QwtThermo::QwtThermo( QWidget *parent ):
+    QwtAbstractScale( parent )
+{
+    d_data = new PrivateData;
+
+    QSizePolicy policy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
+    if ( d_data->orientation == Qt::Vertical )
+        policy.transpose();
+
+    setSizePolicy( policy );
+
+    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    layoutThermo( true );
+}
+
+//! Destructor
+QwtThermo::~QwtThermo()
+{
+    delete d_data;
+}
+
+/*!
+  \brief Exclude/Include min/max values
+
+  According to the flags minValue() and maxValue()
+  are included/excluded from the pipe. In case of an
+  excluded value the corresponding tick is painted
+  1 pixel off of the pipeRect().
+
+  F.e. when a minimum
+  of 0.0 has to be displayed as an empty pipe the minValue()
+  needs to be excluded.
+
+  \param flags Range flags
+  \sa rangeFlags()
+*/
+void QwtThermo::setRangeFlags( QwtInterval::BorderFlags flags )
+{
+    if ( d_data->rangeFlags != flags )
+    {
+        d_data->rangeFlags = flags;
+        update();
+    }
+}
+
+/*!
+  \return Range flags
+  \sa setRangeFlags()
+*/
+QwtInterval::BorderFlags QwtThermo::rangeFlags() const
+{
+    return d_data->rangeFlags;
+}
+
+/*!
+  Set the current value.
+
+  \param value New Value
+  \sa value()
+*/
+void QwtThermo::setValue( double value )
+{
+    if ( d_data->value != value )
+    {
+        d_data->value = value;
+        update();
+    }
+}
+
+//! Return the value.
+double QwtThermo::value() const
+{
+    return d_data->value;
+}
+
+/*!
+  \brief Set a scale draw
+
+  For changing the labels of the scales, it
+  is necessary to derive from QwtScaleDraw and
+  overload QwtScaleDraw::label().
+
+  \param scaleDraw ScaleDraw object, that has to be created with
+                   new and will be deleted in ~QwtThermo() or the next
+                   call of setScaleDraw().
+*/
+void QwtThermo::setScaleDraw( QwtScaleDraw *scaleDraw )
+{
+    setAbstractScaleDraw( scaleDraw );
+    layoutThermo( true );
+}
+
+/*!
+   \return the scale draw of the thermo
+   \sa setScaleDraw()
+*/
+const QwtScaleDraw *QwtThermo::scaleDraw() const
+{
+    return static_cast<const QwtScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+   \return the scale draw of the thermo
+   \sa setScaleDraw()
+*/
+QwtScaleDraw *QwtThermo::scaleDraw()
+{
+    return static_cast<QwtScaleDraw *>( abstractScaleDraw() );
+}
+
+/*!
+  Paint event handler
+  \param event Paint event
+*/
+void QwtThermo::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    const QRect tRect = pipeRect();
+
+    if ( !tRect.contains( event->rect() ) )
+    {
+        if ( d_data->scalePosition != QwtThermo::NoScale )
+            scaleDraw()->draw( &painter, palette() );
+    }
+
+    const int bw = d_data->borderWidth;
+
+    const QBrush brush = palette().brush( QPalette::Base );
+    qDrawShadePanel( &painter, 
+        tRect.adjusted( -bw, -bw, bw, bw ),
+        palette(), true, bw, 
+        d_data->autoFillPipe ? &brush : NULL );
+
+    drawLiquid( &painter, tRect );
+}
+
+/*! 
+  Resize event handler
+  \param event Resize event
+*/
+void QwtThermo::resizeEvent( QResizeEvent *event )
+{
+    Q_UNUSED( event );
+    layoutThermo( false );
+}
+
+/*! 
+  Qt change event handler
+  \param event Event
+*/
+void QwtThermo::changeEvent( QEvent *event )
+{
+    switch( event->type() )
+    {
+        case QEvent::StyleChange:
+        case QEvent::FontChange:
+        {
+            layoutThermo( true );
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*!
+  Recalculate the QwtThermo geometry and layout based on
+  pipeRect() and the fonts.
+
+  \param update_geometry notify the layout system and call update
+         to redraw the scale
+*/
+void QwtThermo::layoutThermo( bool update_geometry )
+{
+    const QRect tRect = pipeRect();
+    const int bw = d_data->borderWidth + d_data->spacing;
+    const bool inverted = ( upperBound() < lowerBound() );
+
+    int from, to;
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        from = tRect.left();
+        to = tRect.right();
+
+        if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum )
+        {
+            if ( inverted )
+                to++;
+            else
+                from--;
+        }
+        if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum )
+        {
+            if ( inverted )
+                from--;
+            else
+                to++;
+        }
+
+        if ( d_data->scalePosition == QwtThermo::TrailingScale )
+        {
+            scaleDraw()->setAlignment( QwtScaleDraw::TopScale );
+            scaleDraw()->move( from, tRect.top() - bw );
+        }
+        else
+        {
+            scaleDraw()->setAlignment( QwtScaleDraw::BottomScale );
+            scaleDraw()->move( from, tRect.bottom() + bw );
+        }
+
+        scaleDraw()->setLength( qMax( to - from, 0 ) );
+    }
+    else // Qt::Vertical
+    {
+        from = tRect.top();
+        to = tRect.bottom();
+
+        if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum )
+        {
+            if ( inverted )
+                from--;
+            else
+                to++;
+        }
+        if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum )
+        {
+            if ( inverted )
+                to++;
+            else
+                from--;
+        }
+
+        if ( d_data->scalePosition == QwtThermo::LeadingScale )
+        {
+            scaleDraw()->setAlignment( QwtScaleDraw::RightScale );
+            scaleDraw()->move( tRect.right() + bw, from );
+        }
+        else
+        {
+            scaleDraw()->setAlignment( QwtScaleDraw::LeftScale );
+            scaleDraw()->move( tRect.left() - bw, from );
+        }
+
+        scaleDraw()->setLength( qMax( to - from, 0 ) );
+    }
+
+    if ( update_geometry )
+    {
+        updateGeometry();
+        update();
+    }
+}
+
+/*!
+  \return Bounding rectangle of the pipe ( without borders )
+          in widget coordinates
+*/
+QRect QwtThermo::pipeRect() const
+{
+    int mbd = 0;
+    if ( d_data->scalePosition != QwtThermo::NoScale )
+    {
+        int d1, d2;
+        scaleDraw()->getBorderDistHint( font(), d1, d2 );
+        mbd = qMax( d1, d2 );
+    }
+    const int bw = d_data->borderWidth;
+    const int scaleOff = bw + mbd;
+
+    const QRect cr = contentsRect();
+
+    QRect pipeRect = cr;
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        pipeRect.adjust( scaleOff, 0, -scaleOff, 0 );
+
+        if ( d_data->scalePosition == QwtThermo::TrailingScale )
+            pipeRect.setTop( cr.top() + cr.height() - bw - d_data->pipeWidth );
+        else
+            pipeRect.setTop( bw );
+
+        pipeRect.setHeight( d_data->pipeWidth );
+    }
+    else // Qt::Vertical
+    {
+        pipeRect.adjust( 0, scaleOff, 0, -scaleOff );
+
+        if ( d_data->scalePosition == QwtThermo::LeadingScale )
+            pipeRect.setLeft( bw );
+        else 
+            pipeRect.setLeft( cr.left() + cr.width() - bw - d_data->pipeWidth );
+
+        pipeRect.setWidth( d_data->pipeWidth );
+    }
+
+    return pipeRect;
+}
+
+/*!
+  \brief Set the orientation.
+  \param orientation Allowed values are Qt::Horizontal and Qt::Vertical.
+
+  \sa orientation(), scalePosition()
+*/
+void QwtThermo::setOrientation( Qt::Orientation orientation )
+{
+    if ( orientation == d_data->orientation )
+        return;
+
+    d_data->orientation = orientation;
+
+    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
+    {
+        QSizePolicy sp = sizePolicy();
+        sp.transpose();
+        setSizePolicy( sp );
+
+        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    }
+
+    layoutThermo( true );
+}
+
+/*!
+  \return Orientation
+  \sa setOrientation()
+*/
+Qt::Orientation QwtThermo::orientation() const
+{
+    return d_data->orientation;
+}
+
+/*!
+  \brief Change how the origin is determined.
+  \sa originMode(), serOrigin(), origin()
+ */
+void QwtThermo::setOriginMode( OriginMode m )
+{
+    if ( m == d_data->originMode )
+        return;
+
+    d_data->originMode = m;
+    update();
+}
+
+/*!
+  \return Mode, how the origin is determined.
+  \sa setOriginMode(), serOrigin(), origin()
+ */
+QwtThermo::OriginMode QwtThermo::originMode() const
+{
+    return d_data->originMode;
+}
+
+/*!
+  \brief Specifies the custom origin.
+
+  If originMode is set to OriginCustom this property controls where the
+  liquid starts.
+
+  \param origin New origin level
+  \sa setOriginMode(), originMode(), origin()
+ */
+void QwtThermo::setOrigin( double origin )
+{
+    if ( origin == d_data->origin )
+        return;
+
+    d_data->origin = origin;
+    update();
+}
+
+/*!
+  \return Origin of the thermo, when OriginCustom is enabled
+  \sa setOrigin(), setOriginMode(), originMode()
+ */
+double QwtThermo::origin() const
+{
+    return d_data->origin;
+}
+
+/*!
+  \brief Change the position of the scale
+  \param scalePosition Position of the scale.
+
+  \sa ScalePosition, scalePosition()
+*/
+void QwtThermo::setScalePosition( ScalePosition scalePosition )
+{
+    if ( d_data->scalePosition == scalePosition )
+        return;
+
+    d_data->scalePosition = scalePosition;
+
+    if ( testAttribute( Qt::WA_WState_Polished ) )
+        layoutThermo( true );
+}
+
+/*!
+   \return Scale position.
+   \sa setScalePosition()
+*/
+QwtThermo::ScalePosition QwtThermo::scalePosition() const
+{
+    return d_data->scalePosition;
+}
+
+//! Notify a scale change.
+void QwtThermo::scaleChange()
+{
+    layoutThermo( true );
+}
+
+/*!
+   Redraw the liquid in thermometer pipe.
+   \param painter Painter
+   \param pipeRect Bounding rectangle of the pipe without borders
+*/
+void QwtThermo::drawLiquid( 
+    QPainter *painter, const QRect &pipeRect ) const
+{
+    painter->save();
+    painter->setClipRect( pipeRect, Qt::IntersectClip );
+    painter->setPen( Qt::NoPen );
+
+    const QwtScaleMap scaleMap = scaleDraw()->scaleMap();
+
+    QRect liquidRect = fillRect( pipeRect );
+
+    if ( d_data->colorMap != NULL )
+    {
+        const QwtInterval interval = scaleDiv().interval().normalized();
+
+        // Because the positions of the ticks are rounded
+        // we calculate the colors for the rounded tick values
+
+        QVector<double> values = qwtTickList( scaleDraw()->scaleDiv() );
+
+        if ( scaleMap.isInverting() )
+            qSort( values.begin(), values.end(), qGreater<double>() );
+        else
+            qSort( values.begin(), values.end(), qLess<double>() );
+
+        int from;
+        if ( !values.isEmpty() )
+        {
+            from = qRound( scaleMap.transform( values[0] ) );
+            qwtDrawLine( painter, from,
+                d_data->colorMap->color( interval, values[0] ),
+                pipeRect, liquidRect, d_data->orientation );
+        }
+
+        for ( int i = 1; i < values.size(); i++ )
+        {
+            const int to = qRound( scaleMap.transform( values[i] ) );
+
+            for ( int pos = from + 1; pos < to; pos++ )
+            {
+                const double v = scaleMap.invTransform( pos );
+
+                qwtDrawLine( painter, pos, 
+                    d_data->colorMap->color( interval, v ),
+                    pipeRect, liquidRect, d_data->orientation );
+            }
+
+            qwtDrawLine( painter, to,
+                d_data->colorMap->color( interval, values[i] ),
+                pipeRect, liquidRect, d_data->orientation );
+
+            from = to;
+        }
+    }
+    else
+    {
+        if ( !liquidRect.isEmpty() && d_data->alarmEnabled )
+        {
+            const QRect r = alarmRect( liquidRect );
+            if ( !r.isEmpty() )
+            {
+                painter->fillRect( r, palette().brush( QPalette::Highlight ) );
+                liquidRect = QRegion( liquidRect ).subtracted( r ).boundingRect();
+            }
+        }
+
+        painter->fillRect( liquidRect, palette().brush( QPalette::ButtonText ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+  \brief Change the spacing between pipe and scale
+
+  A spacing of 0 means, that the backbone of the scale is below
+  the pipe.
+
+  The default setting is 3 pixels.
+
+  \param spacing Number of pixels
+  \sa spacing();
+*/
+void QwtThermo::setSpacing( int spacing )
+{
+    if ( spacing <= 0 )
+        spacing = 0;
+
+    if ( spacing != d_data->spacing  )
+    {
+        d_data->spacing = spacing;
+        layoutThermo( true );
+    }
+}
+
+/*!
+  \return Number of pixels between pipe and scale
+  \sa setSpacing()
+*/
+int QwtThermo::spacing() const
+{
+    return d_data->spacing;
+}
+
+/*!
+   Set the border width of the pipe.
+   \param width Border width
+   \sa borderWidth()
+*/
+void QwtThermo::setBorderWidth( int width )
+{
+    if ( width <= 0 )
+        width = 0;
+
+    if ( width != d_data->borderWidth  )
+    {
+        d_data->borderWidth = width;
+        layoutThermo( true );
+    }
+}
+
+/*!
+   \return Border width of the thermometer pipe.
+   \sa setBorderWidth()
+*/
+int QwtThermo::borderWidth() const
+{
+    return d_data->borderWidth;
+}
+
+/*!
+  \brief Assign a color map for the fill color
+
+  \param colorMap Color map
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+void QwtThermo::setColorMap( QwtColorMap *colorMap )
+{
+    if ( colorMap != d_data->colorMap )
+    {
+        delete d_data->colorMap;
+        d_data->colorMap = colorMap;
+    }
+}
+
+/*!
+  \return Color map for the fill color
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+QwtColorMap *QwtThermo::colorMap()
+{
+    return d_data->colorMap;
+}
+
+/*!
+  \return Color map for the fill color
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+const QwtColorMap *QwtThermo::colorMap() const
+{
+    return d_data->colorMap;
+}
+
+/*!
+  \brief Change the brush of the liquid.
+ 
+  Changes the QPalette::ButtonText brush of the palette.
+
+  \param brush New brush. 
+  \sa fillBrush(), QWidget::setPalette()
+*/
+void QwtThermo::setFillBrush( const QBrush& brush )
+{
+    QPalette pal = palette();
+    pal.setBrush( QPalette::ButtonText, brush );
+    setPalette( pal );
+}
+
+/*!
+  \return Liquid ( QPalette::ButtonText ) brush. 
+  \sa setFillBrush(), QWidget::palette()
+*/
+QBrush QwtThermo::fillBrush() const
+{
+    return palette().brush( QPalette::ButtonText );
+}
+
+/*!
+  \brief Specify the liquid brush above the alarm threshold
+
+  Changes the QPalette::Highlight brush of the palette.
+
+  \param brush New brush. 
+  \sa alarmBrush(), QWidget::setPalette()
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+void QwtThermo::setAlarmBrush( const QBrush& brush )
+{
+    QPalette pal = palette();
+    pal.setBrush( QPalette::Highlight, brush );
+    setPalette( pal );
+}
+
+/*!
+  \return Liquid brush ( QPalette::Highlight ) above the alarm threshold.
+  \sa setAlarmBrush(), QWidget::palette()
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+QBrush QwtThermo::alarmBrush() const
+{
+    return palette().brush( QPalette::Highlight );
+}
+
+/*!
+  Specify the alarm threshold.
+
+  \param level Alarm threshold
+  \sa alarmLevel()
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+void QwtThermo::setAlarmLevel( double level )
+{
+    d_data->alarmLevel = level;
+    d_data->alarmEnabled = 1;
+    update();
+}
+
+/*!
+  \return Alarm threshold.
+  \sa setAlarmLevel()
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+double QwtThermo::alarmLevel() const
+{
+    return d_data->alarmLevel;
+}
+
+/*!
+  Change the width of the pipe.
+
+  \param width Width of the pipe
+  \sa pipeWidth()
+*/
+void QwtThermo::setPipeWidth( int width )
+{
+    if ( width > 0 )
+    {
+        d_data->pipeWidth = width;
+        layoutThermo( true );
+    }
+}
+
+/*!
+  \return Width of the pipe.
+  \sa setPipeWidth()
+*/
+int QwtThermo::pipeWidth() const
+{
+    return d_data->pipeWidth;
+}
+
+/*!
+  \brief Enable or disable the alarm threshold
+  \param on true (disabled) or false (enabled)
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+void QwtThermo::setAlarmEnabled( bool on )
+{
+    d_data->alarmEnabled = on;
+    update();
+}
+
+/*! 
+  \return True, when the alarm threshold is enabled.
+
+  \warning The alarm threshold has no effect, when
+           a color map has been assigned
+*/
+bool QwtThermo::alarmEnabled() const
+{
+    return d_data->alarmEnabled;
+}
+
+/*!
+  \return the minimum size hint
+  \sa minimumSizeHint()
+*/
+QSize QwtThermo::sizeHint() const
+{
+    return minimumSizeHint();
+}
+
+/*!
+  \return Minimum size hint
+  \warning The return value depends on the font and the scale.
+  \sa sizeHint()
+*/
+QSize QwtThermo::minimumSizeHint() const
+{
+    int w = 0, h = 0;
+
+    if ( d_data->scalePosition != NoScale )
+    {
+        const int sdExtent = qCeil( scaleDraw()->extent( font() ) );
+        const int sdLength = scaleDraw()->minLength( font() );
+
+        w = sdLength;
+        h = d_data->pipeWidth + sdExtent + d_data->spacing;
+
+    }
+    else // no scale
+    {
+        w = 200;
+        h = d_data->pipeWidth;
+    }
+
+    if ( d_data->orientation == Qt::Vertical )
+        qSwap( w, h );
+
+    w += 2 * d_data->borderWidth;
+    h += 2 * d_data->borderWidth;
+
+    // finally add the margins
+    int left, right, top, bottom;
+    getContentsMargins( &left, &top, &right, &bottom );
+    w += left + right;
+    h += top + bottom;
+
+    return QSize( w, h );
+}
+
+/*!
+  \brief Calculate the filled rectangle of the pipe
+
+  \param pipeRect Rectangle of the pipe
+  \return Rectangle to be filled ( fill and alarm brush )
+
+  \sa pipeRect(), alarmRect()
+ */
+QRect QwtThermo::fillRect( const QRect &pipeRect ) const
+{
+    double origin;        
+    if ( d_data->originMode == OriginMinimum )
+    {
+        origin = qMin( lowerBound(), upperBound() );
+    }
+    else if ( d_data->originMode == OriginMaximum )
+    {
+        origin = qMax( lowerBound(), upperBound() );
+    }
+    else // OriginCustom
+    {
+        origin = d_data->origin;
+    }
+
+    const QwtScaleMap scaleMap = scaleDraw()->scaleMap();
+
+    int from = qRound( scaleMap.transform( d_data->value ) );
+    int to = qRound( scaleMap.transform( origin ) );
+
+    if ( to < from )
+        qSwap( from, to );
+    
+    QRect fillRect = pipeRect;
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        fillRect.setLeft( from );
+        fillRect.setRight( to );
+    }
+    else // Qt::Vertical
+    {
+        fillRect.setTop( from );
+        fillRect.setBottom( to );
+    }
+
+    return fillRect.normalized();
+}
+
+/*!
+  \brief Calculate the alarm rectangle of the pipe
+
+  \param fillRect Filled rectangle in the pipe
+  \return Rectangle to be filled with the alarm brush
+
+  \sa pipeRect(), fillRect(), alarmLevel(), alarmBrush()
+ */
+QRect QwtThermo::alarmRect( const QRect &fillRect ) const
+{
+    QRect alarmRect( 0, 0, -1, -1); // something invalid
+
+    if ( !d_data->alarmEnabled )
+        return alarmRect;
+
+    const bool inverted = ( upperBound() < lowerBound() );
+    
+    bool increasing;
+    if ( d_data->originMode == OriginCustom )
+    {
+        increasing = d_data->value > d_data->origin;
+    }
+    else
+    {
+        increasing = d_data->originMode == OriginMinimum;
+    }
+
+    const QwtScaleMap map = scaleDraw()->scaleMap();
+    const int alarmPos = qRound( map.transform( d_data->alarmLevel ) );
+    const int valuePos = qRound( map.transform( d_data->value ) );
+    
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        int v1, v2;
+        if ( inverted )
+        {
+            v1 = fillRect.left();
+
+            v2 = alarmPos - 1;
+            v2 = qMin( v2, increasing ? fillRect.right() : valuePos );
+        }
+        else
+        {
+            v1 = alarmPos + 1;
+            v1 = qMax( v1, increasing ? fillRect.left() : valuePos );
+
+            v2 = fillRect.right();
+
+        }
+        alarmRect.setRect( v1, fillRect.top(), v2 - v1 + 1, fillRect.height() );
+    }
+    else
+    {
+        int v1, v2;
+        if ( inverted )
+        {
+            v1 = alarmPos + 1;
+            v1 = qMax( v1, increasing ? fillRect.top() : valuePos );
+
+            v2 = fillRect.bottom();
+        }
+        else
+        {
+            v1 = fillRect.top();
+
+            v2 = alarmPos - 1;
+            v2 = qMin( v2, increasing ? fillRect.bottom() : valuePos );
+        }
+        alarmRect.setRect( fillRect.left(), v1, fillRect.width(), v2 - v1 + 1 );
+    }
+
+    return alarmRect;
+} 
diff --git a/source/third_party/qwt/qwt_transform.cpp b/source/third_party/qwt/qwt_transform.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9395854474179e0d7d351f64071894312abfb234
--- /dev/null
+++ b/source/third_party/qwt/qwt_transform.cpp
@@ -0,0 +1,177 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_transform.h"
+#include "qwt/qwt_math.h"
+
+#if QT_VERSION < 0x040601
+#define qExp(x) ::exp(x)
+#endif
+
+#if QT_VERSION >= 0x050400
+
+//! Smallest allowed value for logarithmic scales: 1.0e-150
+const double QwtLogTransform::LogMin = 1.0e-150;
+    
+//! Largest allowed value for logarithmic scales: 1.0e150
+const double QwtLogTransform::LogMax = 1.0e150;
+
+#else
+
+//! Smallest allowed value for logarithmic scales: 1.0e-150
+QT_STATIC_CONST_IMPL double QwtLogTransform::LogMin = 1.0e-150;
+
+//! Largest allowed value for logarithmic scales: 1.0e150
+QT_STATIC_CONST_IMPL double QwtLogTransform::LogMax = 1.0e150;
+
+#endif
+
+//! Constructor
+QwtTransform::QwtTransform()
+{
+}
+
+//! Destructor
+QwtTransform::~QwtTransform()
+{
+}
+
+/*! 
+  \param value Value to be bounded
+  \return value unmodified
+ */
+double QwtTransform::bounded( double value ) const
+{
+    return value;
+}
+
+//! Constructor
+QwtNullTransform::QwtNullTransform():
+    QwtTransform()
+{
+}
+
+//! Destructor
+QwtNullTransform::~QwtNullTransform()
+{
+}
+
+/*! 
+  \param value Value to be transformed
+  \return value unmodified
+ */
+double QwtNullTransform::transform( double value ) const
+{
+    return value;
+}
+
+/*! 
+  \param value Value to be transformed
+  \return value unmodified
+ */
+double QwtNullTransform::invTransform( double value ) const
+{
+    return value;
+}
+
+//! \return Clone of the transformation
+QwtTransform *QwtNullTransform::copy() const
+{
+    return new QwtNullTransform();
+}
+
+//! Constructor
+QwtLogTransform::QwtLogTransform():
+    QwtTransform()
+{
+}
+
+//! Destructor
+QwtLogTransform::~QwtLogTransform()
+{
+}
+
+/*! 
+  \param value Value to be transformed
+  \return log( value )
+ */
+double QwtLogTransform::transform( double value ) const
+{
+    return ::log( value );
+}
+
+/*! 
+  \param value Value to be transformed
+  \return exp( value )
+ */
+double QwtLogTransform::invTransform( double value ) const
+{
+    return qExp( value );
+}
+
+/*! 
+  \param value Value to be bounded
+  \return qBound( LogMin, value, LogMax )
+ */
+double QwtLogTransform::bounded( double value ) const
+{
+    return qBound( LogMin, value, LogMax );
+}
+
+//! \return Clone of the transformation
+QwtTransform *QwtLogTransform::copy() const
+{
+    return new QwtLogTransform();
+}
+
+/*!
+  Constructor
+  \param exponent Exponent
+*/
+QwtPowerTransform::QwtPowerTransform( double exponent ):
+    QwtTransform(),
+    d_exponent( exponent )
+{
+}
+
+//! Destructor
+QwtPowerTransform::~QwtPowerTransform()
+{
+}
+
+/*! 
+  \param value Value to be transformed
+  \return Exponentiation preserving the sign
+ */
+double QwtPowerTransform::transform( double value ) const
+{
+    if ( value < 0.0 )
+        return -qPow( -value, 1.0 / d_exponent );
+    else
+        return qPow( value, 1.0 / d_exponent );
+    
+}
+
+/*! 
+  \param value Value to be transformed
+  \return Inverse exponentiation preserving the sign
+ */
+double QwtPowerTransform::invTransform( double value ) const
+{
+    if ( value < 0.0 )
+        return -qPow( -value, d_exponent );
+    else
+        return qPow( value, d_exponent );
+}
+
+//! \return Clone of the transformation
+QwtTransform *QwtPowerTransform::copy() const
+{
+    return new QwtPowerTransform( d_exponent );
+}
diff --git a/source/third_party/qwt/qwt_wheel.cpp b/source/third_party/qwt/qwt_wheel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3ea3fc6483550b379fdbebd406ab4775e72fba0
--- /dev/null
+++ b/source/third_party/qwt/qwt_wheel.cpp
@@ -0,0 +1,1299 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_wheel.h"
+#include "qwt/qwt_math.h"
+#include "qwt/qwt_painter.h"
+#include <qevent.h>
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qapplication.h>
+#include <qdatetime.h>
+
+#if QT_VERSION < 0x040601
+#define qFabs(x) ::fabs(x)
+#define qFastSin(x) ::sin(x)
+#define qExp(x) ::exp(x)
+#endif
+
+class QwtWheel::PrivateData
+{
+public:
+    PrivateData():
+        orientation( Qt::Horizontal ),
+        viewAngle( 175.0 ),
+        totalAngle( 360.0 ),
+        tickCount( 10 ),
+        wheelBorderWidth( 2 ),
+        borderWidth( 2 ),
+        wheelWidth( 20 ),
+        isScrolling( false ),
+        mouseOffset( 0.0 ),
+        tracking( true ),
+        pendingValueChanged( false ),
+        updateInterval( 50 ),
+        mass( 0.0 ),
+        timerId( 0 ),
+        speed( 0.0 ),
+        mouseValue( 0.0 ),
+        flyingValue( 0.0 ),
+        minimum( 0.0 ),
+        maximum( 100.0 ),
+        singleStep( 1.0 ),
+        pageStepCount( 1 ),
+        stepAlignment( true ),
+        value( 0.0 ),
+        inverted( false ),
+        wrapping( false )
+    {
+    };
+
+    Qt::Orientation orientation;
+    double viewAngle;
+    double totalAngle;
+    int tickCount;
+    int wheelBorderWidth;
+    int borderWidth;
+    int wheelWidth;
+
+    bool isScrolling;
+    double mouseOffset;
+
+    bool tracking;
+    bool pendingValueChanged; // when not tracking
+
+    int updateInterval;
+    double mass;
+
+    // for the flying wheel effect
+    int timerId;
+    QTime time;
+    double speed;
+    double mouseValue;
+    double flyingValue;
+
+    double minimum;
+    double maximum;
+
+    double singleStep;
+    int pageStepCount;
+    bool stepAlignment;
+
+    double value;
+
+    bool inverted;
+    bool wrapping;
+};
+
+//! Constructor
+QwtWheel::QwtWheel( QWidget *parent ):
+    QWidget( parent )
+{
+    d_data = new PrivateData;
+
+    setFocusPolicy( Qt::StrongFocus );
+    setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
+    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+}
+
+//! Destructor
+QwtWheel::~QwtWheel()
+{
+    delete d_data;
+}
+
+/*!
+  \brief En/Disable tracking
+
+  If tracking is enabled (the default), the wheel emits the valueChanged() 
+  signal while the wheel is moving. If tracking is disabled, the wheel 
+  emits the valueChanged() signal only when the wheel movement is terminated.
+
+  The wheelMoved() signal is emitted regardless id tracking is enabled or not.
+
+  \param enable On/Off
+  \sa isTracking()
+ */
+void QwtWheel::setTracking( bool enable )
+{
+    d_data->tracking = enable;
+}
+
+/*!
+  \return True, when tracking is enabled
+  \sa setTracking(), valueChanged(), wheelMoved()
+*/
+bool QwtWheel::isTracking() const
+{
+    return d_data->tracking;
+}
+
+/*!
+  \brief Specify the update interval when the wheel is flying
+
+  Default and minimum value is 50 ms.
+
+  \param interval Interval in milliseconds
+  \sa updateInterval(), setMass(), setTracking()
+*/
+void QwtWheel::setUpdateInterval( int interval )
+{
+    d_data->updateInterval = qMax( interval, 50 );
+}
+
+/*!
+  \return Update interval when the wheel is flying
+  \sa setUpdateInterval(), mass(), isTracking()
+ */
+int QwtWheel::updateInterval() const
+{
+    return d_data->updateInterval;
+}
+
+/*!
+   \brief Mouse press event handler
+
+   Start movement of the wheel. 
+
+   \param event Mouse event
+*/
+void QwtWheel::mousePressEvent( QMouseEvent *event )
+{
+    stopFlying();
+
+    d_data->isScrolling = wheelRect().contains( event->pos() );
+
+    if ( d_data->isScrolling )
+    {
+        d_data->time.start();
+        d_data->speed = 0.0;
+        d_data->mouseValue = valueAt( event->pos() );
+        d_data->mouseOffset = d_data->mouseValue - d_data->value;
+        d_data->pendingValueChanged = false;
+
+        Q_EMIT wheelPressed();
+    }
+}
+
+/*!
+   \brief Mouse Move Event handler
+
+   Turn the wheel according to the mouse position
+
+   \param event Mouse event
+*/
+void QwtWheel::mouseMoveEvent( QMouseEvent *event )
+{
+    if ( !d_data->isScrolling )
+        return;
+
+    double mouseValue = valueAt( event->pos() );
+
+    if ( d_data->mass > 0.0 )
+    {
+        double ms = d_data->time.restart();
+
+        // the interval when mouse move events are posted are somehow
+        // random. To avoid unrealistic speed values we limit ms
+
+        ms = qMax( ms, 5.0 );
+
+        d_data->speed = ( mouseValue - d_data->mouseValue ) / ms;
+    }
+    
+    d_data->mouseValue = mouseValue; 
+
+    double value = boundedValue( mouseValue - d_data->mouseOffset );
+    if ( d_data->stepAlignment )
+        value = alignedValue( value );
+        
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+
+        update();
+
+        Q_EMIT wheelMoved( d_data->value );
+
+        if ( d_data->tracking )
+            Q_EMIT valueChanged( d_data->value );
+        else
+            d_data->pendingValueChanged = true;
+    }
+}
+
+/*!
+   \brief Mouse Release Event handler
+
+   When the wheel has no mass the movement of the wheel stops, otherwise
+   it starts flying.
+
+   \param event Mouse event
+*/  
+
+void QwtWheel::mouseReleaseEvent( QMouseEvent *event )
+{
+    Q_UNUSED( event );
+
+    if ( !d_data->isScrolling )
+        return;
+
+    d_data->isScrolling = false;
+
+    bool startFlying = false;
+
+    if ( d_data->mass > 0.0 )
+    {
+        const int ms = d_data->time.elapsed();
+        if ( ( qFabs( d_data->speed ) > 0.0 ) && ( ms < 50 ) )
+            startFlying = true;
+    }
+
+    if ( startFlying )
+    {
+        d_data->flyingValue = 
+            boundedValue( d_data->mouseValue - d_data->mouseOffset );
+
+        d_data->timerId = startTimer( d_data->updateInterval );
+    }
+    else
+    {
+        if ( d_data->pendingValueChanged )
+            Q_EMIT valueChanged( d_data->value );
+    }
+
+    d_data->pendingValueChanged = false;
+    d_data->mouseOffset = 0.0;
+
+    Q_EMIT wheelReleased();
+}
+
+/*!
+  \brief Qt timer event
+
+  The flying wheel effect is implemented using a timer
+   
+  \param event Timer event
+
+  \sa updateInterval()
+ */
+void QwtWheel::timerEvent( QTimerEvent *event )
+{
+    if ( event->timerId() != d_data->timerId )
+    {
+        QWidget::timerEvent( event );
+        return;
+    }
+
+    d_data->speed *= qExp( -d_data->updateInterval * 0.001 / d_data->mass );
+
+    d_data->flyingValue += d_data->speed * d_data->updateInterval;
+    d_data->flyingValue = boundedValue( d_data->flyingValue );
+
+    double value = d_data->flyingValue;
+    if ( d_data->stepAlignment )
+        value = alignedValue( value );
+
+    if ( qFabs( d_data->speed ) < 0.001 * d_data->singleStep )
+    {
+        // stop if d_data->speed < one step per second
+        stopFlying();
+    }
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        update();
+
+        if ( d_data->tracking || d_data->timerId == 0 )
+            Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+
+/*!
+  \brief Handle wheel events
+
+  In/Decrement the value 
+
+  \param event Wheel event
+*/
+void QwtWheel::wheelEvent( QWheelEvent *event )
+{
+    if ( !wheelRect().contains( event->pos() ) )
+    {
+        event->ignore();
+        return;
+    }
+
+    if ( d_data->isScrolling )
+        return;
+
+    stopFlying();
+
+    double increment = 0.0;
+
+    if ( ( event->modifiers() & Qt::ControlModifier) || 
+        ( event->modifiers() & Qt::ShiftModifier ) )
+    {
+        // one page regardless of delta
+        increment = d_data->singleStep * d_data->pageStepCount;
+        if ( event->delta() < 0 )
+            increment = -increment;
+    }
+    else
+    {
+        const int numSteps = event->delta() / 120;
+        increment = d_data->singleStep * numSteps;
+    }
+
+    if ( d_data->orientation == Qt::Vertical && d_data->inverted )
+        increment = -increment;
+
+    double value = boundedValue( d_data->value + increment );
+
+    if ( d_data->stepAlignment )
+        value = alignedValue( value );
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        update();
+
+        Q_EMIT valueChanged( d_data->value );
+        Q_EMIT wheelMoved( d_data->value );
+    }
+}
+
+/*!
+  Handle key events
+
+  - Qt::Key_Home\n
+    Step to minimum()
+
+  - Qt::Key_End\n
+    Step to maximum()
+
+  - Qt::Key_Up\n
+    In case of a horizontal or not inverted vertical wheel the value 
+    will be incremented by the step size. For an inverted vertical wheel
+    the value will be decremented by the step size.
+
+  - Qt::Key_Down\n
+    In case of a horizontal or not inverted vertical wheel the value 
+    will be decremented by the step size. For an inverted vertical wheel
+    the value will be incremented by the step size.
+
+  - Qt::Key_PageUp\n
+    The value will be incremented by pageStepSize() * singleStepSize().
+
+  - Qt::Key_PageDown\n
+    The value will be decremented by pageStepSize() * singleStepSize().
+
+  \param event Key event
+*/
+void QwtWheel::keyPressEvent( QKeyEvent *event )
+{
+    if ( d_data->isScrolling )
+    {
+        // don't interfere mouse scrolling
+        return;
+    }
+
+    double value = d_data->value;
+    double increment = 0.0;
+
+    switch ( event->key() )
+    {
+        case Qt::Key_Down:
+        {
+            if ( d_data->orientation == Qt::Vertical && d_data->inverted )
+                increment = d_data->singleStep;
+            else
+                increment = -d_data->singleStep;
+
+            break;
+        }
+        case Qt::Key_Up:
+        {
+            if ( d_data->orientation == Qt::Vertical && d_data->inverted )
+                increment = -d_data->singleStep;
+            else
+                increment = d_data->singleStep;
+
+            break;
+        }
+        case Qt::Key_Left:
+        {
+            if ( d_data->orientation == Qt::Horizontal )
+            {
+                if ( d_data->inverted )
+                    increment = d_data->singleStep;
+                else
+                    increment = -d_data->singleStep;
+            }
+            break;
+        }
+        case Qt::Key_Right:
+        {
+            if ( d_data->orientation == Qt::Horizontal )
+            {
+                if ( d_data->inverted )
+                    increment = -d_data->singleStep;
+                else
+                    increment = d_data->singleStep;
+            }
+            break;
+        }
+        case Qt::Key_PageUp:
+        {
+            increment = d_data->pageStepCount * d_data->singleStep;
+            break;
+        }
+        case Qt::Key_PageDown:
+        {
+            increment = -d_data->pageStepCount * d_data->singleStep;
+            break;
+        }
+        case Qt::Key_Home:
+        {
+            value = d_data->minimum;
+            break;
+        }
+        case Qt::Key_End:
+        {
+            value = d_data->maximum;
+            break;
+        }
+        default:;
+        {
+            event->ignore();
+        }
+    }
+
+    if ( event->isAccepted() )
+        stopFlying();
+    
+    if ( increment != 0.0 )
+    {
+        value = boundedValue( d_data->value + increment );
+
+        if ( d_data->stepAlignment )
+            value = alignedValue( value );
+    }
+
+    if ( value != d_data->value )
+    {
+        d_data->value = value;
+        update();
+
+        Q_EMIT valueChanged( d_data->value );
+        Q_EMIT wheelMoved( d_data->value );
+    }
+}
+
+/*!
+  \brief Adjust the number of grooves in the wheel's surface.
+
+  The number of grooves is limited to 6 <= count <= 50.
+  Values outside this range will be clipped.
+  The default value is 10.
+
+  \param count Number of grooves per 360 degrees
+  \sa tickCount()
+*/
+void QwtWheel::setTickCount( int count )
+{
+    count = qBound( 6, count, 50 );
+
+    if ( count != d_data->tickCount )
+    {
+        d_data->tickCount = qBound( 6, count, 50 );
+        update();
+    }
+}
+
+/*!
+  \return Number of grooves in the wheel's surface.
+  \sa setTickCnt()
+*/
+int QwtWheel::tickCount() const
+{
+    return d_data->tickCount;
+}
+
+/*!
+  \brief Set the wheel border width of the wheel.
+
+  The wheel border must not be smaller than 1
+  and is limited in dependence on the wheel's size.
+  Values outside the allowed range will be clipped.
+
+  The wheel border defaults to 2.
+
+  \param borderWidth Border width
+  \sa internalBorder()
+*/
+void QwtWheel::setWheelBorderWidth( int borderWidth )
+{
+    const int d = qMin( width(), height() ) / 3;
+    borderWidth = qMin( borderWidth, d );
+    d_data->wheelBorderWidth = qMax( borderWidth, 1 );
+    update();
+}
+
+/*!
+   \return Wheel border width 
+   \sa setWheelBorderWidth()
+*/
+int QwtWheel::wheelBorderWidth() const
+{
+    return d_data->wheelBorderWidth;
+}
+
+/*!
+  \brief Set the border width 
+
+  The border defaults to 2.
+
+  \param width Border width
+  \sa borderWidth()
+*/
+void QwtWheel::setBorderWidth( int width )
+{
+    d_data->borderWidth = qMax( width, 0 );
+    update();
+}
+
+/*!
+   \return Border width 
+   \sa setBorderWidth()
+*/
+int QwtWheel::borderWidth() const
+{
+    return d_data->borderWidth;
+}
+
+/*!
+   \return Rectangle of the wheel without the outer border
+*/
+QRect QwtWheel::wheelRect() const
+{
+    const int bw = d_data->borderWidth;
+    return contentsRect().adjusted( bw, bw, -bw, -bw );
+}
+
+/*!
+  \brief Set the total angle which the wheel can be turned.
+
+  One full turn of the wheel corresponds to an angle of
+  360 degrees. A total angle of n*360 degrees means
+  that the wheel has to be turned n times around its axis
+  to get from the minimum value to the maximum value.
+
+  The default setting of the total angle is 360 degrees.
+
+  \param angle total angle in degrees
+  \sa totalAngle()
+*/
+void QwtWheel::setTotalAngle( double angle )
+{
+    if ( angle < 0.0 )
+        angle = 0.0;
+
+    d_data->totalAngle = angle;
+    update();
+}
+
+/*!
+  \return Total angle which the wheel can be turned.
+  \sa setTotalAngle()
+*/
+double QwtWheel::totalAngle() const
+{
+    return d_data->totalAngle;
+}
+
+/*!
+  \brief Set the wheel's orientation.
+
+  The default orientation is Qt::Horizontal.
+
+  \param orientation Qt::Horizontal or Qt::Vertical.
+  \sa orientation()
+*/
+void QwtWheel::setOrientation( Qt::Orientation orientation )
+{
+    if ( d_data->orientation == orientation )
+        return;
+
+    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
+    {
+        QSizePolicy sp = sizePolicy();
+        sp.transpose();
+        setSizePolicy( sp );
+
+        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
+    }
+
+    d_data->orientation = orientation;
+    update();
+}
+
+/*!
+  \return Orientation
+  \sa setOrientation()
+*/
+Qt::Orientation QwtWheel::orientation() const
+{
+    return d_data->orientation;
+}
+
+/*!
+  \brief Specify the visible portion of the wheel.
+
+  You may use this function for fine-tuning the appearance of
+  the wheel. The default value is 175 degrees. The value is
+  limited from 10 to 175 degrees.
+
+  \param angle Visible angle in degrees
+  \sa viewAngle(), setTotalAngle()
+*/
+void QwtWheel::setViewAngle( double angle )
+{
+    d_data->viewAngle = qBound( 10.0, angle, 175.0 );
+    update();
+}
+
+/*!
+  \return Visible portion of the wheel
+  \sa setViewAngle(), totalAngle()
+*/
+double QwtWheel::viewAngle() const
+{
+    return d_data->viewAngle;
+}
+
+/*! 
+  Determine the value corresponding to a specified point
+
+  \param pos Position
+  \return Value corresponding to pos
+*/
+double QwtWheel::valueAt( const QPoint &pos ) const
+{
+    const QRectF rect = wheelRect();
+
+    double w, dx;
+    if ( d_data->orientation == Qt::Vertical )
+    {
+        w = rect.height();
+        dx = rect.top() - pos.y();
+    }
+    else
+    {
+        w = rect.width();
+        dx = pos.x() - rect.left();
+    }
+
+    if ( w == 0.0 )
+        return 0.0;
+
+    if ( d_data->inverted )
+    {
+        dx = w - dx;
+    }
+
+    // w pixels is an arc of viewAngle degrees,
+    // so we convert change in pixels to change in angle
+    const double ang = dx * d_data->viewAngle / w;
+
+    // value range maps to totalAngle degrees,
+    // so convert the change in angle to a change in value
+    const double val = ang * ( maximum() - minimum() ) / d_data->totalAngle;
+
+    return val;
+}
+
+/*! 
+   \brief Qt Paint Event
+   \param event Paint event
+*/
+void QwtWheel::paintEvent( QPaintEvent *event )
+{
+    QPainter painter( this );
+    painter.setClipRegion( event->region() );
+
+    QStyleOption opt;
+    opt.init(this);
+    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+    qDrawShadePanel( &painter, 
+        contentsRect(), palette(), true, d_data->borderWidth );
+
+    drawWheelBackground( &painter, wheelRect() );
+    drawTicks( &painter, wheelRect() );
+
+    if ( hasFocus() )
+        QwtPainter::drawFocusRect( &painter, this );
+}
+
+/*!
+   Draw the Wheel's background gradient
+
+   \param painter Painter
+   \param rect Geometry for the wheel
+*/
+void QwtWheel::drawWheelBackground( 
+    QPainter *painter, const QRectF &rect )
+{
+    painter->save();
+
+    QPalette pal = palette();
+
+    //  draw shaded background
+    QLinearGradient gradient( rect.topLeft(), 
+        ( d_data->orientation == Qt::Horizontal ) ? rect.topRight() : rect.bottomLeft() );
+    gradient.setColorAt( 0.0, pal.color( QPalette::Button ) );
+    gradient.setColorAt( 0.2, pal.color( QPalette::Midlight ) );
+    gradient.setColorAt( 0.7, pal.color( QPalette::Mid ) );
+    gradient.setColorAt( 1.0, pal.color( QPalette::Dark ) );
+
+    painter->fillRect( rect, gradient );
+
+    // draw internal border
+
+    const QPen lightPen( palette().color( QPalette::Light ), 
+        d_data->wheelBorderWidth, Qt::SolidLine, Qt::FlatCap );
+    const QPen darkPen( pal.color( QPalette::Dark ), 
+        d_data->wheelBorderWidth, Qt::SolidLine, Qt::FlatCap );
+
+    const double bw2 = 0.5 * d_data->wheelBorderWidth;
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        painter->setPen( lightPen );
+        painter->drawLine( QPointF( rect.left(), rect.top() + bw2 ), 
+            QPointF( rect.right(), rect.top() + bw2 ) );
+
+        painter->setPen( darkPen );
+        painter->drawLine( QPointF( rect.left(), rect.bottom() - bw2 ), 
+            QPointF( rect.right(), rect.bottom() - bw2 ) );
+    }
+    else // Qt::Vertical
+    {
+        painter->setPen( lightPen );
+        painter->drawLine( QPointF( rect.left() + bw2, rect.top() ), 
+            QPointF( rect.left() + bw2, rect.bottom() ) );
+
+        painter->setPen( darkPen );
+        painter->drawLine( QPointF( rect.right() - bw2, rect.top() ), 
+            QPointF( rect.right() - bw2, rect.bottom() ) );
+    }
+
+    painter->restore();
+}
+
+/*!
+   Draw the Wheel's ticks
+
+   \param painter Painter
+   \param rect Geometry for the wheel
+*/
+void QwtWheel::drawTicks( QPainter *painter, const QRectF &rect )
+{
+    const double range = d_data->maximum - d_data->minimum;
+
+    if ( range == 0.0 || d_data->totalAngle == 0.0 )
+    {
+        return;
+    }
+
+    const QPen lightPen( palette().color( QPalette::Light ), 
+        0, Qt::SolidLine, Qt::FlatCap );
+    const QPen darkPen( palette().color( QPalette::Dark ), 
+        0, Qt::SolidLine, Qt::FlatCap );
+
+    const double cnvFactor = qAbs( d_data->totalAngle / range );
+    const double halfIntv = 0.5 * d_data->viewAngle / cnvFactor;
+    const double loValue = value() - halfIntv;
+    const double hiValue = value() + halfIntv;
+    const double tickWidth = 360.0 / double( d_data->tickCount ) / cnvFactor;
+    const double sinArc = qFastSin( d_data->viewAngle * M_PI / 360.0 );
+
+    if ( d_data->orientation == Qt::Horizontal )
+    {
+        const double radius = rect.width() * 0.5;
+
+        double l1 = rect.top() + d_data->wheelBorderWidth;
+        double l2 = rect.bottom() - d_data->wheelBorderWidth - 1;
+
+        // draw one point over the border if border > 1
+        if ( d_data->wheelBorderWidth > 1 )
+        {
+            l1--;
+            l2++;
+        }
+
+        const double maxpos = rect.right() - 2;
+        const double minpos = rect.left() + 2;
+
+        // draw tick marks
+        for ( double tickValue = ::ceil( loValue / tickWidth ) * tickWidth;
+            tickValue < hiValue; tickValue += tickWidth )
+        {
+            const double angle = qwtRadians( tickValue - value() );
+            const double s = qFastSin( angle * cnvFactor );
+
+            const double off = radius * ( sinArc + s ) / sinArc;
+
+            double tickPos;
+            if ( d_data->inverted ) 
+                tickPos = rect.left() + off;
+            else
+                tickPos = rect.right() - off;
+
+            if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) )
+            {
+                painter->setPen( darkPen );
+                painter->drawLine( QPointF( tickPos - 1 , l1 ), 
+                    QPointF( tickPos - 1,  l2 ) );
+                painter->setPen( lightPen );
+                painter->drawLine( QPointF( tickPos, l1 ), 
+                    QPointF( tickPos, l2 ) );
+            }
+        }
+    }
+    else // Qt::Vertical
+    {
+        const double radius = rect.height() * 0.5;
+
+        double l1 = rect.left() + d_data->wheelBorderWidth;
+        double l2 = rect.right() - d_data->wheelBorderWidth - 1;
+
+        if ( d_data->wheelBorderWidth > 1 )
+        {
+            l1--;
+            l2++;
+        }
+
+        const double maxpos = rect.bottom() - 2;
+        const double minpos = rect.top() + 2;
+
+        for ( double tickValue = ::ceil( loValue / tickWidth ) * tickWidth;
+            tickValue < hiValue; tickValue += tickWidth )
+        {
+            const double angle = qwtRadians( tickValue - value() );
+            const double s = qFastSin( angle * cnvFactor );
+
+            const double off = radius * ( sinArc + s ) / sinArc;
+
+            double tickPos;
+
+            if ( d_data->inverted )
+                tickPos = rect.bottom() - off;
+            else
+                tickPos = rect.top() + off;
+
+            if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) )
+            {
+                painter->setPen( darkPen );
+                painter->drawLine( QPointF( l1, tickPos - 1 ), 
+                    QPointF( l2, tickPos - 1 ) );
+                painter->setPen( lightPen );
+                painter->drawLine( QPointF( l1, tickPos ), 
+                    QPointF( l2, tickPos ) );
+            }
+        }
+    }
+}
+
+/*!
+  \brief Set the width of the wheel
+
+  Corresponds to the wheel height for horizontal orientation,
+  and the wheel width for vertical orientation.
+
+  \param width the wheel's width
+  \sa wheelWidth()
+*/
+void QwtWheel::setWheelWidth( int width )
+{
+    d_data->wheelWidth = width;
+    update();
+}
+
+/*!
+  \return Width of the wheel
+  \sa setWheelWidth()
+*/
+int QwtWheel::wheelWidth() const
+{
+    return d_data->wheelWidth;
+}
+
+/*!
+  \return a size hint
+*/
+QSize QwtWheel::sizeHint() const
+{
+    const QSize hint = minimumSizeHint();
+    return hint.expandedTo( QApplication::globalStrut() );
+}
+
+/*!
+  \return Minimum size hint
+  \warning The return value is based on the wheel width.
+*/
+QSize QwtWheel::minimumSizeHint() const
+{
+    QSize sz( 3 * d_data->wheelWidth + 2 * d_data->borderWidth,
+        d_data->wheelWidth + 2 * d_data->borderWidth );
+    if ( d_data->orientation != Qt::Horizontal )
+        sz.transpose();
+
+    return sz;
+}
+
+/*!
+  \brief Set the step size of the counter
+
+  A value <= 0.0 disables stepping
+
+  \param stepSize Single step size
+  \sa singleStep(), setPageStepCount()
+*/
+void QwtWheel::setSingleStep( double stepSize )
+{
+    d_data->singleStep = qMax( stepSize, 0.0 );
+}
+
+/*!
+  \return Single step size
+  \sa setSingleStep()
+ */
+double QwtWheel::singleStep() const
+{
+    return d_data->singleStep;
+}
+
+/*!
+  \brief En/Disable step alignment
+
+  When step alignment is enabled value changes initiated by
+  user input ( mouse, keyboard, wheel ) are aligned to
+  the multiples of the single step.
+
+  \param on On/Off
+  \sa stepAlignment(), setSingleStep()
+ */
+void QwtWheel::setStepAlignment( bool on )
+{
+    if ( on != d_data->stepAlignment )
+    {
+        d_data->stepAlignment = on;
+    }
+}
+
+/*!
+  \return True, when the step alignment is enabled
+  \sa setStepAlignment(), singleStep()
+ */
+bool QwtWheel::stepAlignment() const
+{
+    return d_data->stepAlignment;
+}
+
+/*!
+  \brief Set the page step count  
+    
+  pageStepCount is a multiplicator for the single step size
+  that typically corresponds to the user pressing PageUp or PageDown.
+    
+  A value of 0 disables page stepping. 
+
+  The default value is 1.
+
+  \param count Multiplicator for the single step size
+  \sa pageStepCount(), setSingleStep()
+ */
+void QwtWheel::setPageStepCount( int count )
+{
+    d_data->pageStepCount = qMax( 0, count );
+}
+
+/*! 
+  \return Page step count
+  \sa setPageStepCount(), singleStep()
+ */
+int QwtWheel::pageStepCount() const
+{
+    return d_data->pageStepCount;
+}
+
+/*!
+  \brief Set the minimum and maximum values
+
+  The maximum is adjusted if necessary to ensure that the range remains valid.
+  The value might be modified to be inside of the range.
+
+  \param min Minimum value
+  \param max Maximum value
+
+  \sa minimum(), maximum()
+ */
+void QwtWheel::setRange( double min, double max )
+{
+    max = qMax( min, max );
+
+    if ( d_data->minimum == min && d_data->maximum == max )
+        return;
+
+    d_data->minimum = min;
+    d_data->maximum = max;
+
+    if ( d_data->value < min || d_data->value > max )
+    {
+        d_data->value = qBound( min, d_data->value, max );
+
+        update();
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+/*!
+  Set the minimum value of the range
+
+  \param value Minimum value
+  \sa setRange(), setMaximum(), minimum()
+
+  \note The maximum is adjusted if necessary to ensure that the range remains valid.
+*/
+void QwtWheel::setMinimum( double value )
+{
+    setRange( value, maximum() );
+}
+
+/*!
+  \return The minimum of the range
+  \sa setRange(), setMinimum(), maximum()
+*/
+double QwtWheel::minimum() const
+{
+    return d_data->minimum;
+}
+
+/*!
+  Set the maximum value of the range
+
+  \param value Maximum value
+  \sa setRange(), setMinimum(), maximum()
+*/
+void QwtWheel::setMaximum( double value )
+{
+    setRange( minimum(), value );
+}
+
+/*!
+  \return The maximum of the range
+  \sa setRange(), setMaximum(), minimum()
+*/
+double QwtWheel::maximum() const
+{
+    return d_data->maximum;
+}
+
+/*!
+  \brief Set a new value without adjusting to the step raster
+
+  \param value New value
+
+  \sa value(), valueChanged()
+  \warning The value is clipped when it lies outside the range.
+*/
+void QwtWheel::setValue( double value )
+{
+    stopFlying();
+    d_data->isScrolling = false;
+
+    value = qBound( d_data->minimum, value, d_data->maximum );
+
+    if ( d_data->value != value )
+    {
+        d_data->value = value;
+
+        update();
+        Q_EMIT valueChanged( d_data->value );
+    }
+}
+
+/*!
+  \return Current value of the wheel
+  \sa setValue(), valueChanged()
+ */
+double QwtWheel::value() const
+{
+    return d_data->value;
+}
+
+/*!
+  \brief En/Disable inverted appearance
+
+  An inverted wheel increases its values in the opposite direction.
+  The direction of an inverted horizontal wheel will be from right to left
+  an inverted vertical wheel will increase from bottom to top.
+  
+  \param on En/Disable inverted appearance
+  \sa isInverted()
+ 
+ */
+void QwtWheel::setInverted( bool on )
+{
+    if ( d_data->inverted != on )
+    {
+        d_data->inverted = on;
+        update();
+    }
+}
+
+/*!
+  \return True, when the wheel is inverted
+  \sa setInverted()
+ */
+bool QwtWheel::isInverted() const
+{
+    return d_data->inverted;
+}
+
+/*!
+  \brief En/Disable wrapping
+
+  If wrapping is true stepping up from maximum() value will take 
+  you to the minimum() value and vice versa. 
+
+  \param on En/Disable wrapping
+  \sa wrapping()
+ */
+void QwtWheel::setWrapping( bool on )
+{
+    d_data->wrapping = on;
+}
+
+/*!
+  \return True, when wrapping is set
+  \sa setWrapping()
+ */
+bool QwtWheel::wrapping() const
+{
+    return d_data->wrapping;
+}
+
+/*!
+  \brief Set the slider's mass for flywheel effect.
+
+  If the slider's mass is greater then 0, it will continue
+  to move after the mouse button has been released. Its speed
+  decreases with time at a rate depending on the slider's mass.
+  A large mass means that it will continue to move for a
+  long time.
+
+  Derived widgets may overload this function to make it public.
+
+  \param mass New mass in kg
+
+  \bug If the mass is smaller than 1g, it is set to zero.
+       The maximal mass is limited to 100kg.
+  \sa mass()
+*/
+void QwtWheel::setMass( double mass )
+{
+    if ( mass < 0.001 )
+    {
+        d_data->mass = 0.0;
+    }
+    else
+    {
+        d_data->mass = qMin( 100.0, mass );
+    }
+
+    if ( d_data->mass <= 0.0 )
+        stopFlying();
+}
+
+/*!
+  \return mass
+  \sa setMass()
+*/
+double QwtWheel::mass() const
+{
+    return d_data->mass;
+}
+
+//!  Stop the flying movement of the wheel
+void QwtWheel::stopFlying()
+{
+    if ( d_data->timerId != 0 )
+    {
+        killTimer( d_data->timerId );
+        d_data->timerId = 0;
+        d_data->speed = 0.0;
+    }
+}
+
+double QwtWheel::boundedValue( double value ) const
+{
+    const double range = d_data->maximum - d_data->minimum;
+    
+    if ( d_data->wrapping && range >= 0.0 )
+    {
+        if ( value < d_data->minimum )
+        {
+            value += ::ceil( ( d_data->minimum - value ) / range ) * range;
+        }       
+        else if ( value > d_data->maximum )
+        {
+            value -= ::ceil( ( value - d_data->maximum ) / range ) * range;
+        }
+    }
+    else
+    {
+        value = qBound( d_data->minimum, value, d_data->maximum );
+    }
+
+    return value;
+}
+
+double QwtWheel::alignedValue( double value ) const
+{
+    const double stepSize = d_data->singleStep;
+
+    if ( stepSize > 0.0 )
+    {
+        value = d_data->minimum +
+            qRound( ( value - d_data->minimum ) / stepSize ) * stepSize;
+
+        if ( stepSize > 1e-12 )
+        {
+            if ( qFuzzyCompare( value + 1.0, 1.0 ) )
+            {
+                // correct rounding error if value = 0
+                value = 0.0;
+            }
+            else if ( qFuzzyCompare( value, d_data->maximum ) )
+            {
+                // correct rounding error at the border
+                value = d_data->maximum;
+            }
+        }
+    }       
+
+    return value;
+}
+
diff --git a/source/third_party/qwt/qwt_widget_overlay.cpp b/source/third_party/qwt/qwt_widget_overlay.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..18de451733fef2d9d8dbd98440a59cfff62a0cd0
--- /dev/null
+++ b/source/third_party/qwt/qwt_widget_overlay.cpp
@@ -0,0 +1,376 @@
+/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
+ * Qwt Widget Library
+ * Copyright (C) 1997   Josef Wilgen
+ * Copyright (C) 2002   Uwe Rathmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the Qwt License, Version 1.0
+ *****************************************************************************/
+
+#include "qwt/qwt_widget_overlay.h"
+#include "qwt/qwt_painter.h"
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qimage.h>
+#include <qevent.h>
+
+static QImage::Format qwtMaskImageFormat()
+{
+    if ( QwtPainter::isX11GraphicsSystem() )
+        return QImage::Format_ARGB32;
+
+    return QImage::Format_ARGB32_Premultiplied;
+}
+
+static QRegion qwtAlphaMask( 
+    const QImage& image, const QVector<QRect> rects )
+{
+    const int w = image.width();
+    const int h = image.height();
+
+    QRegion region;
+    QRect rect;
+
+    for ( int i = 0; i < rects.size(); i++ )
+    {
+        int x1, x2, y1, y2;
+        rects[i].getCoords( &x1, &y1, &x2, &y2 );
+
+        x1 = qMax( x1, 0 );
+        x2 = qMin( x2, w - 1 );
+        y1 = qMax( y1, 0 );
+        y2 = qMin( y2, h - 1 );
+
+        for ( int y = y1; y <= y2; ++y ) 
+        {
+            bool inRect = false;
+            int rx0 = -1;
+
+            const uint *line = 
+                reinterpret_cast<const uint *> ( image.scanLine( y ) ) + x1;
+            for ( int x = x1; x <= x2; x++ ) 
+            {
+                const bool on = ( ( *line++ >> 24 ) != 0 );
+                if ( on != inRect ) 
+                {
+                    if ( inRect  ) 
+                    {
+                        rect.setCoords( rx0, y, x - 1, y );
+                        region += rect;
+                    } 
+                    else 
+                    {
+                        rx0 = x;
+                    }
+
+                    inRect = on;
+                } 
+            }
+
+            if ( inRect ) 
+            {
+                rect.setCoords( rx0, y, x2, y );
+                region = region.united( rect );
+            }
+        }
+    }
+
+    return region;
+}
+
+class QwtWidgetOverlay::PrivateData
+{
+public:
+    PrivateData():
+        maskMode( QwtWidgetOverlay::MaskHint ),
+        renderMode( QwtWidgetOverlay::AutoRenderMode ),
+        rgbaBuffer( NULL )
+    {
+    }
+
+    ~PrivateData()
+    {
+        resetRgbaBuffer();
+    }
+
+    void resetRgbaBuffer()
+    {
+        if ( rgbaBuffer )
+        {
+            ::free( rgbaBuffer );
+            rgbaBuffer = NULL;
+        }
+    }
+
+    MaskMode maskMode;
+    RenderMode renderMode;
+    uchar *rgbaBuffer;
+};
+
+/*!
+   \brief Constructor
+   \param widget Parent widget, where the overlay is aligned to
+*/
+QwtWidgetOverlay::QwtWidgetOverlay( QWidget* widget ):
+    QWidget( widget )
+{
+    d_data = new PrivateData;
+
+    setAttribute( Qt::WA_TransparentForMouseEvents );
+    setAttribute( Qt::WA_NoSystemBackground );
+    setFocusPolicy( Qt::NoFocus );
+
+    if ( widget )
+    {
+        resize( widget->size() );
+        widget->installEventFilter( this );
+    }
+}
+
+//! Destructor
+QwtWidgetOverlay::~QwtWidgetOverlay()
+{
+    delete d_data;
+}
+
+/*!
+   \brief Specify how to find the mask for the overlay
+
+   \param mode New mode
+   \sa maskMode()
+ */
+void QwtWidgetOverlay::setMaskMode( MaskMode mode )
+{
+    if ( mode != d_data->maskMode )
+    {
+        d_data->maskMode = mode;
+        d_data->resetRgbaBuffer();
+    }
+}
+
+/*!
+   \return Mode how to find the mask for the overlay
+   \sa setMaskMode()
+ */
+QwtWidgetOverlay::MaskMode QwtWidgetOverlay::maskMode() const
+{
+    return d_data->maskMode;
+}
+
+/*!
+   Set the render mode
+   \param mode Render mode
+
+   \sa RenderMode, renderMode()
+*/
+void QwtWidgetOverlay::setRenderMode( RenderMode mode )
+{
+    d_data->renderMode = mode;
+}
+
+/*!
+   \return Render mode
+   \sa RenderMode, setRenderMode()
+ */
+QwtWidgetOverlay::RenderMode QwtWidgetOverlay::renderMode() const
+{
+    return d_data->renderMode;
+}
+
+/*!
+   Recalculate the mask and repaint the overlay
+ */
+void QwtWidgetOverlay::updateOverlay()
+{
+    updateMask();
+    update();
+}
+
+void QwtWidgetOverlay::updateMask()
+{
+    d_data->resetRgbaBuffer();
+
+    QRegion mask;
+
+    if ( d_data->maskMode == QwtWidgetOverlay::MaskHint )
+    {
+        mask = maskHint();
+    }
+    else if ( d_data->maskMode == QwtWidgetOverlay::AlphaMask )
+    {
+        // TODO: the image doesn't need to be larger than
+        //       the bounding rectangle of the hint !!
+
+        QRegion hint = maskHint();
+        if ( hint.isEmpty() )
+            hint += QRect( 0, 0, width(), height() );
+
+        // A fresh buffer from calloc() is usually faster
+        // than reinitializing an existing one with
+        // QImage::fill( 0 ) or memset()
+
+        d_data->rgbaBuffer = ( uchar* )::calloc( width() * height(), 4 );
+
+        QImage image( d_data->rgbaBuffer, 
+            width(), height(), qwtMaskImageFormat() );
+
+        QPainter painter( &image );
+        draw( &painter );
+        painter.end();
+
+        mask = qwtAlphaMask( image, hint.rects() );
+
+        if ( d_data->renderMode == QwtWidgetOverlay::DrawOverlay )
+        {
+            // we don't need the buffer later
+            d_data->resetRgbaBuffer();
+        }
+    }
+
+    // A bug in Qt initiates a full repaint of the widget
+    // when we change the mask, while we are visible !
+
+    setVisible( false );
+
+    if ( mask.isEmpty() )
+        clearMask();
+    else
+        setMask( mask );
+
+    setVisible( true );
+}
+
+/*!
+  Paint event
+  \param event Paint event
+
+  \sa drawOverlay()
+*/
+void QwtWidgetOverlay::paintEvent( QPaintEvent* event )
+{
+    const QRegion clipRegion = event->region();
+
+    QPainter painter( this );
+
+    bool useRgbaBuffer = false;
+    if ( d_data->renderMode == QwtWidgetOverlay::CopyAlphaMask )
+    {
+        useRgbaBuffer = true;
+    }
+    else if ( d_data->renderMode == QwtWidgetOverlay::AutoRenderMode )
+    {
+        if ( painter.paintEngine()->type() == QPaintEngine::Raster )
+            useRgbaBuffer = true;
+    }
+
+    if ( d_data->rgbaBuffer && useRgbaBuffer )
+    {
+        const QImage image( d_data->rgbaBuffer, 
+            width(), height(), qwtMaskImageFormat() );
+
+        QVector<QRect> rects;
+        if ( clipRegion.rects().size() > 2000 )
+        {
+            // the region is to complex
+            painter.setClipRegion( clipRegion );
+            rects += clipRegion.boundingRect();
+        }
+        else
+        {
+            rects = clipRegion.rects();
+        }
+
+        for ( int i = 0; i < rects.size(); i++ )
+        {
+            const QRect r = rects[i];
+            painter.drawImage( r.topLeft(), image, r );
+        }
+    }
+    else
+    {
+        painter.setClipRegion( clipRegion );
+        draw( &painter );
+    }
+}
+
+/*!
+  Resize event
+  \param event Resize event
+*/
+void QwtWidgetOverlay::resizeEvent( QResizeEvent* event )
+{
+    Q_UNUSED( event );
+
+    d_data->resetRgbaBuffer();
+}
+
+void QwtWidgetOverlay::draw( QPainter *painter ) const
+{
+    QWidget *widget = const_cast< QWidget *>( parentWidget() );
+    if ( widget )
+    {
+        painter->setClipRect( parentWidget()->contentsRect() );
+
+        // something special for the plot canvas
+
+        const int idx = widget->metaObject()->indexOfMethod( "borderPath(QRect)" );
+        if ( idx >= 0 )
+        {
+            QPainterPath clipPath;
+
+            ( void )QMetaObject::invokeMethod(
+                widget, "borderPath", Qt::DirectConnection,
+                Q_RETURN_ARG( QPainterPath, clipPath ), Q_ARG( QRect, rect() ) );
+
+            if (!clipPath.isEmpty())
+                painter->setClipPath( clipPath, Qt::IntersectClip );
+        }
+    }
+
+    drawOverlay( painter );
+}
+
+/*!
+   \brief Calculate an approximation for the mask
+
+   - MaskHint
+     The hint is used as mask.
+
+   - AlphaMask
+     The hint is used to speed up the algorithm
+     for calculating a mask from non transparent pixels
+
+   - NoMask
+     The hint is unused.
+
+   The default implementation returns an invalid region
+   indicating no hint.
+
+   \return Hint for the mask
+ */
+QRegion QwtWidgetOverlay::maskHint() const
+{
+    return QRegion();
+}
+
+/*!
+  \brief Event filter
+
+  Resize the overlay according to the size of the parent widget.
+
+  \param object Object to be filtered
+  \param event Event
+
+  \return See QObject::eventFilter()
+*/
+
+bool QwtWidgetOverlay::eventFilter( QObject* object, QEvent* event )
+{
+    if ( object == parent() && event->type() == QEvent::Resize )
+    {
+        QResizeEvent *resizeEvent = static_cast<QResizeEvent *>( event );
+        resize( resizeEvent->size() );
+    }
+
+    return QObject::eventFilter( object, event );
+}
diff --git a/source/third_party/qxt/QxtLetterBoxWidget.cpp b/source/third_party/qxt/QxtLetterBoxWidget.cpp
index e4c6d0d8a028e84d68582ccedfb983a58c949aae..847478287a5585dc4f73f69e6f9f65976dbb7a4e 100644
--- a/source/third_party/qxt/QxtLetterBoxWidget.cpp
+++ b/source/third_party/qxt/QxtLetterBoxWidget.cpp
@@ -1,4 +1,4 @@
-#include "third_party/qxt/QxtLetterBoxWidget.h"
+#include "qxt/QxtLetterBoxWidget.h"
 /****************************************************************************
 ** Copyright (c) 2006 - 2011, the LibQxt project.
 ** See the Qxt AUTHORS file for a list of authors and copyright holders.
@@ -29,7 +29,7 @@
 ** <http://libqxt.org>  <foundation@libqxt.org>
 *****************************************************************************/
 
-#include "third_party/qxt/QxtLetterBoxWidgetP.h"
+#include "qxt/QxtLetterBoxWidgetP.h"
 #include <QLayout>
 #include <QStyle>
 
diff --git a/source/third_party/qxt/QxtSpanSlider.cpp b/source/third_party/qxt/QxtSpanSlider.cpp
index 4923e4589f7190df8b43a60778552e3514135a90..99e5c6767078a668f866150b0318f0dcbd6fc020 100644
--- a/source/third_party/qxt/QxtSpanSlider.cpp
+++ b/source/third_party/qxt/QxtSpanSlider.cpp
@@ -1,4 +1,4 @@
-#include <third_party/qxt/QxtSpanSlider.h>
+#include <qxt/QxtSpanSlider.h>
 /****************************************************************************
 ** Copyright (c) 2006 - 2011, the LibQxt project.
 ** See the Qxt AUTHORS file for a list of authors and copyright holders.
@@ -29,7 +29,7 @@
 ** <http://libqxt.org>  <foundation@libqxt.org>
 *****************************************************************************/
 
-#include <third_party/qxt/QxtSpanSliderP.h>
+#include <qxt/QxtSpanSliderP.h>
 #include <QKeyEvent>
 #include <QMouseEvent>
 #include <QApplication>
diff --git a/source/ui/application.cpp b/source/ui/application.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..782a474c21a985133fa19315f4bdda5960f4bf9a
--- /dev/null
+++ b/source/ui/application.cpp
@@ -0,0 +1,104 @@
+#include <pli_vis/ui/application.hpp>
+
+#include <cuda_runtime_api.h>
+#include <QShortcut>
+
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/plugin_base.hpp>
+
+namespace pli
+{
+application:: application()
+{
+  setupUi      (this);
+  showMaximized();
+  wait_spinner_ = new wait_spinner(toolbox, true, false);
+
+  set_sink             (std::make_shared<text_browser_sink>(console));
+  bind_actions         ();
+  create_gpu_status_bar();
+
+  plugins_ = findChildren<plugin_base*>(QRegExp("plugin")).toVector().toStdVector();
+  for (auto plugin : plugins_)
+    plugin->set_owner(this);
+  for (auto plugin : plugins_)
+    plugin->awake();
+  for (auto plugin : plugins_)
+    plugin->start();
+
+  splitter_vertical_left->setSizes(QList<int>{height(), 0});
+
+  action_help_version ->trigger();
+  action_help_gpu_info->trigger();
+}
+application::~application()
+{
+  for (auto plugin : plugins_)
+    plugin->destroy();
+}
+
+void application::set_is_loading(const bool is_loading) const
+{
+  is_loading ? wait_spinner_->start() : wait_spinner_->stop();
+  toolbox->setEnabled(!is_loading);
+}
+
+void application::bind_actions()
+{  
+  auto fullscreen_action = new QAction(this);
+  fullscreen_action->setShortcut       (QKeySequence(Qt::Key_F11));
+  fullscreen_action->setShortcutContext(Qt::ApplicationShortcut );
+  addAction(fullscreen_action);
+  connect(fullscreen_action, &QAction::triggered, this, [&]()
+  {
+    logger_->info(std::string("Toggling fullscreen mode."));
+    isFullScreen() ? showNormal() : showFullScreen();
+  });
+  
+  auto version_action = new QAction(this);
+  version_action->setShortcut       (QKeySequence(Qt::Key_V));
+  version_action->setShortcutContext(Qt::ApplicationShortcut );
+  addAction(version_action);
+  connect(version_action, &QAction::triggered, this, [&]()
+  {
+    logger_->info(std::string("Version ") + __DATE__);
+  });
+
+  auto gpu_info_action = new QAction(this);
+  gpu_info_action->setShortcut       (QKeySequence(Qt::Key_G));
+  gpu_info_action->setShortcutContext(Qt::ApplicationShortcut );
+  addAction(gpu_info_action);
+  connect(gpu_info_action, &QAction::triggered, this, [&]()
+  {
+    std::size_t free, total;
+    cudaMemGetInfo(&free, &total);
+    logger_->info("Available GPU memory: {} MB. Total GPU memory: {} MB.", free * 1E-6, total * 1E-6);
+  });
+}
+
+void application::create_gpu_status_bar()
+{
+  gpu_status_label_ = new QLabel      (this);
+  gpu_status_bar_   = new QProgressBar(this);
+  gpu_status_label_->setText  ("GPU Memory Occupancy ");
+  gpu_status_bar_  ->setFormat(QString("%p% (%v / %m MB)"));
+  status_bar       ->addPermanentWidget(gpu_status_label_);
+  status_bar       ->addPermanentWidget(gpu_status_bar_  );
+
+  auto timer = new QTimer(this);
+  connect(timer, &QTimer::timeout, [&]()
+  {
+    std::size_t free, total;
+    cudaMemGetInfo(&free, &total);
+    auto ratio = (total - free) / float(total);
+    gpu_status_bar_->setMaximum   ( total         * 1e-6);
+    gpu_status_bar_->setValue     ((total - free) * 1e-6);
+    gpu_status_bar_->setStyleSheet(QString::fromStdString(std::string(
+      " QProgressBar { border: 2px solid grey; border-radius: 0px; text-align: center; } "
+      " QProgressBar::chunk {background-color: rgb(") + 
+      std::to_string(std::min(255, int(80 + 255 *         ratio)))   + "," +
+      std::to_string(std::min(255, int(80 + 255 * (1.0F - ratio))))  + ",80); width: 1px;}"));
+  });
+  timer->start(16);
+}
+}
diff --git a/source/ui/plugins/color_plugin.cpp b/source/ui/plugins/color_plugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4614547a1a359a4b48bff5bf06822413c8ea7fc3
--- /dev/null
+++ b/source/ui/plugins/color_plugin.cpp
@@ -0,0 +1,71 @@
+#include <pli_vis/ui/plugins/color_plugin.hpp>
+
+#include <boost/format.hpp>
+
+#include <pli_vis/ui/utility/line_edit.hpp>
+
+namespace pli
+{
+color_plugin::color_plugin(QWidget* parent) : plugin(parent)
+{
+  line_edit_k->setValidator(new QDoubleValidator(0, std::numeric_limits<double>::max(), 10, this));
+
+  connect(radio_button_hsl_1, &QRadioButton::clicked     , [&]
+  {
+    on_change(mode(), k(), inverted());
+  });
+  connect(radio_button_hsl_2, &QRadioButton::clicked     , [&]
+  {
+    on_change(mode(), k(), inverted());
+  });
+  connect(radio_button_hsv_1, &QRadioButton::clicked     , [&]
+  {
+    on_change(mode(), k(), inverted());
+  });
+  connect(radio_button_hsv_2, &QRadioButton::clicked     , [&]
+  {
+    on_change(mode(), k(), inverted());
+  });
+  connect(radio_button_rgb  , &QRadioButton::clicked     , [&]
+  {
+    on_change(mode(), k(), inverted());
+  });
+  connect(slider_k          , &QSlider::valueChanged     , [&]
+  {
+    line_edit_k->setText(QString::fromStdString((boost::format("%.4f") % (float(slider_k->value()) / slider_k->maximum())).str()));
+    on_change(mode(), k(), inverted());
+  });
+  connect(slider_k          , &QSlider::sliderReleased   , [&]
+  {
+    on_change(mode(), k(), inverted());
+  });
+  connect(line_edit_k       , &QLineEdit::editingFinished, [&]
+  {
+    auto value = line_edit::get_text<float>(line_edit_k);
+    slider_k->setValue (value * slider_k->maximum());
+    on_change(mode(), k(), inverted());
+  });
+  connect(checkbox_invert_k , &QCheckBox::stateChanged   , [&](bool state)
+  {
+    on_change(mode(), k(), inverted());
+  });
+}
+
+int   color_plugin::mode    () const
+{
+  if(radio_button_hsl_1->isChecked()) return 0;
+  if(radio_button_hsl_2->isChecked()) return 1;
+  if(radio_button_hsv_1->isChecked()) return 2;
+  if(radio_button_hsv_2->isChecked()) return 3;
+  if(radio_button_rgb  ->isChecked()) return 4;
+  return -1;
+}
+float color_plugin::k       () const
+{
+  return line_edit::get_text<float>(line_edit_k);
+}
+bool  color_plugin::inverted() const
+{
+  return checkbox_invert_k->isChecked();
+}
+}
diff --git a/source/ui/plugins/data_plugin.cpp b/source/ui/plugins/data_plugin.cpp
index af7b4665a68641e054fd51215019767390d30953..882e6fe5726ccda8e93fafa229480cecd4296767 100644
--- a/source/ui/plugins/data_plugin.cpp
+++ b/source/ui/plugins/data_plugin.cpp
@@ -1,160 +1,351 @@
-#include /* implements */ <ui/plugins/data_plugin.hpp>
+#include <pli_vis/ui/plugins/data_plugin.hpp>
+
+#define _USE_MATH_DEFINES
+
+#include <math.h>
 
 #include <QFileDialog>
 
-#include <io/hdf5_io.hpp>
-#include <io/hdf5_io_2.hpp>
-#include <ui/window.hpp>
-#include <utility/line_edit_utility.hpp>
-#include <utility/qt_text_browser_sink.hpp>
+#include <pli_vis/cuda/utility/convert.h>
+#include <pli_vis/cuda/utility/vector_ops.h>
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+#include <pli_vis/utility/make_even.hpp>
 
 namespace pli
 {
 data_plugin::data_plugin(QWidget* parent) : plugin(parent)
 {
-  setupUi(this);
-  
-  connect(radio_button_slice_by_slice, &QRadioButton::clicked     , [&]()
+  connect(button_open_slice , &QPushButton::clicked             , [&]
   {
-    logger_->info(std::string("Toggled sliced (Vervet1818 style) data type."));
-
-    line_edit_file          ->setPlaceholderText("C:/Vervet1818.h5");
-    line_edit_vector_spacing->setPlaceholderText("VectorSpacing");
-    line_edit_transmittance ->setPlaceholderText("%Slice%/Microscope/Processed/Registered/NTransmittance");
-    line_edit_retardation   ->setPlaceholderText("%Slice%/Microscope/Processed/Registered/Retardation");
-    line_edit_direction     ->setPlaceholderText("%Slice%/Microscope/Processed/Registered/Direction");
-    line_edit_inclination   ->setPlaceholderText("%Slice%/Microscope/Processed/Registered/Inclination");
-    line_edit_unit_vector   ->setPlaceholderText("%Slice%/Microscope/Processed/Registered/UnitVector");
-
-    label_direction      ->setEnabled(true );
-    label_inclination    ->setEnabled(true );
-    label_unit_vector    ->setEnabled(false);
+    auto filename = QFileDialog::getOpenFileName(this, tr("Select PLI file."), "C:/", tr("HDF5 Files (*.h5)")).toStdString();
+    if(filename.empty())
+      return;
 
-    line_edit_direction  ->setEnabled(true );
-    line_edit_inclination->setEnabled(true );
-    line_edit_unit_vector->setEnabled(false);
+    io_.set_filepath          (filename.c_str());
+    io_.set_transmittance_path("%Slice%/Microscope/Processed/Registered/NTransmittance");
+    io_.set_retardation_path  ("%Slice%/Microscope/Processed/Registered/Retardation"   );
+    io_.set_direction_path    ("%Slice%/Microscope/Processed/Registered/Direction"     );
+    io_.set_inclination_path  ("%Slice%/Microscope/Processed/Registered/Inclination"   );
+    io_.set_mask_path         ("%Slice%/Microscope/Processed/Registered/Mask"          );
+    io_.set_unit_vector_path  ("%Slice%/Microscope/Processed/Registered/UnitVector"    );
 
-    if (radio_button_slice_by_slice->isChecked())
-      set_file(line_edit_file->text().toStdString());
+    setup();
   });
-  connect(radio_button_volume        , &QRadioButton::clicked     , [&]()
+  connect(button_open_volume, &QPushButton::clicked             , [&]
   {
-    logger_->info(std::string("Toggled volumetric (MSA0309 style) data type."));
-    
-    line_edit_file          ->setPlaceholderText("C:/MSA0309_s0536-0695.h5");
-    line_edit_vector_spacing->setPlaceholderText("Voxelsize");
-    line_edit_transmittance ->setPlaceholderText("Transmittance");
-    line_edit_retardation   ->setPlaceholderText("Retardation");
-    line_edit_direction     ->setPlaceholderText("Direction");
-    line_edit_inclination   ->setPlaceholderText("Inclination");
-    line_edit_unit_vector   ->setPlaceholderText("UnitVector");
-
-    label_direction      ->setEnabled(false);
-    label_inclination    ->setEnabled(false);
-    label_unit_vector    ->setEnabled(true );
+    auto filename = QFileDialog::getOpenFileName(this, tr("Select PLI file."), "C:/", tr("HDF5 Files (*.h5)")).toStdString();
+    if (filename.empty())
+      return;
 
-    line_edit_direction  ->setEnabled(false);
-    line_edit_inclination->setEnabled(false);
-    line_edit_unit_vector->setEnabled(true );
+    io_.set_filepath          (filename.c_str());
+    io_.set_transmittance_path("Transmittance");
+    io_.set_retardation_path  ("Retardation"  );
+    io_.set_direction_path    ("Direction"    );
+    io_.set_inclination_path  ("Inclination"  );
+    io_.set_mask_path         ("Mask"         );
+    io_.set_unit_vector_path  ("UnitVector"   );
 
-    if (radio_button_volume->isChecked())
-      set_file(line_edit_file->text().toStdString());
+    setup();
+  });
+  connect(image             , &roi_selector::on_selection_change, [&](const std::array<float, 2> offset_perc, const std::array<float, 2> size_perc)
+  {
+    std::array<int, 2> offset { make_even(int(offset_perc[0] * slider_x->maximum())), make_even(int(offset_perc[1] * slider_y->maximum())) };
+    std::array<int, 2> size   { make_even(int(size_perc  [0] * slider_x->maximum())), make_even(int(size_perc  [1] * slider_y->maximum())) };
+    line_edit_offset_x->setText      (QString::fromStdString(std::to_string(offset[0])));
+    line_edit_size_x  ->setText      (QString::fromStdString(std::to_string(size  [0])));
+    line_edit_offset_y->setText      (QString::fromStdString(std::to_string(offset[1])));
+    line_edit_size_y  ->setText      (QString::fromStdString(std::to_string(size  [1])));
+    slider_x          ->setLowerValue(offset[0]);
+    slider_x          ->setUpperValue(offset[0] + size[0]);
+    slider_y          ->setLowerValue(offset[1]);
+    slider_y          ->setUpperValue(offset[1] + size[1]);
+  });
+  connect(slider_x          , &QxtSpanSlider::lowerValueChanged , [&](int value)
+  {
+    line_edit_offset_x->setText(QString::fromStdString(std::to_string(make_even(value))));
+    line_edit_size_x  ->setText(QString::fromStdString(std::to_string(make_even(slider_x->upperValue() - value))));
+  });
+  connect(slider_x          , &QxtSpanSlider::upperValueChanged , [&](int value)
+  {
+    line_edit_size_x->setText(QString::fromStdString(std::to_string(make_even(value - slider_x->lowerValue()))));
+  });
+  connect(slider_x          , &QxtSpanSlider::sliderReleased    , [&]
+  {
+    image->set_selection_offset_percentage({static_cast<float>(slider_x->lowerValue())                          / slider_x->maximum(), image->selection_offset_percentage()[1]});
+    image->set_selection_size_percentage  ({static_cast<float>(slider_x->upperValue() - slider_x->lowerValue()) / slider_x->maximum(), image->selection_size_percentage  ()[1]});
+  });
+  connect(slider_y          , &QxtSpanSlider::lowerValueChanged , [&](int value)
+  {
+    line_edit_offset_y->setText(QString::fromStdString(std::to_string(make_even(value))));
+    line_edit_size_y  ->setText(QString::fromStdString(std::to_string(make_even(slider_y->upperValue() - value))));
   });
-  connect(button_browse              , &QPushButton::clicked      , [&]
+  connect(slider_y          , &QxtSpanSlider::upperValueChanged , [&](int value)
   {
-    logger_->info(std::string("Opening file browser."));
-    auto filename = QFileDialog::getOpenFileName(this, tr("Select PLI file."), "C:/", tr("HDF5 Files (*.h5)"));
-    logger_->info("Closing file browser. Selection is: {}.", filename.toStdString());
-    set_file(filename.toStdString());
+    line_edit_size_y->setText(QString::fromStdString(std::to_string(make_even(value - slider_y->lowerValue()))));
   });
-  connect(line_edit_vector_spacing   , &QLineEdit::editingFinished, [&]
+  connect(slider_y          , &QxtSpanSlider::sliderReleased    , [&]
   {
-    auto text = line_edit_utility::get_text(line_edit_vector_spacing);
-    logger_->info("Vector spacing attribute path is set to {}.", text);
-    if (io_)
-      io_->set_attribute_path_vector_spacing(text);
-    on_change();
+    image->set_selection_offset_percentage({image->selection_offset_percentage()[0], static_cast<float>(slider_y->lowerValue())                          / slider_y->maximum()});
+    image->set_selection_size_percentage  ({image->selection_size_percentage  ()[0], static_cast<float>(slider_y->upperValue() - slider_y->lowerValue()) / slider_y->maximum()});
   });
-  connect(line_edit_transmittance    , &QLineEdit::editingFinished, [&] 
+  connect(slider_z          , &QxtSpanSlider::lowerValueChanged , [&](int value)
   {
-    auto text = line_edit_utility::get_text(line_edit_transmittance);
-    logger_->info("Transmittance dataset path is set to {}.", text);
-    if (io_)
-      io_->set_dataset_path_transmittance(text);
-    on_change();
+    line_edit_offset_z->setText(QString::fromStdString(std::to_string(value)));
+    line_edit_size_z  ->setText(QString::fromStdString(std::to_string(slider_z->upperValue() - value)));
   });
-  connect(line_edit_retardation      , &QLineEdit::editingFinished, [&]
+  connect(slider_z          , &QxtSpanSlider::upperValueChanged , [&](int value)
   {
-    auto text = line_edit_utility::get_text(line_edit_retardation);
-    logger_->info("Retardation dataset path is set to {}.", text);
-    if (io_)
-      io_->set_dataset_path_retardation(text);
-    on_change();
+    line_edit_size_z->setText(QString::fromStdString(std::to_string(value - slider_z->lowerValue())));
   });
-  connect(line_edit_direction        , &QLineEdit::editingFinished, [&]
+  connect(line_edit_offset_x, &QLineEdit::editingFinished       , [&]
   {
-    auto text = line_edit_utility::get_text(line_edit_direction);
-    logger_->info("Fiber direction dataset path is set to {}.", text);
-    if (io_)
-      io_->set_dataset_path_fiber_direction(text);
-    on_change();
+    auto value = std::max(std::min(make_even(line_edit::get_text<int>(line_edit_offset_x)), int(slider_x->maximum())), int(slider_x->minimum()));
+    if (slider_x->upperValue() < value)
+      slider_x->setUpperValue(value);
+    slider_x        ->setLowerValue                  (value);
+    image           ->set_selection_offset_percentage({static_cast<float>(value) / slider_x->maximum(), image->selection_offset_percentage()[1]});
+    image           ->set_selection_size_percentage  ({static_cast<float>(slider_x->upperValue() - slider_x->lowerValue()) / slider_x->maximum(), image->selection_size_percentage()[1]});
+    line_edit_size_x->setText                        (QString::fromStdString(std::to_string(slider_x->upperValue() - value)));
   });
-  connect(line_edit_inclination      , &QLineEdit::editingFinished, [&]
+  connect(line_edit_size_x  , &QLineEdit::editingFinished       , [&]
   {
-    auto text = line_edit_utility::get_text(line_edit_inclination);
-    logger_->info("Fiber inclination dataset path is set to {}.", text);
-    if (io_)
-      io_->set_dataset_path_fiber_inclination(text);
-    on_change();
+    auto value = std::max(std::min(make_even(line_edit::get_text<int>(line_edit_size_x)), int(slider_x->maximum())), int(slider_x->minimum()));
+    slider_x->setUpperValue                (slider_x->lowerValue() + value);
+    image   ->set_selection_size_percentage({static_cast<float>(value) / slider_x->maximum(), image->selection_size_percentage()[1]});
+  });
+  connect(line_edit_offset_y, &QLineEdit::editingFinished       , [&]
+  {
+    auto value = std::max(std::min(make_even(line_edit::get_text<int>(line_edit_offset_y)), int(slider_y->maximum())), int(slider_y->minimum()));
+    if (slider_y->upperValue() < value)
+      slider_y->setUpperValue(value);
+    slider_y        ->setLowerValue                  (value);
+    image           ->set_selection_offset_percentage({image->selection_offset_percentage()[0], static_cast<float>(value) / slider_y->maximum()});
+    image           ->set_selection_size_percentage  ({image->selection_size_percentage  ()[0], static_cast<float>(slider_y->upperValue() - slider_y->lowerValue()) / slider_y->maximum()});
+    line_edit_size_y->setText                        (QString::fromStdString(std::to_string(make_even(slider_y->upperValue() - value))));
+  });
+  connect(line_edit_size_y  , &QLineEdit::editingFinished       , [&]
+  {
+    auto value = std::max(std::min(make_even(line_edit::get_text<int>(line_edit_size_y)), int(slider_y->maximum())), int(slider_y->minimum()));
+    slider_y->setUpperValue                (make_even(slider_y->lowerValue() + value));
+    image   ->set_selection_size_percentage({image->selection_size_percentage()[0], static_cast<float>(value) / slider_y->maximum()});
+  });
+  connect(line_edit_offset_z, &QLineEdit::editingFinished       , [&]
+  {
+    auto value = std::max(std::min(line_edit::get_text<int>(line_edit_offset_z), int(slider_z->maximum())), int(slider_z->minimum()));
+    if (slider_z->upperValue() < value)
+      slider_z->setUpperValue(value + 1);
+    slider_z        ->setLowerValue(value);
+    line_edit_size_z->setText      (QString::fromStdString(std::to_string(slider_z->upperValue() - value)));
+  });
+  connect(line_edit_size_z  , &QLineEdit::editingFinished       , [&]
+  {
+    auto value = std::min(line_edit::get_text<int>(line_edit_size_z), int(slider_z->maximum() - slider_z->minimum()));
+    slider_z->setUpperValue(slider_z->lowerValue() + value);
+  });
+  connect(button_update     , &QAbstractButton::clicked         , [&]
+  {
+    owner_->set_is_loading(true);
+
+    future_ = std::async(std::launch::async, [&]
+    {
+      try
+      {
+        transmittance_bounds_ = io_.load_transmittance_bounds();
+        retardation_bounds_   = io_.load_retardation_bounds  ();
+        direction_bounds_     = io_.load_direction_bounds    ();
+        inclination_bounds_   = io_.load_inclination_bounds  ();
+        mask_bounds_          = io_.load_mask_bounds         ();
+        unit_vector_bounds_   = io_.load_unit_vector_bounds  ();
+
+        transmittance_        = std::make_unique<boost::multi_array<float, 3>>(io_.load_transmittance(selection_offset(), selection_size(), selection_stride()));
+        retardation_          = std::make_unique<boost::multi_array<float, 3>>(io_.load_retardation  (selection_offset(), selection_size(), selection_stride()));
+        direction_            = std::make_unique<boost::multi_array<float, 3>>(io_.load_direction    (selection_offset(), selection_size(), selection_stride()));
+        inclination_          = std::make_unique<boost::multi_array<float, 3>>(io_.load_inclination  (selection_offset(), selection_size(), selection_stride()));
+        mask_                 = std::make_unique<boost::multi_array<float, 3>>(io_.load_mask         (selection_offset(), selection_size(), selection_stride()));
+        unit_vector_          = std::make_unique<boost::multi_array<float, 4>>(io_.load_unit_vector  (selection_offset(), selection_size(), selection_stride()));
+      }
+      catch (std::exception& exception)
+      {
+        logger_->error(std::string(exception.what()));
+      }
+    });
+
+    while(future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
+      QApplication::processEvents();
+
+    on_load();
+
+    owner_->set_is_loading(false);
+    owner_->viewer ->update();
   });
 }
-void data_plugin::start()
+
+std::array<std::size_t, 3> data_plugin::selection_offset() const
+{
+  return
+  {
+    line_edit::get_text<std::size_t>(line_edit_offset_x),
+    line_edit::get_text<std::size_t>(line_edit_offset_y),
+    line_edit::get_text<std::size_t>(line_edit_offset_z)
+  };
+}
+std::array<std::size_t, 3> data_plugin::selection_size  () const
+{
+  auto stride = selection_stride();
+  return
+  {
+    line_edit::get_text<std::size_t>(line_edit_size_x) / stride[0],
+    line_edit::get_text<std::size_t>(line_edit_size_y) / stride[1],
+    line_edit::get_text<std::size_t>(line_edit_size_z) / stride[2]
+  };
+}
+std::array<std::size_t, 3> data_plugin::selection_stride() const
 {
-  set_sink(std::make_shared<qt_text_browser_sink>(owner_->console));
-  logger_->info(std::string("Start successful."));
+  return
+  {
+    line_edit::get_text<std::size_t>(line_edit_stride_x),
+    line_edit::get_text<std::size_t>(line_edit_stride_y),
+    line_edit::get_text<std::size_t>(line_edit_stride_z)
+  };
 }
 
-hdf5_io_base* data_plugin::io() const
+boost::multi_array<unsigned char, 2> data_plugin::generate_preview_image  (std::size_t x_resolution)
 {
-  return io_.get();
+  if (retardation_bounds_.second[0] == 0)
+    return boost::multi_array<unsigned char, 2>();
+
+  std::array<std::size_t, 3> size   = {1, 1, 1};
+  std::array<std::size_t, 3> stride = {1, 1, 1};
+  size  [0] = std::min(int(retardation_bounds_.second[0]), int(x_resolution));
+  stride[0] = retardation_bounds_.second[0] / size  [0];
+  size  [1] = retardation_bounds_.second[1] / stride[0];
+  stride[1] = stride[0];
+
+  boost::multi_array<unsigned char, 2> preview_image(boost::extents[size[0]][size[1]], boost::fortran_storage_order());
+  auto data = io_.load_retardation(retardation_bounds_.first, size, stride);
+  for (auto x = 0; x < size[0]; x++)
+    for (auto y = 0; y < size[1]; y++)
+      preview_image[x][y] = data[x][y][0] * 255.0;
+  return preview_image;
 }
+boost::multi_array<unsigned char, 2> data_plugin::generate_selection_image(std::size_t x_resolution)
+{
+  if (retardation_bounds_.second[0] == 0)
+    return boost::multi_array<unsigned char, 2>();
 
-void data_plugin::set_file(const std::string& filename)
+  std::array<std::size_t, 3> size   = {1, 1, 1};
+  std::array<std::size_t, 3> stride = {1, 1, 1};
+  size  [0] = std::min(int(selection_size()[0]), int(x_resolution));
+  stride[0] = line_edit::get_text<std::size_t>(line_edit_size_x) / size  [0];
+  size  [1] = line_edit::get_text<std::size_t>(line_edit_size_y) / stride[0];
+  stride[1] = stride[0];
+
+  boost::multi_array<unsigned char, 2> preview_image(boost::extents[size[0]][size[1]], boost::fortran_storage_order());
+  auto data = io_.load_retardation(selection_offset(), size, stride);
+  for (auto x = 0; x < size[0]; x++)
+    for (auto y = 0; y < size[1]; y++)
+      preview_image[x][y] = data[x][y][0] * 255.0;
+  return preview_image;
+}
+boost::multi_array<float3, 3>        data_plugin::generate_vectors        (bool        cartesian   )
 {
-  line_edit_file->setText(filename.c_str());
-  io_.reset(nullptr);
-  if (filename.empty())
-    logger_->info(std::string("Failed to open file: No filepath."));
+  // Generate from direction - inclination pairs.
+  if(direction_bounds_.second[0] != 0 && inclination_bounds_.second[0] != 0)
+  {
+    boost::multi_array<float3, 3> vectors(boost::extents[direction_->shape()[0]][direction_->shape()[1]][direction_->shape()[2]]);
+    
+    std::transform(
+      direction_  ->data(), 
+      direction_  ->data() + direction_->num_elements(), 
+      inclination_->data(), 
+      vectors     . data(), 
+      [cartesian] (const float& direction, const float& inclination)
+      {
+        float3 vector {1.0, (90.0F + direction) * M_PI / 180.0F, (90.0F - inclination) * M_PI / 180.0F};
+        return cartesian ? to_cartesian_coords(vector) : vector;
+      });
+
+    if(mask_bounds_.second[0] != 0)
+      std::transform(
+        vectors. data(),
+        vectors. data() + vectors.num_elements(),
+        mask_  ->data(),
+        vectors. data(), 
+        [] (const float3& vector, const float& mask)
+        {
+          return mask ? vector : float3{0.0F, 0.0F, 0.0F};
+        });
+
+    return vectors;
+  }
+  // Generate from unit vectors.
   else
   {
-    if (radio_button_slice_by_slice->isChecked())
-      io_.reset(new hdf5_io(
-        filename,
-        line_edit_utility::get_text(line_edit_vector_spacing),
-        ""                                                   ,
-        ""                                                   ,
-        line_edit_utility::get_text(line_edit_transmittance ),
-        line_edit_utility::get_text(line_edit_retardation   ),
-        line_edit_utility::get_text(line_edit_direction     ),
-        line_edit_utility::get_text(line_edit_inclination   ),
-        line_edit_utility::get_text(line_edit_unit_vector   ),
-        ""
-      ));
-    else
-      io_.reset(new hdf5_io_2(
-        filename,
-        line_edit_utility::get_text(line_edit_vector_spacing),
-        ""                                                   ,
-        ""                                                   ,
-        line_edit_utility::get_text(line_edit_transmittance ),
-        line_edit_utility::get_text(line_edit_retardation   ),
-        line_edit_utility::get_text(line_edit_direction     ),
-        line_edit_utility::get_text(line_edit_inclination   ),
-        line_edit_utility::get_text(line_edit_unit_vector   ),
-        ""
-      ));
-    logger_->info("Successfully opened file: {}.", filename);
+    boost::multi_array<float3, 3> vectors(boost::extents[unit_vector_->shape()[0]][unit_vector_->shape()[1]][unit_vector_->shape()[2]]);
+
+    std::transform(
+      reinterpret_cast<float3*>(unit_vector_->data()), 
+      reinterpret_cast<float3*>(unit_vector_->data() + unit_vector_->num_elements()), 
+      vectors.data(), 
+      [cartesian] (const float3& unit_vector)
+      {
+        auto vector = unit_vector / length(unit_vector);
+        return cartesian ? vector : to_spherical_coords(vector);
+      });
+      
+    if(mask_bounds_.second[0] != 0)
+      std::transform(
+        vectors. data(),
+        vectors. data() + vectors.num_elements(),
+        mask_  ->data(),
+        vectors. data(), 
+        [] (const float3& vector, const float& mask)
+        {
+          return mask ? vector : float3{0.0F, 0.0F, 0.0F};
+        });
+
+    return vectors;
   }
-  on_change();
+}
+
+void data_plugin::start()
+{
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+}
+void data_plugin::setup()
+{
+  // Load bounds.
+  transmittance_bounds_ = io_.load_transmittance_bounds();
+  retardation_bounds_   = io_.load_retardation_bounds  ();
+  direction_bounds_     = io_.load_direction_bounds    ();
+  inclination_bounds_   = io_.load_inclination_bounds  ();
+  mask_bounds_          = io_.load_mask_bounds         ();
+  unit_vector_bounds_   = io_.load_unit_vector_bounds  ();
+
+  // Adjust slider boundaries.
+  auto bounds = retardation_bounds();
+  slider_x->setMinimum(bounds.first[0]); slider_x->setMaximum(bounds.second[0]);
+  slider_y->setMinimum(bounds.first[1]); slider_y->setMaximum(bounds.second[1]);
+  slider_z->setMinimum(bounds.first[2]); slider_z->setMaximum(bounds.second[2]);
+  slider_z->setSpan   (bounds.first[2], bounds.first[2] + 1);
+
+  // Generate preview image.
+  auto preview_image = generate_preview_image();
+  auto shape         = preview_image.shape();
+  image->setPixmap(QPixmap::fromImage(QImage(preview_image.data(), shape[0], shape[1], QImage::Format::Format_Grayscale8)));
+
+  // Adjust widget size.
+  image    ->setSizeIncrement(shape[0], shape[1]);
+  letterbox->setWidget(image);
+  letterbox->update();
+  update();
+
+  // Hack for enforcing a UI update.
+  auto sizes = owner_->splitter->sizes();
+  owner_->splitter->setSizes(QList<int>{0       , sizes[1]});
+  owner_->splitter->setSizes(QList<int>{sizes[0], sizes[1]});
+  owner_->update();
 }
 }
diff --git a/source/ui/plugins/fdm_plugin.cpp b/source/ui/plugins/fdm_plugin.cpp
deleted file mode 100644
index 2031c3e039bf0039eaf369f9b5faa9798daf277a..0000000000000000000000000000000000000000
--- a/source/ui/plugins/fdm_plugin.cpp
+++ /dev/null
@@ -1,389 +0,0 @@
-#include /* implements */ <ui/plugins/fdm_plugin.hpp>
-
-#include <limits>
-#include <string>
-
-#include <boost/format.hpp>
-#include <boost/optional.hpp>
-
-#include <cuda/odf_field.h>
-#include <ui/plugins/data_plugin.hpp>
-#include <ui/plugins/selector_plugin.hpp>
-#include <ui/window.hpp>
-#include <utility/line_edit_utility.hpp>
-#include <utility/qt_text_browser_sink.hpp>
-#include <visualization/odf_field.hpp>
-
-namespace pli
-{
-fdm_plugin::fdm_plugin(QWidget* parent) : plugin(parent)
-{
-  setupUi(this);
-  
-  line_edit_vector_block_x   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
-  line_edit_vector_block_x   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
-  line_edit_vector_block_x   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
-  line_edit_histogram_theta  ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
-  line_edit_histogram_phi    ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
-  line_edit_maximum_sh_degree->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
-  line_edit_sampling_theta   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
-  line_edit_sampling_phi     ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
-  
-  connect(checkbox_enabled, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info(std::string(state ? "Enabled." : "Disabled."));
-    odf_field_->set_active(state);
-  });
-  connect(checkbox_depth_0, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 0 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_1, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 1 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_2, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 2 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_3, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 3 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_4, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 4 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_5, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 5 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_6, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 6 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_7, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 7 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_8, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 8 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-  connect(checkbox_depth_9, &QCheckBox::stateChanged, [&](int state)
-  {
-    logger_->info("Grid layer 9 is {}.", state ? "enabled" : "disabled");
-    set_visible_layers();
-  });
-
-  connect(slider_vector_block_x      , &QxtSpanSlider::valueChanged, [&]
-  {
-    line_edit_vector_block_x->setText(QString::fromStdString(std::to_string(slider_vector_block_x->value())));
-  });
-  connect(line_edit_vector_block_x   , &QLineEdit::editingFinished , [&]
-  {
-    slider_vector_block_x->setValue(line_edit_utility::get_text<int>(line_edit_vector_block_x));
-  });
-  connect(slider_vector_block_y      , &QxtSpanSlider::valueChanged, [&]
-  {
-    line_edit_vector_block_y->setText(QString::fromStdString(std::to_string(slider_vector_block_y->value())));
-  });
-  connect(line_edit_vector_block_y   , &QLineEdit::editingFinished , [&]
-  {
-    slider_vector_block_y->setValue(line_edit_utility::get_text<int>(line_edit_vector_block_y));
-  });
-  connect(slider_vector_block_z      , &QxtSpanSlider::valueChanged, [&]
-  {
-    line_edit_vector_block_z->setText(QString::fromStdString(std::to_string(slider_vector_block_z->value())));
-  });
-  connect(line_edit_vector_block_z   , &QLineEdit::editingFinished , [&]
-  {
-    slider_vector_block_z->setValue(line_edit_utility::get_text<int>(line_edit_vector_block_z));
-  });
-  connect(slider_histogram_theta     , &QxtSpanSlider::valueChanged, [&]
-  {
-    line_edit_histogram_theta->setText(QString::fromStdString(std::to_string(slider_histogram_theta->value())));
-  });
-  connect(line_edit_histogram_theta  , &QLineEdit::editingFinished , [&]
-  {
-    slider_histogram_theta->setValue(line_edit_utility::get_text<int>(line_edit_histogram_theta));
-  });
-  connect(slider_histogram_phi       , &QxtSpanSlider::valueChanged, [&]
-  {
-    line_edit_histogram_phi->setText(QString::fromStdString(std::to_string(slider_histogram_phi->value())));
-  });
-  connect(line_edit_histogram_phi    , &QLineEdit::editingFinished , [&]
-  {
-    slider_histogram_phi->setValue(line_edit_utility::get_text<int>(line_edit_histogram_phi));
-  });
-  connect(slider_maximum_sh_degree   , &QxtSpanSlider::valueChanged, [&]
-  {
-    line_edit_maximum_sh_degree->setText(QString::fromStdString(std::to_string(slider_maximum_sh_degree->value())));
-  });
-  connect(line_edit_maximum_sh_degree, &QLineEdit::editingFinished , [&]
-  {
-    slider_maximum_sh_degree->setValue(line_edit_utility::get_text<int>(line_edit_maximum_sh_degree));
-  });
-  connect(slider_sampling_theta      , &QxtSpanSlider::valueChanged, [&]
-  {
-    line_edit_sampling_theta->setText(QString::fromStdString(std::to_string(slider_sampling_theta->value())));
-  });
-  connect(line_edit_sampling_theta   , &QLineEdit::editingFinished , [&]
-  {
-    slider_sampling_theta->setValue(line_edit_utility::get_text<int>(line_edit_sampling_theta));
-  });
-  connect(slider_sampling_phi        , &QxtSpanSlider::valueChanged, [&]
-  {
-    line_edit_sampling_phi->setText(QString::fromStdString(std::to_string(slider_sampling_phi->value())));
-  });
-  connect(line_edit_sampling_phi     , &QLineEdit::editingFinished , [&]
-  {
-    slider_sampling_phi->setValue(line_edit_utility::get_text<int>(line_edit_sampling_phi));
-  });
-
-  connect(checkbox_clustering_enabled, &QCheckBox::stateChanged    , [&](int state)
-  {
-    logger_->info("Clustering is {}.", state ? "enabled" : "disabled");
-    label_threshold    ->setEnabled(state);
-    slider_threshold   ->setEnabled(state);
-    line_edit_threshold->setEnabled(state);
-  });
-  connect(slider_threshold           , &QxtSpanSlider::valueChanged, [&]
-  {
-    auto threshold = threshold_multiplier_ * slider_threshold->value();
-    line_edit_threshold->setText(QString::fromStdString((boost::format("%.2f") % threshold).str()));
-  });
-  connect(line_edit_threshold        , &QLineEdit::editingFinished , [&]
-  {
-    auto threshold = line_edit_utility::get_text<double>(line_edit_threshold);
-    slider_threshold->setValue(threshold / threshold_multiplier_);
-  });
-
-  connect(button_calculate           , &QAbstractButton::clicked   , [&]
-  {
-    calculate();
-  });
-  connect(button_extract_peaks       , &QAbstractButton::clicked   , [&]
-  {
-    extract_peaks();
-  });
-}
-
-void fdm_plugin::start    ()
-{
-  set_sink(std::make_shared<qt_text_browser_sink>(owner_->console));
-
-  line_edit_vector_block_x   ->setText(QString::fromStdString(std::to_string(slider_vector_block_x   ->value())));
-  line_edit_vector_block_y   ->setText(QString::fromStdString(std::to_string(slider_vector_block_y   ->value())));
-  line_edit_vector_block_z   ->setText(QString::fromStdString(std::to_string(slider_vector_block_z   ->value())));
-  line_edit_histogram_theta  ->setText(QString::fromStdString(std::to_string(slider_histogram_theta  ->value())));
-  line_edit_histogram_phi    ->setText(QString::fromStdString(std::to_string(slider_histogram_phi    ->value())));
-  line_edit_maximum_sh_degree->setText(QString::fromStdString(std::to_string(slider_maximum_sh_degree->value())));
-  line_edit_sampling_theta   ->setText(QString::fromStdString(std::to_string(slider_sampling_theta   ->value())));
-  line_edit_sampling_phi     ->setText(QString::fromStdString(std::to_string(slider_sampling_phi     ->value())));
-  line_edit_threshold        ->setText(QString::fromStdString((boost::format("%.2f") % (threshold_multiplier_ * slider_threshold->value())).str()));
-
-  odf_field_ = owner_->viewer->add_renderable<odf_field>();
-  set_visible_layers();
-
-  logger_->info(std::string("Resetting GPU."));
-  cudaDeviceReset();
-
-  logger_->info(std::string("Initializing cusolver and cublas."));
-  cusolverDnCreate(&cusolver_);
-  cublasCreate    (&cublas_  );
-
-  logger_->info(std::string("Start successful."));
-}
-void fdm_plugin::destroy()
-{
-  logger_->info(std::string("Destroying cusolver and cublas."));
-  cusolverDnDestroy(cusolver_);
-  cublasDestroy    (cublas_  );
-}
-
-void fdm_plugin::calculate    ()
-{
-  logger_->info(std::string("Updating viewer..."));
-  
-  auto io         = owner_->get_plugin<pli::data_plugin>    ()->io();
-  auto selector   = owner_->get_plugin<pli::selector_plugin>();
-  auto offset     = selector->selection_offset();
-  auto size       = selector->selection_size  ();
-  auto stride     = selector->selection_stride();
-  auto max_degree = line_edit_utility::get_text<unsigned>(line_edit_maximum_sh_degree);
-  uint3 block_dimensions       = {
-    line_edit_utility::get_text<unsigned>(line_edit_vector_block_x ), 
-    line_edit_utility::get_text<unsigned>(line_edit_vector_block_y ), 
-    line_edit_utility::get_text<unsigned>(line_edit_vector_block_z )};
-  uint2 histogram_dimensions   = {
-    line_edit_utility::get_text<unsigned>(line_edit_histogram_theta),
-    line_edit_utility::get_text<unsigned>(line_edit_histogram_phi  )};
-  uint2 sampling_dimensions    = { 
-    line_edit_utility::get_text<unsigned>(line_edit_sampling_theta),
-    line_edit_utility::get_text<unsigned>(line_edit_sampling_phi  )};
-
-  if(io == nullptr || size[0] == 0 || size[1] == 0 || size[2] == 0)
-  {
-    logger_->info(std::string("Update failed: No data."));
-    return;
-  }
-
-  size = {size[0] / stride[0], size[1] / stride[1], size[2] / stride[2]};
-
-  // Roll to the previous power of 2.
-  //size = {
-  //  std::size_t(pow(2, floor(log(size[0]) / log(2)))),
-  //  std::size_t(pow(2, floor(log(size[1]) / log(2)))),
-  //  std::size_t(pow(2, floor(log(size[2]) / log(2)))) };
-
-  uint3 coefficient_dimensions = {
-    unsigned(size[0]) / block_dimensions.x,
-    unsigned(size[1]) / block_dimensions.y,
-    unsigned(size[2]) / block_dimensions.z };
-
-  size = {
-    block_dimensions.x * coefficient_dimensions.x,
-    block_dimensions.y * coefficient_dimensions.y,
-    block_dimensions.z * coefficient_dimensions.z };
-
-  owner_->viewer      ->set_wait_spinner_enabled(true);
-  button_calculate    ->setEnabled(false);
-  button_extract_peaks->setEnabled(false);
-  selector            ->setEnabled(false);
-
-  // Load data from hard drive (on another thread).
-  std::array<float, 3>                          spacing    ;
-  boost::optional<boost::multi_array<float, 3>> direction  ;
-  boost::optional<boost::multi_array<float, 3>> inclination;
-  boost::optional<boost::multi_array<float, 4>> unit_vector;
-  coefficients_.resize(boost::extents
-    [coefficient_dimensions.x]
-    [coefficient_dimensions.y]
-    [coefficient_dimensions.z]
-    [(max_degree + 1) * (max_degree + 1)]);
-  future_ = std::async(std::launch::async, [&]
-  {
-    try
-    {
-      spacing = io->load_vector_spacing();
-      direction  .reset(io->load_fiber_direction_dataset  (offset, size, stride, false));
-      inclination.reset(io->load_fiber_inclination_dataset(offset, size, stride, false));
-      
-      calculate_odfs(
-        cublas_,
-        cusolver_,
-        coefficient_dimensions,
-        block_dimensions,
-        histogram_dimensions,
-        max_degree,
-        direction    .get().data(),
-        inclination  .get().data(),
-        coefficients_.data(),
-        [&](const std::string& message) { logger_->info(message); });
-    }
-    catch (std::exception& exception)
-    {
-      try
-      {
-        logger_->warn(std::string("Unable to load direction maps. Attempting to load unit vectors instead."));
-        unit_vector.reset(io->load_fiber_unit_vectors_dataset(offset, size, stride, false));
-
-        calculate_odfs(
-          cublas_,
-          cusolver_,
-          coefficient_dimensions,
-          block_dimensions,
-          histogram_dimensions,
-          max_degree,
-          reinterpret_cast<float3*>(unit_vector.get().data()),
-          coefficients_.data(),
-          [&](const std::string& message) { logger_->info(message); });
-      }
-      catch (std::exception& exception2)
-      {
-        logger_->error(std::string(exception2.what()));
-      }
-    }
-  });
-  while (future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
-    QApplication::processEvents();
-  
-  // Upload data to GPU.
-  uint3  cuda_size    {unsigned(coefficient_dimensions.x), unsigned(coefficient_dimensions.y), unsigned(coefficient_dimensions.z)};
-  float3 cuda_spacing {spacing[0], spacing[1], spacing[2]};
-  if (coefficients_.num_elements() > 0)
-    odf_field_->set_data(
-      cuda_size, 
-      unsigned(coefficients_.shape()[3]),
-      coefficients_.data(),
-      sampling_dimensions, 
-      cuda_spacing,
-      block_dimensions, 
-      1.0F, 
-      checkbox_clustering_enabled->isChecked(),
-      threshold_multiplier_ * float(slider_threshold->value()),
-      [&] (const std::string& message) { logger_->info(message); });
-  
-  selector            ->setEnabled(true);
-  button_extract_peaks->setEnabled(true);
-  button_calculate    ->setEnabled(true);
-  owner_->viewer      ->set_wait_spinner_enabled(false);
-  owner_->viewer      ->update();
-
-  logger_->info(std::string("Update successful."));
-}
-void fdm_plugin::extract_peaks()
-{
-  logger_->info(std::string("Extracting peaks..."));
-
-  auto selector = owner_->get_plugin<pli::selector_plugin>();
-
-  owner_->viewer      ->set_wait_spinner_enabled(true);
-  button_calculate    ->setEnabled(false);
-  button_extract_peaks->setEnabled(false);
-  selector            ->setEnabled(false);
-
-  // TODO: Apply peak extraction.
-  //auto shape = coefficients_.shape();
-  //for(auto x = 0; x < shape[0]; x++)
-  //  for(auto y = 0; y < shape[1]; y++)
-  //    for(auto z = 0; z < shape[2]; z++)
-  //      for(auto v = 0; v < shape[3]; v++)
-  //        logger_->info("[" + boost::lexical_cast<std::string>(x) + "," + boost::lexical_cast<std::string>(y) + "," + boost::lexical_cast<std::string>(z) + "]: ");
-  
-  selector            ->setEnabled(true);
-  button_calculate    ->setEnabled(true);
-  button_extract_peaks->setEnabled(true);
-  owner_->viewer      ->set_wait_spinner_enabled(false);
-  owner_->viewer      ->update();
-
-  logger_->info(std::string("Extraction successful."));
-}
-
-void fdm_plugin::set_visible_layers() const
-{
-  odf_field_->set_visible_layers({
-    checkbox_depth_0->isChecked(),
-    checkbox_depth_1->isChecked(),
-    checkbox_depth_2->isChecked(),
-    checkbox_depth_3->isChecked(),
-    checkbox_depth_4->isChecked(),
-    checkbox_depth_5->isChecked(),
-    checkbox_depth_6->isChecked(),
-    checkbox_depth_7->isChecked(),
-    checkbox_depth_8->isChecked(),
-    checkbox_depth_9->isChecked()
-  });
-  owner_->viewer->update();
-}
-}
diff --git a/source/ui/plugins/fom_plugin.cpp b/source/ui/plugins/fom_plugin.cpp
index 41851cf2298433a00d0b24c3f98a78f73e5e1496..b96936881c2f8759dcf6b6a20137775fb44f0a03 100644
--- a/source/ui/plugins/fom_plugin.cpp
+++ b/source/ui/plugins/fom_plugin.cpp
@@ -1,137 +1,91 @@
-#include /* implements */ <ui/plugins/fom_plugin.hpp>
+#include <pli_vis/ui/plugins/fom_plugin.hpp>
 
 #include <limits>
 
 #include <boost/format.hpp>
-#include <boost/optional.hpp>
+#include <vector_functions.hpp>
 
-#include <ui/plugins/data_plugin.hpp>
-#include <ui/plugins/selector_plugin.hpp>
-#include <ui/window.hpp>
-#include <utility/line_edit_utility.hpp>
-#include <utility/qt_text_browser_sink.hpp>
-#include <visualization/vector_field.hpp>
+#include <pli_vis/ui/plugins/color_plugin.hpp>
+#include <pli_vis/ui/plugins/data_plugin.hpp>
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+#include <pli_vis/visualization/algorithms/vector_field.hpp>
 
 namespace pli
 {
 fom_plugin::fom_plugin(QWidget* parent) : plugin(parent)
 {
-  setupUi(this);
-  
   line_edit_fiber_scale->setValidator(new QDoubleValidator(0, std::numeric_limits<double>::max(), 10, this));
 
-  connect(checkbox_enabled     , &QCheckBox::stateChanged      , [&] (bool state)
+  line_edit_rate_of_decay->setText(QString::fromStdString(std::to_string(slider_rate_of_decay->value())));
+
+  connect(checkbox_enabled                    , &QCheckBox::stateChanged      , [&] (bool state)
   {
     logger_->info(std::string(state ? "Enabled." : "Disabled."));
     vector_field_->set_active(state);
   });
-  connect(slider_fiber_scale   , &QxtSpanSlider::valueChanged  , [&]
+  connect(slider_fiber_scale                  , &QxtSpanSlider::valueChanged  , [&]
   {
-    auto scale = float(slider_fiber_scale->value()) / slider_fiber_scale->maximum();
-    line_edit_fiber_scale->setText(QString::fromStdString((boost::format("%.4f") % scale).str()));
+    line_edit_fiber_scale->setText(QString::fromStdString((boost::format("%.4f") % (float(slider_fiber_scale->value()) / slider_fiber_scale->maximum())).str()));
   });
-  connect(slider_fiber_scale   , &QxtSpanSlider::sliderReleased, [&]
+  connect(slider_fiber_scale                  , &QxtSpanSlider::sliderReleased, [&]
   {
-    upload();
+    vector_field_->set_scale(line_edit::get_text<float>(line_edit_fiber_scale));
   });
-  connect(line_edit_fiber_scale, &QLineEdit::editingFinished   , [&]
+  connect(line_edit_fiber_scale               , &QLineEdit::editingFinished   , [&]
   {
-    auto scale = line_edit_utility::get_text<double>(line_edit_fiber_scale);
-    slider_fiber_scale->setValue(scale * slider_fiber_scale->maximum());
-    upload();
+    auto value = line_edit::get_text<float>(line_edit_fiber_scale);
+    slider_fiber_scale->setValue (value * slider_fiber_scale->maximum());
+    vector_field_     ->set_scale(value);
+  });
+  connect(checkbox_view_dependent             , &QCheckBox::stateChanged      , [&] (bool state)
+  {
+    logger_->info(std::string("View dependent transparency is " + state ? "enabled." : "disabled."));
+    vector_field_->set_view_dependent_transparency(state);
+    label_rate_of_decay    ->setEnabled(state);
+    slider_rate_of_decay   ->setEnabled(state);
+    line_edit_rate_of_decay->setEnabled(state);
+  });
+  connect(slider_rate_of_decay                , &QxtSpanSlider::valueChanged  , [&]
+  {
+    line_edit_rate_of_decay->setText(QString::fromStdString(std::to_string(slider_rate_of_decay->value())));
+    vector_field_->set_view_dependent_rate_of_decay(slider_rate_of_decay->value());
+  });
+  connect(line_edit_rate_of_decay             , &QLineEdit::editingFinished   , [&]
+  {
+    auto value = line_edit::get_text<int>(line_edit_rate_of_decay);
+    slider_rate_of_decay->setValue(value);
+    vector_field_->set_view_dependent_rate_of_decay(value);
   });
 }
 
 void fom_plugin::start ()
 {
-  set_sink(std::make_shared<qt_text_browser_sink>(owner_->console));
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+
+  vector_field_ = owner_->viewer->add_renderable<vector_field>();
 
-  connect(owner_->get_plugin<data_plugin>    (), &data_plugin::on_change    , [&]
+  connect(owner_->get_plugin<data_plugin>(), &data_plugin::on_load    , [&]
   {
     upload();
   });
-  connect(owner_->get_plugin<selector_plugin>(), &selector_plugin::on_change, [&]
+  connect(owner_->get_plugin<color_plugin>(), &color_plugin::on_change, [&] (int mode, float k, bool inverted)
   {
-    upload();
+    vector_field_->set_color_mapping(mode, k, inverted);
   });
-  
-  vector_field_ = owner_->viewer->add_renderable<vector_field>();
 
-  logger_->info(std::string("Start successful."));
 }
 void fom_plugin::upload()
 {
   logger_->info(std::string("Updating viewer..."));
 
-  auto io       = owner_->get_plugin<pli::data_plugin>    ()->io();
-  auto selector = owner_->get_plugin<pli::selector_plugin>();
-  auto offset   = selector->selection_offset();
-  auto size     = selector->selection_size  ();
-  auto stride   = selector->selection_stride();
-  auto scale    = line_edit_utility::get_text<float>(line_edit_fiber_scale);
-
-  if  (io == nullptr || size[0] == 0 || size[1] == 0 || size[2] == 0)
-  {
-    logger_->info(std::string("Update failed: No data."));
-    return;
-  }
-
-  size = { size[0] / stride[0], size[1] / stride[1], size[2] / stride[2] };
-
-  owner_->viewer->set_wait_spinner_enabled(true);
-  selector->setEnabled(false);
-
-  // Load data from hard drive (on another thread).
-  std::array<float, 3>                          spacing    ;
-  boost::optional<boost::multi_array<float, 3>> direction  ;
-  boost::optional<boost::multi_array<float, 3>> inclination;
-  boost::optional<boost::multi_array<float, 4>> unit_vector;
-  future_ = std::async(std::launch::async, [&]
-  {
-    try
-    {
-      spacing = io->load_vector_spacing();
-      direction  .reset(io->load_fiber_direction_dataset   (offset, size, stride, false));
-      inclination.reset(io->load_fiber_inclination_dataset (offset, size, stride, false));
-    }
-    catch (std::exception& exception)
-    {
-      try
-      {
-        logger_->warn(std::string("Unable to load direction maps. Attempting to load unit vectors instead."));
-        unit_vector.reset(io->load_fiber_unit_vectors_dataset(offset, size, stride, false));
-      }
-      catch (std::exception& exception2)
-      {
-        logger_->error(std::string(exception2.what()));
-      }
-    }
-  });
-  while(future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
-    QApplication::processEvents();
-
-  // Upload data to GPU.
-  uint3  cuda_size    {unsigned(size[0]), unsigned(size[1]), unsigned(size[2])};
-  float3 cuda_spacing {spacing[0], spacing[1], spacing[2]};
-  if (direction.is_initialized() && direction.get().num_elements() > 0)
-    vector_field_->set_data(
-      cuda_size, 
-      direction  .get().data(), 
-      inclination.get().data(), 
-      cuda_spacing, 
-      scale, 
-      [&] (const std::string& message) { logger_->info(message); });
-  if (unit_vector.is_initialized() && unit_vector.get().num_elements() > 0)
-    vector_field_->set_data(
-      cuda_size,
-      reinterpret_cast<float3*>(unit_vector.get().data()),
-      cuda_spacing,
-      scale,
-      [&] (const std::string& message) { logger_->info(message); });
-
-  selector->setEnabled(true);
-  owner_->viewer->set_wait_spinner_enabled(false);
-  owner_->viewer->update();
+  auto vectors = owner_->get_plugin<data_plugin>()->generate_vectors(true);
+  vector_field_->set_data(
+    make_uint3(vectors.shape()[0], vectors.shape()[1], vectors.shape()[2]),
+    1,
+    vectors.data(),
+    [&] (const std::string& message) { logger_->info(message); });
 
   logger_->info(std::string("Update successful."));
 }
diff --git a/source/ui/plugins/global_tractography_plugin.cpp b/source/ui/plugins/global_tractography_plugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b291f5f754d578b2412ebe79673b9ccf37c3674
--- /dev/null
+++ b/source/ui/plugins/global_tractography_plugin.cpp
@@ -0,0 +1,53 @@
+#include <pli_vis/ui/plugins/global_tractography_plugin.hpp>
+
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+
+namespace pli
+{
+global_tractography_plugin::global_tractography_plugin(QWidget* parent) : plugin(parent)
+{
+  connect(checkbox_enabled      , &QCheckBox::stateChanged, [&] (bool state)
+  {
+    logger_->info(std::string(state ? "Enabled." : "Disabled."));
+    streamline_renderer_->set_active(state);
+  });
+  connect(button_trace_selection, &QPushButton::clicked   , [&]
+  {
+    trace();
+  });
+}
+
+void global_tractography_plugin::start()
+{
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+  streamline_renderer_ = owner_->viewer->add_renderable<streamline_renderer>();
+}
+void global_tractography_plugin::trace()
+{
+  owner_ ->set_is_loading(true);
+  logger_->info          (std::string("Tracing..."));
+
+  std::vector<float3> points    ;
+  std::vector<float3> directions;
+  future_ = std::async(std::launch::async, [&]
+  {
+    try
+    {
+      auto vectors = owner_->get_plugin<data_plugin>()->generate_vectors(true);
+      auto shape   = vectors.shape();
+
+      // TODO.
+    }
+    catch (std::exception& exception)
+    {
+      logger_->error(std::string(exception.what()));
+    }
+  });
+  while (future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
+    QApplication::processEvents();
+
+  logger_->info          (std::string("Trace complete."));
+  owner_ ->set_is_loading(false);
+}
+}
diff --git a/source/ui/plugins/interactor_plugin.cpp b/source/ui/plugins/interactor_plugin.cpp
index d4fc85961ac6b123985badc8ad32076d8e64e254..b2abbece44f7cd3290b2cb51fa216341ffcb9713 100644
--- a/source/ui/plugins/interactor_plugin.cpp
+++ b/source/ui/plugins/interactor_plugin.cpp
@@ -1,66 +1,96 @@
-#include /* implements */ <ui/plugins/interactor_plugin.hpp>
+#include <pli_vis/ui/plugins/interactor_plugin.hpp>
 
 #include <boost/format.hpp>
 
-#include <ui/window.hpp>
-#include <utility/line_edit_utility.hpp>
-#include <utility/qt_text_browser_sink.hpp>
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+#include <pli_vis/visualization/interactors/first_person_interactor.hpp>
+#include <pli_vis/visualization/interactors/orbit_interactor.hpp>
+#include <pli_vis/visualization/interactors/simple_interactor.hpp>
 
 namespace pli
 {
 interactor_plugin::interactor_plugin(QWidget* parent) : plugin(parent)
 {
-  setupUi(this);
-  
   line_edit_move_speed->setValidator(new QDoubleValidator(0, std::numeric_limits<double>::max(), 4, this));
   line_edit_look_speed->setValidator(new QDoubleValidator(0, std::numeric_limits<double>::max(), 4, this));
-
-  connect(slider_move_speed   , &QSlider::valueChanged     , [&]
+  
+  connect(radio_button_vtklike     , &QRadioButton::clicked     , [&]
+  {
+    logger_->info(std::string("VTK-like controls selected."));
+    owner_->viewer->set_interactor<simple_interactor>();
+    owner_->viewer->interactor()->set_move_speed(float(slider_move_speed->value()) / slider_move_speed->maximum());
+    owner_->viewer->interactor()->set_look_speed(float(slider_look_speed->value()) / slider_look_speed->maximum());
+  });
+  connect(radio_button_orbit       , &QRadioButton::clicked     , [&]
+  {
+    logger_->info(std::string("Orbit controls selected."));
+    owner_->viewer->set_interactor<orbit_interactor>();
+    owner_->viewer->interactor()->set_move_speed(float(slider_move_speed->value()) / slider_move_speed->maximum());
+    owner_->viewer->interactor()->set_look_speed(float(slider_look_speed->value()) / slider_look_speed->maximum());
+  });
+  connect(radio_button_wasd        , &QRadioButton::clicked     , [&]
+  {
+    logger_->info(std::string("WASD controls selected."));
+    owner_->viewer->set_interactor<first_person_interactor>();
+    owner_->viewer->interactor()->set_move_speed(float(slider_move_speed->value()) / slider_move_speed->maximum());
+    owner_->viewer->interactor()->set_look_speed(float(slider_look_speed->value()) / slider_look_speed->maximum());
+  });
+  connect(radio_button_orthographic, &QRadioButton::clicked     , [&]
+  {
+    logger_->info(std::string("Orthographic projection selected."));
+    owner_->viewer->camera()->set_orthographic(true);
+    owner_->viewer->reset_camera_transform();
+  });
+  connect(radio_button_perspective , &QRadioButton::clicked     , [&]
+  {
+    logger_->info(std::string("Perspective projection selected."));
+    owner_->viewer->camera()->set_orthographic(false);
+    owner_->viewer->reset_camera_transform();
+  });
+  connect(slider_move_speed        , &QSlider::valueChanged     , [&]
   {
     auto speed = float(slider_move_speed->value()) / slider_move_speed->maximum();
     line_edit_move_speed->setText(QString::fromStdString((boost::format("%.4f") % speed).str()));
     owner_->viewer->interactor()->set_move_speed(speed);
   });
-  connect(slider_look_speed   , &QSlider::valueChanged     , [&]
+  connect(slider_look_speed        , &QSlider::valueChanged     , [&]
   {
     auto speed = float(slider_look_speed->value()) / slider_look_speed->maximum();
     line_edit_look_speed->setText(QString::fromStdString((boost::format("%.4f") % speed).str()));
     owner_->viewer->interactor()->set_look_speed(speed);
   });
-  connect(line_edit_move_speed, &QLineEdit::editingFinished, [&]
+  connect(line_edit_move_speed     , &QLineEdit::editingFinished, [&]
   {
-    auto speed = line_edit_utility::get_text<double>(line_edit_move_speed);
+    auto speed = line_edit::get_text<double>(line_edit_move_speed);
     slider_move_speed->setValue(speed * slider_move_speed->maximum());
     owner_->viewer->interactor()->set_move_speed(speed);
   });
-  connect(line_edit_look_speed, &QLineEdit::editingFinished, [&]
+  connect(line_edit_look_speed     , &QLineEdit::editingFinished, [&]
   {
-    auto speed = line_edit_utility::get_text<double>(line_edit_look_speed);
+    auto speed = line_edit::get_text<double>(line_edit_look_speed);
     slider_look_speed->setValue(speed * slider_look_speed->maximum());
     owner_->viewer->interactor()->set_look_speed(speed);
   });
-  connect(button_reset_camera , &QPushButton::clicked      , [&]
+  connect(button_reset_camera      , &QPushButton::clicked      , [&]
   {
     logger_->info(std::string("Resetting camera transform."));
-    owner_->viewer->camera()->set_translation({0, 0, 1});
-    owner_->viewer->camera()->look_at        ({0, 0, 0});
+    owner_->viewer->reset_camera_transform();
   });
 }
 
 void interactor_plugin::start()
 {
-  set_sink(std::make_shared<qt_text_browser_sink>(owner_->console));
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
 
   auto move_value = double(slider_move_speed->value()) / slider_move_speed->maximum();
   auto look_value = double(slider_look_speed->value()) / slider_look_speed->maximum();
-
   line_edit_move_speed->setText(QString::fromStdString((boost::format("%.4f") % move_value).str()));
   line_edit_look_speed->setText(QString::fromStdString((boost::format("%.4f") % look_value).str()));
 
   auto interactor = owner_->viewer->interactor();
   interactor->set_move_speed(move_value);
   interactor->set_look_speed(look_value);
-
-  logger_->info(std::string("Start successful."));
 }
 }
diff --git a/source/ui/plugins/local_tractography_plugin.cpp b/source/ui/plugins/local_tractography_plugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..004e3423ae20ff0e0c431993b159f92d8f36c17f
--- /dev/null
+++ b/source/ui/plugins/local_tractography_plugin.cpp
@@ -0,0 +1,392 @@
+#include <pli_vis/ui/plugins/local_tractography_plugin.hpp>
+
+#include <algorithm>
+#include <random>
+
+#include <boost/format.hpp>
+#include <tangent-base/default_tracers.hpp>
+
+#include <pli_vis/cuda/pt/tracer.h>
+#include <pli_vis/cuda/utility/vector_ops.h>
+#include <pli_vis/ui/plugins/data_plugin.hpp>
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+#include <pli_vis/utility/make_even.hpp>
+#include <pli_vis/visualization/algorithms/streamline_renderer.hpp>
+#include <pli_vis/visualization/algorithms/lineao_streamline_renderer.hpp>
+
+namespace pli
+{
+local_tractography_plugin::local_tractography_plugin(QWidget* parent) : plugin(parent)
+{
+  line_edit_integration_step->setValidator(new QDoubleValidator(0, std::numeric_limits<double>::max(), 10, this));
+  line_edit_iterations      ->setValidator(new QIntValidator   (0, std::numeric_limits<int>   ::max(),     this));
+ 
+  line_edit_integration_step->setText(QString::fromStdString((boost::format("%.4f") % (float(slider_integration_step->value()) / slider_integration_step->maximum())).str()));
+  line_edit_iterations      ->setText(QString::fromStdString(std::to_string(slider_iterations   ->value())));
+
+  connect(checkbox_enabled          , &QCheckBox::stateChanged          , [&] (bool state)
+  {
+    logger_->info(std::string(state ? "Enabled." : "Disabled."));
+    streamline_renderer_->set_active(state);
+  });
+  connect(image                     , &roi_selector::on_selection_change, [&] (const std::array<float, 2> offset_perc, const std::array<float, 2> size_perc)
+  {
+    std::array<int, 2> offset { make_even(int(offset_perc[0] * slider_x->maximum())), make_even(int(offset_perc[1] * slider_y->maximum())) };
+    std::array<int, 2> size   { make_even(int(size_perc  [0] * slider_x->maximum())), make_even(int(size_perc  [1] * slider_y->maximum())) };
+    line_edit_offset_x->setText      (QString::fromStdString(std::to_string(offset[0])));
+    line_edit_size_x  ->setText      (QString::fromStdString(std::to_string(size  [0])));
+    line_edit_offset_y->setText      (QString::fromStdString(std::to_string(offset[1])));
+    line_edit_size_y  ->setText      (QString::fromStdString(std::to_string(size  [1])));
+    slider_x          ->setLowerValue(offset[0]);
+    slider_x          ->setUpperValue(offset[0] + size[0]);
+    slider_y          ->setLowerValue(offset[1]);
+    slider_y          ->setUpperValue(offset[1] + size[1]);
+  });
+  connect(slider_x                  , &QxtSpanSlider::lowerValueChanged , [&] (int value)
+  {
+    line_edit_offset_x->setText(QString::fromStdString(std::to_string(make_even(value))));
+    line_edit_size_x  ->setText(QString::fromStdString(std::to_string(make_even(slider_x->upperValue() - value))));
+  });
+  connect(slider_x                  , &QxtSpanSlider::upperValueChanged , [&] (int value)
+  {
+    line_edit_size_x->setText(QString::fromStdString(std::to_string(make_even(value - slider_x->lowerValue()))));
+  });
+  connect(slider_x                  , &QxtSpanSlider::sliderReleased    , [&] 
+  {
+    image->set_selection_offset_percentage({static_cast<float>(slider_x->lowerValue())                          / slider_x->maximum(), image->selection_offset_percentage()[1]});
+    image->set_selection_size_percentage  ({static_cast<float>(slider_x->upperValue() - slider_x->lowerValue()) / slider_x->maximum(), image->selection_size_percentage  ()[1]});
+  });
+  connect(slider_y                  , &QxtSpanSlider::lowerValueChanged , [&] (int value)
+  {
+    line_edit_offset_y->setText(QString::fromStdString(std::to_string(make_even(value))));
+    line_edit_size_y  ->setText(QString::fromStdString(std::to_string(make_even(slider_y->upperValue() - value))));
+  });
+  connect(slider_y                  , &QxtSpanSlider::upperValueChanged , [&] (int value)
+  {
+    line_edit_size_y->setText(QString::fromStdString(std::to_string(make_even(value - slider_y->lowerValue()))));
+  });
+  connect(slider_y                  , &QxtSpanSlider::sliderReleased    , [&] 
+  {
+    image->set_selection_offset_percentage({image->selection_offset_percentage()[0], static_cast<float>(slider_y->lowerValue())                          / slider_y->maximum()});
+    image->set_selection_size_percentage  ({image->selection_size_percentage  ()[0], static_cast<float>(slider_y->upperValue() - slider_y->lowerValue()) / slider_y->maximum()});
+  });
+  connect(slider_z                  , &QxtSpanSlider::lowerValueChanged , [&] (int value)
+  {
+    line_edit_offset_z->setText(QString::fromStdString(std::to_string(value)));
+    line_edit_size_z  ->setText(QString::fromStdString(std::to_string(slider_z->upperValue() - value)));
+  });
+  connect(slider_z                  , &QxtSpanSlider::upperValueChanged , [&] (int value)
+  {
+    line_edit_size_z->setText(QString::fromStdString(std::to_string(value - slider_z->lowerValue())));
+  });
+  connect(line_edit_offset_x        , &QLineEdit::editingFinished       , [&] 
+  {
+    auto value = std::max(std::min(make_even(line_edit::get_text<int>(line_edit_offset_x)), int(slider_x->maximum())), int(slider_x->minimum()));
+    if (slider_x->upperValue() < value)
+      slider_x->setUpperValue(value);
+    slider_x        ->setLowerValue                  (value);
+    image           ->set_selection_offset_percentage({static_cast<float>(value) / slider_x->maximum(), image->selection_offset_percentage()[1]});
+    image           ->set_selection_size_percentage  ({static_cast<float>(slider_x->upperValue() - slider_x->lowerValue()) / slider_x->maximum(), image->selection_size_percentage()[1]});
+    line_edit_size_x->setText                        (QString::fromStdString(std::to_string(make_even(slider_x->upperValue() - value))));
+  });
+  connect(line_edit_size_x          , &QLineEdit::editingFinished       , [&] 
+  {
+    auto value = std::max(std::min(make_even(line_edit::get_text<int>(line_edit_size_x)), int(slider_x->maximum())), int(slider_x->minimum()));
+    slider_x->setUpperValue                (make_even(slider_x->lowerValue() + value));
+    image   ->set_selection_size_percentage({static_cast<float>(value) / slider_x->maximum(), image->selection_size_percentage()[1]});
+  });
+  connect(line_edit_offset_y        , &QLineEdit::editingFinished       , [&] 
+  {
+    auto value = std::max(std::min(make_even(line_edit::get_text<int>(line_edit_offset_y)), int(slider_y->maximum())), int(slider_y->minimum()));
+    if (slider_y->upperValue() < value)
+      slider_y->setUpperValue(value);
+    slider_y        ->setLowerValue                  (value);
+    image           ->set_selection_offset_percentage({image->selection_offset_percentage()[0], static_cast<float>(value) / slider_y->maximum()});
+    image           ->set_selection_size_percentage  ({image->selection_size_percentage  ()[0], static_cast<float>(slider_y->upperValue() - slider_y->lowerValue()) / slider_y->maximum()});
+    line_edit_size_y->setText                        (QString::fromStdString(std::to_string(make_even(slider_y->upperValue() - value))));
+  });
+  connect(line_edit_size_y          , &QLineEdit::editingFinished       , [&] 
+  {
+    auto value = std::max(std::min(make_even(line_edit::get_text<int>(line_edit_size_y)), int(slider_y->maximum())), int(slider_y->minimum()));
+    slider_y->setUpperValue                (make_even(slider_y->lowerValue() + value));
+    image   ->set_selection_size_percentage({image->selection_size_percentage()[0], static_cast<float>(value) / slider_y->maximum()});
+  });
+  connect(line_edit_offset_z        , &QLineEdit::editingFinished       , [&] 
+  {
+    auto value = std::max(std::min(line_edit::get_text<int>(line_edit_offset_z), int(slider_z->maximum())), int(slider_z->minimum()));
+    if (slider_z->upperValue() < value)
+      slider_z->setUpperValue(value + 1);
+    slider_z        ->setLowerValue(value);
+    line_edit_size_z->setText      (QString::fromStdString(std::to_string(slider_z->upperValue() - value)));
+  });
+  connect(line_edit_size_z          , &QLineEdit::editingFinished       , [&] 
+  {
+    auto value = std::min(line_edit::get_text<int>(line_edit_size_z), int(slider_z->maximum() - slider_z->minimum()));
+    slider_z->setUpperValue(slider_z->lowerValue() + value);
+  });
+  connect(slider_integration_step   , &QSlider::valueChanged            , [&] 
+  {
+    line_edit_integration_step->setText(QString::fromStdString((boost::format("%.4f") % (float(slider_integration_step->value()) / slider_integration_step->maximum())).str()));
+  });
+  connect(line_edit_integration_step, &QLineEdit::editingFinished       , [&] 
+  {
+    slider_integration_step->setValue(line_edit::get_text<double>(line_edit_integration_step) * slider_integration_step->maximum());
+  });
+  connect(slider_iterations         , &QSlider::valueChanged            , [&] 
+  {
+    line_edit_iterations->setText(QString::fromStdString(boost::lexical_cast<std::string>(slider_iterations->value())));
+  });
+  connect(line_edit_iterations      , &QLineEdit::editingFinished       , [&] 
+  {
+    slider_iterations->setValue(line_edit::get_text<int>(line_edit_iterations));
+  });
+  connect(button_trace_selection, &QPushButton::clicked, [&]
+  {
+    trace();
+  });
+  connect(radio_button_cpu          , &QRadioButton::clicked            , [&]
+  {
+    logger_->info(std::string("CPU particle tracing selected."));
+    gpu_tracing_ = false;
+  });
+  connect(radio_button_gpu          , &QRadioButton::clicked            , [&]
+  {
+    logger_->info(std::string("GPU particle tracing selected."));
+    gpu_tracing_ = true;
+  });
+  connect(radio_button_regular      , &QRadioButton::clicked            , [&]
+  {
+    owner_->viewer->remove_renderable(streamline_renderer_);
+    streamline_renderer_ = owner_->viewer->add_renderable<streamline_renderer>();
+  });
+  connect(radio_button_lineao       , &QRadioButton::clicked            , [&]
+  {
+    owner_->viewer->remove_renderable(streamline_renderer_);
+    streamline_renderer_ = owner_->viewer->add_renderable<lineao_streamline_renderer>();
+  });
+}
+
+void local_tractography_plugin::start()
+{
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+
+  if(radio_button_lineao->isChecked())
+    streamline_renderer_ = owner_->viewer->add_renderable<lineao_streamline_renderer>();
+  else
+    streamline_renderer_ = owner_->viewer->add_renderable<streamline_renderer>();
+
+  connect(owner_->toolbox, &QToolBox::currentChanged, [&](int tab)
+  {
+    // Hack for enforcing a UI update.
+    auto sizes = owner_->splitter->sizes();
+    owner_->splitter->setSizes(QList<int>{0, sizes[1]});
+    owner_->splitter->setSizes(QList<int>{sizes[0], sizes[1]});
+    owner_->update();
+    update();
+  });
+  connect(owner_->get_plugin<pli::data_plugin>(), &data_plugin::on_load, [&]
+  {
+    auto data_plugin = owner_->get_plugin<pli::data_plugin>();
+
+    // Adjust slider boundaries.
+    auto size = data_plugin->selection_size  ();
+    slider_x->setMinimum(0); slider_x->setMaximum(size[0]);
+    slider_y->setMinimum(0); slider_y->setMaximum(size[1]);
+    slider_z->setMinimum(0); slider_z->setMaximum(size[2]);
+
+    // Generate selection image.
+    auto selection_image = data_plugin->generate_selection_image();
+    auto shape           = selection_image.shape();
+    image->setPixmap(QPixmap::fromImage(QImage(selection_image.data(), shape[0], shape[1], QImage::Format::Format_Grayscale8)));
+
+    // Adjust widget size.
+    image    ->setSizeIncrement(shape[0], shape[1]);
+    letterbox->setWidget(image);
+    letterbox->update();
+    update();
+    
+    // Hack for enforcing a UI update.
+    auto sizes = owner_->splitter->sizes();
+    owner_->splitter->setSizes(QList<int>{0       , sizes[1]});
+    owner_->splitter->setSizes(QList<int>{sizes[0], sizes[1]});
+    owner_->update();
+    update();
+  });
+  connect(owner_->get_plugin<color_plugin>(), &color_plugin::on_change, [&](int mode, float k, bool inverted)
+  {
+    streamline_renderer_->set_color_mapping(mode, k, inverted);
+  });
+}
+void local_tractography_plugin::trace()
+{
+  owner_->set_is_loading(true);
+
+  logger_->info(std::string("Tracing..."));
+
+  std::vector<float3> points        ;
+  std::vector<float3> directions    ;
+  std::vector<float4> random_vectors;
+  future_ = std::async(std::launch::async, [&]
+  {
+    try
+    {
+      auto vectors = owner_->get_plugin<data_plugin>()->generate_vectors(true);
+      auto shape   = vectors.shape();
+
+      if(!gpu_tracing_)
+      {
+        tangent::CartesianGrid data(tangent::grid_dim_t{{shape[0], shape[1], shape[2]}}, tangent::vector_t{{1.0, 1.0, 1.0}});
+        auto data_ptr = data.GetVectorPointer(0);
+        for (auto x = 0; x < shape[0]; x++)
+          for (auto y = 0; y < shape[1]; y++)
+            for (auto z = 0; z < shape[2]; z++)
+            {
+              auto vector = vectors[x][y][z];
+              data_ptr[x + shape[0] * (y + shape[1] * z)] = tangent::vector_t{{vector.x, vector.y, vector.z}};
+            }
+
+        auto offset = seed_offset();
+        auto size   = seed_size  ();
+        auto stride = seed_stride();
+        std::vector<tangent::point_t> seeds;
+        for (auto x = offset[0]; x < offset[0] + size[0]; x+= stride[0])
+          for (auto y = offset[1]; y < offset[1] + size[1]; y += stride[1])
+            for (auto z = offset[2]; z < offset[2] + size[2]; z += stride[2])
+              seeds.push_back({{float(x), float(y), float(z), 0.0F}});
+
+        tangent::TraceRecorder recorder;
+        tangent::OmpCartGridStreamlineTracer tracer(&recorder);
+        tracer.SetData              (&data);
+        tracer.SetIntegrationStep   (float(slider_integration_step->value()) / slider_integration_step->maximum());
+        tracer.SetNumberOfIterations(slider_iterations->value());
+        auto output = tracer.TraceSeeds(seeds);
+
+        auto& population = recorder.GetPopulation();
+        for(auto i = 0; i < population.GetNumberOfTraces(); i++)
+        {
+          auto& path = population[i];
+          for(auto j = 0; j < path.size() - 1; j++)
+          {
+            float3 start {path[j]    [0], path[j]    [1], path[j]    [2]};
+            float3 end   {path[j + 1][0], path[j + 1][1], path[j + 1][2]};
+            points.push_back(start);
+            points.push_back(end  );
+            directions.push_back(normalize(end   - start));
+            directions.push_back(normalize(start - end  ));
+          }
+        }
+      }
+      else
+      {
+        std::vector<float3> data(shape[0] * shape[1] * shape[2]);
+        for (auto x = 0; x < shape[0]; x++)
+          for (auto y = 0; y < shape[1]; y++)
+            for (auto z = 0; z < shape[2]; z++)
+              data[x + shape[0] * (y + shape[1] * z)] = vectors[x][y][z];
+
+        std::vector<float3> seeds;
+        auto offset = seed_offset();
+        auto size   = seed_size  ();
+        auto stride = seed_stride();
+        for (auto x = offset[0]; x < offset[0] + size[0]; x += stride[0])
+          for (auto y = offset[1]; y < offset[1] + size[1]; y += stride[1])
+            for (auto z = offset[2]; z < offset[2] + size[2]; z += stride[2])
+              seeds.push_back(float3{float(x), float(y), float(z)});
+        
+        auto traces = cupt::trace(
+          slider_iterations->value(),
+          float(slider_integration_step->value()) / slider_integration_step->maximum(),
+          uint3  {unsigned(shape[0]), unsigned(shape[1]), unsigned(shape[2])},
+          float3 {1.0f, 1.0f, 1.0f},
+          data ,
+          seeds);
+
+        for (auto i = 0; i < traces.size(); ++i)
+          for (auto j = 0; j < traces[i].size() - 1; ++j)
+          {
+            auto& start = traces[i][j  ];
+            auto& end   = traces[i][j+1];
+            if(end.x == 0.0f && end.y == 0.0f && end.z == 0.0f)
+              break;
+            points    .push_back(start);
+            points    .push_back(end  ); 
+            directions.push_back(normalize(end   - start));
+            directions.push_back(normalize(start - end  ));
+          }
+      }
+
+      std::random_device                    random_device;
+      std::mt19937                          mersenne_twister(random_device());
+      std::uniform_real_distribution<float> distribution;
+
+      auto lineao_renderer = dynamic_cast<lineao_streamline_renderer*>(streamline_renderer_);
+      if  (lineao_renderer)
+      {
+        auto screen_size = lineao_renderer->screen_size();
+        random_vectors.resize(screen_size[0] * screen_size[1] * lineao_renderer->ao_samples());
+        std::generate(random_vectors.begin(), random_vectors.end(), [&mersenne_twister, &distribution]()
+        {
+          return float4{
+            distribution(mersenne_twister),
+            distribution(mersenne_twister),
+            distribution(mersenne_twister),
+            distribution(mersenne_twister) };
+        });
+      }
+    }
+    catch (std::exception& exception)
+    {
+      logger_->error(std::string(exception.what()));
+    }
+  });
+  while (future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
+    QApplication::processEvents();
+
+  if (points.size() > 0 && directions.size() > 0)
+  {
+    logger_->info(std::string("Trace successful."));
+
+    auto lineao_renderer  = dynamic_cast<lineao_streamline_renderer*>(streamline_renderer_);
+    if  (lineao_renderer)  lineao_renderer ->set_data(points, directions, random_vectors);
+    
+    auto regular_renderer = dynamic_cast<streamline_renderer*>(streamline_renderer_);
+    if  (regular_renderer) regular_renderer->set_data(points, directions);
+  }
+  else
+  {
+    logger_->info(std::string("Trace failed."));
+  }
+
+  owner_->set_is_loading(false);
+}
+  
+std::array<std::size_t, 3> local_tractography_plugin::seed_offset() const
+{
+  return
+  {
+    line_edit::get_text<std::size_t>(line_edit_offset_x),
+    line_edit::get_text<std::size_t>(line_edit_offset_y),
+    line_edit::get_text<std::size_t>(line_edit_offset_z)
+  };
+}
+std::array<std::size_t, 3> local_tractography_plugin::seed_size  () const
+{
+  return
+  {
+    line_edit::get_text<std::size_t>(line_edit_size_x),
+    line_edit::get_text<std::size_t>(line_edit_size_y),
+    line_edit::get_text<std::size_t>(line_edit_size_z)
+  };
+}
+std::array<std::size_t, 3> local_tractography_plugin::seed_stride() const
+{
+  return
+  {
+    line_edit::get_text<std::size_t>(line_edit_stride_x),
+    line_edit::get_text<std::size_t>(line_edit_stride_y),
+    line_edit::get_text<std::size_t>(line_edit_stride_z)
+  };
+}
+}
diff --git a/source/ui/plugins/odf_plugin.cpp b/source/ui/plugins/odf_plugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..21e45380e671ec84f559e38c39113ac0fb2c6128
--- /dev/null
+++ b/source/ui/plugins/odf_plugin.cpp
@@ -0,0 +1,359 @@
+#include <pli_vis/ui/plugins/odf_plugin.hpp>
+
+#include <limits>
+#include <string>
+
+#include <boost/format.hpp>
+#include <vector_functions.hpp>
+
+#include <pli_vis/cuda/sh/spherical_harmonics.h>
+#include <pli_vis/cuda/utility/vector_ops.h>
+#include <pli_vis/cuda/odf_field.h>
+#include <pli_vis/ui/plugins/data_plugin.hpp>
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+#include <pli_vis/visualization/algorithms/odf_field.hpp>
+
+namespace pli
+{
+odf_plugin::odf_plugin(QWidget* parent) : plugin(parent)
+{
+  line_edit_vector_block_x   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_vector_block_y   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_vector_block_z   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_histogram_theta  ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_histogram_phi    ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_maximum_sh_degree->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_sampling_theta   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_sampling_phi     ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  
+  line_edit_vector_block_x   ->setText(QString::fromStdString(std::to_string(slider_vector_block_x   ->value())));
+  line_edit_vector_block_y   ->setText(QString::fromStdString(std::to_string(slider_vector_block_y   ->value())));
+  line_edit_vector_block_z   ->setText(QString::fromStdString(std::to_string(slider_vector_block_z   ->value())));
+  line_edit_histogram_theta  ->setText(QString::fromStdString(std::to_string(slider_histogram_theta  ->value())));
+  line_edit_histogram_phi    ->setText(QString::fromStdString(std::to_string(slider_histogram_phi    ->value())));
+  line_edit_maximum_sh_degree->setText(QString::fromStdString(std::to_string(slider_maximum_sh_degree->value())));
+  line_edit_sampling_theta   ->setText(QString::fromStdString(std::to_string(slider_sampling_theta   ->value())));
+  line_edit_sampling_phi     ->setText(QString::fromStdString(std::to_string(slider_sampling_phi     ->value())));
+  line_edit_threshold        ->setText(QString::fromStdString((boost::format("%.2f") % (threshold_multiplier_ * slider_threshold->value())).str()));
+
+  connect(checkbox_enabled, &QCheckBox::stateChanged, [&](int state)
+  {
+    logger_->info(std::string(state ? "Enabled." : "Disabled."));
+    odf_field_->set_active(state);
+  });
+  connect(slider_vector_block_x      , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_vector_block_x->setText(QString::fromStdString(std::to_string(slider_vector_block_x->value())));
+  });
+  connect(line_edit_vector_block_x   , &QLineEdit::editingFinished , [&]
+  {
+    slider_vector_block_x->setValue(line_edit::get_text<int>(line_edit_vector_block_x));
+  });
+  connect(slider_vector_block_y      , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_vector_block_y->setText(QString::fromStdString(std::to_string(slider_vector_block_y->value())));
+  });
+  connect(line_edit_vector_block_y   , &QLineEdit::editingFinished , [&]
+  {
+    slider_vector_block_y->setValue(line_edit::get_text<int>(line_edit_vector_block_y));
+  });
+  connect(slider_vector_block_z      , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_vector_block_z->setText(QString::fromStdString(std::to_string(slider_vector_block_z->value())));
+  });
+  connect(line_edit_vector_block_z   , &QLineEdit::editingFinished , [&]
+  {
+    slider_vector_block_z->setValue(line_edit::get_text<int>(line_edit_vector_block_z));
+  });
+  connect(slider_histogram_theta     , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_histogram_theta->setText(QString::fromStdString(std::to_string(slider_histogram_theta->value())));
+  });
+  connect(line_edit_histogram_theta  , &QLineEdit::editingFinished , [&]
+  {
+    slider_histogram_theta->setValue(line_edit::get_text<int>(line_edit_histogram_theta));
+  });
+  connect(slider_histogram_phi       , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_histogram_phi->setText(QString::fromStdString(std::to_string(slider_histogram_phi->value())));
+  });
+  connect(line_edit_histogram_phi    , &QLineEdit::editingFinished , [&]
+  {
+    slider_histogram_phi->setValue(line_edit::get_text<int>(line_edit_histogram_phi));
+  });
+  connect(slider_maximum_sh_degree   , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_maximum_sh_degree->setText(QString::fromStdString(std::to_string(slider_maximum_sh_degree->value())));
+  });
+  connect(line_edit_maximum_sh_degree, &QLineEdit::editingFinished , [&]
+  {
+    slider_maximum_sh_degree->setValue(line_edit::get_text<int>(line_edit_maximum_sh_degree));
+  });
+  connect(slider_sampling_theta      , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_sampling_theta->setText(QString::fromStdString(std::to_string(slider_sampling_theta->value())));
+  });
+  connect(line_edit_sampling_theta   , &QLineEdit::editingFinished , [&]
+  {
+    slider_sampling_theta->setValue(line_edit::get_text<int>(line_edit_sampling_theta));
+  });
+  connect(slider_sampling_phi        , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_sampling_phi->setText(QString::fromStdString(std::to_string(slider_sampling_phi->value())));
+  });
+  connect(line_edit_sampling_phi     , &QLineEdit::editingFinished , [&]
+  {
+    slider_sampling_phi->setValue(line_edit::get_text<int>(line_edit_sampling_phi));
+  });
+  
+  connect(checkbox_hierarchical      , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Hierarchical construction is {}.", state ? "enabled" : "disabled");
+    label_layer_checkboxes ->setEnabled(state);
+    layout_layer_checkboxes->setEnabled(state);
+  });
+  connect(checkbox_depth_0           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 0 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_1           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 1 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_2           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 2 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_3           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 3 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_4           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 4 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_5           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 5 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_6           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 6 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_7           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 7 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_8           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 8 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+  connect(checkbox_depth_9           , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Grid layer 9 is {}.", state ? "enabled" : "disabled");
+    set_visible_layers();
+  });
+
+  connect(checkbox_clustering_enabled, &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info("Clustering is {}.", state ? "enabled" : "disabled");
+    label_threshold    ->setEnabled(state);
+    slider_threshold   ->setEnabled(state);
+    line_edit_threshold->setEnabled(state);
+  });
+  connect(slider_threshold           , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_threshold->setText(QString::fromStdString((boost::format("%.2f") % (threshold_multiplier_ * slider_threshold->value())).str()));
+  });
+  connect(line_edit_threshold        , &QLineEdit::editingFinished , [&]
+  {
+    slider_threshold->setValue(line_edit::get_text<double>(line_edit_threshold) / threshold_multiplier_);
+  });
+
+  connect(button_calculate           , &QAbstractButton::clicked   , [&]
+  {
+    calculate();
+  });
+  connect(button_extract_peaks       , &QAbstractButton::clicked   , [&]
+  {
+    extract_peaks();
+  });
+}
+
+void odf_plugin::start  ()
+{
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+
+  odf_field_ = owner_->viewer->add_renderable<odf_field>();
+  set_visible_layers();
+
+  connect(owner_->get_plugin<color_plugin>(), &color_plugin::on_change, [&] (int mode, float k, bool inverted)
+  {
+    odf_field_->set_color_mapping(mode, k, inverted);
+  });
+
+  logger_->info(std::string("Resetting GPU."));
+  cudaDeviceReset();
+
+  logger_->info(std::string("Initializing cusolver and cublas."));
+  cusolverDnCreate(&cusolver_);
+  cublasCreate    (&cublas_  );
+}
+void odf_plugin::destroy()
+{
+  logger_->info(std::string("Destroying cusolver and cublas."));
+  cusolverDnDestroy(cusolver_);
+  cublasDestroy    (cublas_  );
+}
+
+void odf_plugin::calculate         ()
+{
+  owner_->set_is_loading(true);
+
+  logger_->info(std::string("Updating viewer..."));
+  
+  auto max_degree              = 
+    line_edit::get_text<unsigned>(line_edit_maximum_sh_degree);
+  uint3 block_dimensions       = {
+    line_edit::get_text<unsigned>(line_edit_vector_block_x   ), 
+    line_edit::get_text<unsigned>(line_edit_vector_block_y   ), 
+    line_edit::get_text<unsigned>(line_edit_vector_block_z   )};
+  uint2 histogram_dimensions   = {                           
+    line_edit::get_text<unsigned>(line_edit_histogram_theta  ),
+    line_edit::get_text<unsigned>(line_edit_histogram_phi    )};
+  uint2 sampling_dimensions    = { 
+    line_edit::get_text<unsigned>(line_edit_sampling_theta   ),
+    line_edit::get_text<unsigned>(line_edit_sampling_phi     )};
+
+  future_ = std::async(std::launch::async, [&]
+  {
+    try
+    {
+      auto vectors = owner_->get_plugin<data_plugin>()->generate_vectors(false);
+
+      uint3 coefficient_dimensions = {
+        unsigned(vectors.shape()[0]) / block_dimensions.x,
+        unsigned(vectors.shape()[1]) / block_dimensions.y,
+        unsigned(vectors.shape()[2]) / block_dimensions.z };
+      auto true_size = block_dimensions * coefficient_dimensions;
+
+      vectors      .resize(boost::extents
+        [true_size.x]
+        [true_size.y]
+        [true_size.z]);
+      coefficients_.resize(boost::extents
+        [coefficient_dimensions.x]
+        [coefficient_dimensions.y]
+        [coefficient_dimensions.z]
+        [(max_degree + 1) * (max_degree + 1)]);
+
+      calculate_odfs(
+        cublas_               ,
+        cusolver_             ,
+        coefficient_dimensions,
+        block_dimensions      ,
+        histogram_dimensions  ,
+        max_degree            ,
+        vectors      .data()  ,
+        coefficients_.data()  ,
+        checkbox_even_only->isChecked(),
+        [&] (const std::string& message) { logger_->info(message); });
+    }
+    catch (std::exception& exception)
+    {
+      logger_->error(std::string(exception.what()));
+    }
+  });
+  while (future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
+    QApplication::processEvents();
+
+  odf_field_->set_data(
+    make_uint3(coefficients_.shape()[0], coefficients_.shape()[1], coefficients_.shape()[2]),
+    maximum_degree(coefficients_.shape()[3]),
+    coefficients_.data(),
+    sampling_dimensions,
+    block_dimensions,
+    1.0F,
+    checkbox_hierarchical      ->isChecked(),
+    checkbox_clustering_enabled->isChecked(),
+    threshold_multiplier_ * float(slider_threshold->value()),
+    [&] (const std::string& message) { logger_->info(message); });
+
+  logger_->info(std::string("Update successful."));
+ 
+  owner_->set_is_loading(false);
+}
+void odf_plugin::extract_peaks     ()
+{
+  owner_->set_is_loading(true);
+
+  logger_->info(std::string("Extracting peaks..."));
+    
+  auto dimensions = make_uint3(
+    coefficients_.shape()[0],
+    coefficients_.shape()[1],
+    coefficients_.shape()[2]);
+
+  uint2 sampling_dimensions    = { 
+    line_edit::get_text<unsigned>(line_edit_sampling_theta   ),
+    line_edit::get_text<unsigned>(line_edit_sampling_phi     )};
+  
+  future_ = std::async(std::launch::async, [&]
+  {
+    try
+    {
+      maxima_.resize(boost::extents
+        [coefficients_.shape()[0]]
+        [coefficients_.shape()[1]]
+        [coefficients_.shape()[2]]
+        [maxima_count_]);
+
+      pli::extract_peaks(
+        dimensions,
+        maximum_degree(coefficients_.shape()[3]),
+        coefficients_.data(),
+        sampling_dimensions ,
+        maxima_count_       ,
+        maxima_.data()      ,
+        [&] (const std::string& message) { logger_->info(message); });
+    }
+    catch (std::exception& exception)
+    {
+      logger_->error(std::string(exception.what()));
+    }
+  });
+  while (future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
+    QApplication::processEvents();
+
+  logger_->info(std::string("Extraction successful."));
+  
+  owner_->set_is_loading(false);
+}
+void odf_plugin::set_visible_layers() const
+{
+  odf_field_->set_visible_layers({
+    checkbox_depth_0->isChecked(),
+    checkbox_depth_1->isChecked(),
+    checkbox_depth_2->isChecked(),
+    checkbox_depth_3->isChecked(),
+    checkbox_depth_4->isChecked(),
+    checkbox_depth_5->isChecked(),
+    checkbox_depth_6->isChecked(),
+    checkbox_depth_7->isChecked(),
+    checkbox_depth_8->isChecked(),
+    checkbox_depth_9->isChecked()
+  });
+  owner_->viewer->update();
+}
+}
diff --git a/source/ui/plugins/plugin.cpp b/source/ui/plugins/plugin.cpp
deleted file mode 100644
index 68075d500f876fd127e78506e4ca611c19e81ab6..0000000000000000000000000000000000000000
--- a/source/ui/plugins/plugin.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include /* implements */ <ui/plugins/plugin.hpp>
-
-namespace pli
-{
-plugin::plugin(QWidget* parent) : QWidget(parent)
-{
-
-}
-
-void plugin::set_owner(pli::window* owner)
-{
-  owner_ = owner;
-}
-
-void plugin::awake  ()
-{
-
-}
-void plugin::start  ()
-{
-
-}
-void plugin::destroy()
-{
-
-}
-}
diff --git a/source/ui/plugins/polar_plot_plugin.cpp b/source/ui/plugins/polar_plot_plugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5e065294ff8db72b0978a7e245d807bbed770e08
--- /dev/null
+++ b/source/ui/plugins/polar_plot_plugin.cpp
@@ -0,0 +1,85 @@
+#include <pli_vis/ui/plugins/polar_plot_plugin.hpp>
+
+#include <pli_vis/cuda/polar_plot.h>
+#include <pli_vis/cuda/utility/vector_ops.h>
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+
+namespace pli
+{
+polar_plot_plugin::polar_plot_plugin(QWidget* parent)
+{
+  line_edit_superpixel_size   ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_angular_partitions->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_superpixel_size   ->setText     (QString::fromStdString(std::to_string(slider_superpixel_size   ->value())));
+  line_edit_angular_partitions->setText     (QString::fromStdString(std::to_string(slider_angular_partitions->value())));
+
+  connect(checkbox_enabled            , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info(std::string(state ? "Enabled." : "Disabled."));
+    field_->set_active(state);
+  });
+  connect(slider_superpixel_size      , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_superpixel_size->setText(QString::fromStdString(std::to_string(slider_superpixel_size->value())));
+  });
+  connect(line_edit_superpixel_size   , &QLineEdit::editingFinished , [&]
+  {
+    slider_superpixel_size->setValue(line_edit::get_text<int>(line_edit_superpixel_size));
+  });
+  connect(slider_angular_partitions   , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_angular_partitions->setText(QString::fromStdString(std::to_string(slider_angular_partitions->value())));
+  });
+  connect(line_edit_angular_partitions, &QLineEdit::editingFinished , [&]
+  {
+    slider_angular_partitions->setValue(line_edit::get_text<int>(line_edit_angular_partitions));
+  });
+  connect(button_calculate            , &QAbstractButton::clicked   , [&]
+  {
+    owner_ ->set_is_loading(true);
+    logger_->info(std::string("Updating viewer..."));
+
+    auto vectors            = owner_->get_plugin<data_plugin>()->generate_vectors(false);
+    auto superpixel_size    = line_edit::get_text<unsigned>(line_edit_superpixel_size   );
+    auto angular_partitions = line_edit::get_text<unsigned>(line_edit_angular_partitions);
+    auto symmetric          = checkbox_symmetric->isChecked();
+    
+    std::array<std::vector<float3>, 2> polar_plots;
+    future_ = std::async(std::launch::async, [&]
+    {
+      try
+      {
+        auto vectors_dimensions = make_uint2(vectors.shape()[0] / superpixel_size, vectors.shape()[1] / superpixel_size) * superpixel_size;
+        vectors.resize(boost::extents[vectors_dimensions.x][vectors_dimensions.y][1]);
+        polar_plots = calculate(
+          std::vector<float3>(vectors.data(), vectors.data() + vectors.num_elements()), 
+          vectors_dimensions,
+          superpixel_size   , 
+          angular_partitions, 
+          symmetric         );
+      }
+      catch (std::exception& exception) { logger_->error(std::string(exception.what())); }
+    });
+    while (future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
+      QApplication::processEvents();
+    
+    field_->set_data(polar_plots[0], polar_plots[1]);
+
+    logger_->info(std::string("Update successful."));
+    owner_ ->set_is_loading(false);
+  });
+}
+
+void polar_plot_plugin::start()
+{
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+
+  field_ = owner_->viewer->add_renderable<polar_plot_field>();
+  connect(owner_->get_plugin<color_plugin>(), &color_plugin::on_change, [&] (int mode, float k, bool inverted)
+  {
+    field_->set_color_mapping(mode, k, inverted);
+  });
+}
+}
diff --git a/source/ui/plugins/scalar_plugin.cpp b/source/ui/plugins/scalar_plugin.cpp
index a41fc633fd2d199d4224b85e595b55e48ec4e8f5..cc7ba6c83125349cb285fb69f7851b343f3cc67e 100644
--- a/source/ui/plugins/scalar_plugin.cpp
+++ b/source/ui/plugins/scalar_plugin.cpp
@@ -1,114 +1,95 @@
-#include /* implements */ <ui/plugins/scalar_plugin.hpp>
+#include <pli_vis/ui/plugins/scalar_plugin.hpp>
 
-#include <boost/optional.hpp>
+#include <vector_functions.hpp>
 
-#include <ui/plugins/data_plugin.hpp>
-#include <ui/plugins/selector_plugin.hpp>
-#include <ui/window.hpp>
-#include <utility/qt_text_browser_sink.hpp>
-#include <visualization/scalar_field.hpp>
+#include <pli_vis/ui/plugins/data_plugin.hpp>
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+#include <pli_vis/visualization/algorithms/scalar_field.hpp>
 
 namespace pli
 {
 scalar_plugin::scalar_plugin(QWidget* parent) : plugin(parent)
 {
-  setupUi(this);
-  
-  connect(checkbox_enabled      , &QCheckBox::stateChanged, [&](bool state)
+  connect(checkbox_enabled      , &QCheckBox::stateChanged   , [&](bool state)
   {
     logger_->info(std::string(state ? "Enabled." : "Disabled."));
     scalar_field_->set_active(state);
   });
-  connect(checkbox_transmittance, &QRadioButton::clicked  , [&]()
+  connect(checkbox_transmittance, &QRadioButton::clicked     , [&]
   {
     logger_->info(std::string("Transmittance maps are selected."));
     upload();
   });
-  connect(checkbox_retardation  , &QRadioButton::clicked  , [&]()
+  connect(checkbox_retardation  , &QRadioButton::clicked     , [&]
   {
     logger_->info(std::string("Retardation maps are selected."));
     upload();
   });
-  connect(checkbox_direction    , &QRadioButton::clicked  , [&]()
+  connect(checkbox_direction    , &QRadioButton::clicked     , [&]
   {
     logger_->info(std::string("Direction maps are selected."));
     upload();
   });
-  connect(checkbox_inclination  , &QRadioButton::clicked  , [&]()
+  connect(checkbox_inclination  , &QRadioButton::clicked     , [&]
   {
     logger_->info(std::string("Inclination maps are selected."));
     upload();
   });
-}
-
-void scalar_plugin::start ()
-{
-  set_sink(std::make_shared<qt_text_browser_sink>(owner_->console));
-
-  connect(owner_->get_plugin<data_plugin>    (), &data_plugin    ::on_change, [&]
+  connect(slider_slice          , &QSlider::valueChanged     , [&]
   {
+    line_edit_slice->setText(QString::fromStdString(boost::lexical_cast<std::string>(slider_slice->value())));
     upload();
   });
-  connect(owner_->get_plugin<selector_plugin>(), &selector_plugin::on_change, [&]
+  connect(line_edit_slice       , &QLineEdit::editingFinished, [&]
   {
+    slider_slice->setValue(line_edit::get_text<int>(line_edit_slice));
     upload();
   });
-  
+}
+
+void scalar_plugin::start ()
+{
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+
   scalar_field_ = owner_->viewer->add_renderable<scalar_field>();
 
-  logger_->info(std::string("Start successful."));
+  connect(owner_->get_plugin<data_plugin>(), &data_plugin::on_load, [&]
+  {
+    auto data_plugin = owner_->get_plugin<pli::data_plugin>();
+    auto offset = data_plugin->selection_offset();
+    auto size   = data_plugin->selection_size  ();
+    slider_slice->setMinimum(offset[2]);
+    slider_slice->setMaximum(offset[2] + size[2] - 1);
+    slider_slice->setValue  (offset[2] + size[2] - 1);
+
+    upload();
+  });
 }
 void scalar_plugin::upload()
 {
   logger_->info(std::string("Updating viewer..."));
 
-  auto io       = owner_->get_plugin<pli::data_plugin>    ()->io();
-  auto selector = owner_->get_plugin<pli::selector_plugin>();
-  auto offset   = selector->selection_offset();
-  auto size     = selector->selection_size  ();
-  auto stride   = selector->selection_stride();
-
-  if (io == nullptr || size[0] == 0 || size[1] == 0 || size[2] == 0)
-  {
-    logger_->info(std::string("Update failed: No data."));
-    return;
-  }
+  auto data_plugin = owner_->get_plugin<pli::data_plugin>();
 
-  size = {size[0] / stride[0], size[1] / stride[1], 1};
+  boost::multi_array<float, 3> const* data = nullptr;
+  if (checkbox_transmittance->isChecked())
+    data = &data_plugin->transmittance();
+  if (checkbox_retardation  ->isChecked())
+    data = &data_plugin->retardation();
+  if (checkbox_direction    ->isChecked())
+    data = &data_plugin->direction();
+  if (checkbox_inclination  ->isChecked())
+    data = &data_plugin->inclination();
 
-  owner_->viewer->set_wait_spinner_enabled(true);
-  selector->setEnabled(false);
-
-  // Load data from hard drive (on another thread).
-  std::array<float, 3>                          spacing;
-  boost::optional<boost::multi_array<float, 3>> data   ;
-  future_ = std::async(std::launch::async, [&]
+  if(data && !data->empty())
   {
-    try
-    {
-      spacing = io->load_vector_spacing();
-      if (checkbox_transmittance->isChecked()) data.reset(io->load_transmittance_dataset    (offset, size, stride));
-      if (checkbox_retardation  ->isChecked()) data.reset(io->load_retardation_dataset      (offset, size, stride));
-      if (checkbox_direction    ->isChecked()) data.reset(io->load_fiber_direction_dataset  (offset, size, stride));
-      if (checkbox_inclination  ->isChecked()) data.reset(io->load_fiber_inclination_dataset(offset, size, stride));
-    }
-    catch (std::exception& exception)
-    {
-      logger_->error(std::string(exception.what()));
-    }
-  });
-  while(future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
-    QApplication::processEvents();
-
-  // Upload data to GPU.
-  uint3  cuda_size    {unsigned(size[0]), unsigned(size[1]), unsigned(size[2])};
-  float3 cuda_spacing {spacing[0], spacing[1], spacing[2]};
-  if (data.is_initialized() && data.get().num_elements() > 0)
-    scalar_field_->set_data(cuda_size, data.get().data(), cuda_spacing);
-
-  selector->setEnabled(true);
-  owner_->viewer->set_wait_spinner_enabled(false);
-  owner_->viewer->update();
+    auto index   = slider_slice->value() - slider_slice->minimum();
+    auto subdata = boost::multi_array<float, 2>((*data)[boost::indices[boost::multi_array_types::index_range()][boost::multi_array_types::index_range()][index]]);
+    scalar_field_->set_data       (make_uint3(subdata.shape()[0], subdata.shape()[1], 1), subdata.data());
+    scalar_field_->set_translation(glm::vec3(0, 0, index));
+  }
 
   logger_->info(std::string("Update successful."));
 }
diff --git a/source/ui/plugins/selector_plugin.cpp b/source/ui/plugins/selector_plugin.cpp
deleted file mode 100644
index 84dfffef273048b5cd215a93740382cd9d4ff16b..0000000000000000000000000000000000000000
--- a/source/ui/plugins/selector_plugin.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-#include /* implements */ <ui/plugins/selector_plugin.hpp>
-
-#include <algorithm>
-
-#include <ui/window.hpp>
-#include <utility/line_edit_utility.hpp>
-#include <utility/qt_text_browser_sink.hpp>
-
-namespace pli
-{
-selector_plugin::selector_plugin(QWidget* parent) : plugin(parent)
-{
-  setupUi(this);
-
-  connect(slider_x          , &QxtSpanSlider::lowerValueChanged, [&](int value)
-  {
-    line_edit_offset_x->setText(QString::fromStdString(std::to_string(value)));
-    line_edit_size_x  ->setText(QString::fromStdString(std::to_string(slider_x->upperValue() - value)));
-  });
-  connect(slider_x          , &QxtSpanSlider::upperValueChanged, [&](int value)
-  {
-    line_edit_size_x->setText(QString::fromStdString(std::to_string(value - slider_x->lowerValue())));
-  });
-  connect(slider_x          , &QxtSpanSlider::sliderReleased, [&]
-  {
-    image->set_selection_offset_percentage({static_cast<float>(slider_x->lowerValue())                          / slider_x->maximum(), image->selection_offset_percentage()[1]});
-    image->set_selection_size_percentage  ({static_cast<float>(slider_x->upperValue() - slider_x->lowerValue()) / slider_x->maximum(), image->selection_size_percentage  ()[1]});
-  });
-  connect(slider_y          , &QxtSpanSlider::lowerValueChanged, [&](int value)
-  {
-    line_edit_offset_y->setText(QString::fromStdString(std::to_string(value)));
-    line_edit_size_y  ->setText(QString::fromStdString(std::to_string(slider_y->upperValue() - value)));
-  });
-  connect(slider_y          , &QxtSpanSlider::upperValueChanged, [&](int value)
-  {
-    line_edit_size_y  ->setText(QString::fromStdString(std::to_string(value - slider_y->lowerValue())));
-  });
-  connect(slider_y          , &QxtSpanSlider::sliderReleased, [&]
-  {
-    image->set_selection_offset_percentage({image->selection_offset_percentage()[0], static_cast<float>(slider_y->lowerValue())                          / slider_y->maximum()});
-    image->set_selection_size_percentage  ({image->selection_size_percentage  ()[0], static_cast<float>(slider_y->upperValue() - slider_y->lowerValue()) / slider_y->maximum()});
-  });
-  connect(slider_z          , &QxtSpanSlider::lowerValueChanged, [&](int value)
-  {
-    line_edit_offset_z->setText(QString::fromStdString(std::to_string(value)));
-    line_edit_size_z  ->setText(QString::fromStdString(std::to_string(slider_z->upperValue() - value)));
-  });
-  connect(slider_z          , &QxtSpanSlider::upperValueChanged, [&](int value)
-  {
-    line_edit_size_z  ->setText(QString::fromStdString(std::to_string(value - slider_z->lowerValue())));
-  });
-  connect(line_edit_offset_x, &QLineEdit::editingFinished      , [&]
-  {
-    auto value = std::max(std::min(line_edit_utility::get_text<int>(line_edit_offset_x), int(slider_x->maximum())), int(slider_x->minimum()));
-    if (slider_x->upperValue() < value)
-      slider_x->setUpperValue(value);
-    slider_x->setLowerValue(value);
-    image->set_selection_offset_percentage({static_cast<float>(value) / slider_x->maximum(), image->selection_offset_percentage()[1]});
-    image->set_selection_size_percentage  ({static_cast<float>(slider_x->upperValue() - slider_x->lowerValue()) / slider_x->maximum(), image->selection_size_percentage  ()[1]});
-    line_edit_size_x->setText(QString::fromStdString(std::to_string(slider_x->upperValue() - value)));
-  });
-  connect(line_edit_size_x  , &QLineEdit::editingFinished      , [&]
-  {
-    auto value = std::max(std::min(line_edit_utility::get_text<int>(line_edit_size_x), int(slider_x->maximum())), int(slider_x->minimum()));
-    slider_x->setUpperValue(slider_x->lowerValue() + value);
-    image->set_selection_size_percentage({static_cast<float>(value) / slider_x->maximum(), image->selection_size_percentage()[1]});
-  });
-  connect(line_edit_offset_y, &QLineEdit::editingFinished      , [&]
-  {
-    int value = std::max(std::min(line_edit_utility::get_text<int>(line_edit_offset_y), int(slider_y->maximum())), int(slider_y->minimum()));
-    if (slider_y->upperValue() < value)
-      slider_y->setUpperValue(value);
-    slider_y->setLowerValue(value);
-    image->set_selection_offset_percentage({image->selection_offset_percentage()[0], static_cast<float>(value) / slider_y->maximum()});
-    image->set_selection_size_percentage  ({image->selection_size_percentage  ()[0], static_cast<float>(slider_y->upperValue() - slider_y->lowerValue()) / slider_y->maximum()});
-    line_edit_size_y->setText(QString::fromStdString(std::to_string(slider_y->upperValue() - value)));
-  });
-  connect(line_edit_size_y  , &QLineEdit::editingFinished      , [&]
-  {
-    auto value = std::max(std::min(line_edit_utility::get_text<int>(line_edit_size_y), int(slider_y->maximum())), int(slider_y->minimum()));
-    slider_y->setUpperValue(slider_y->lowerValue() + value);
-    image->set_selection_size_percentage({image->selection_size_percentage()[0], static_cast<float>(value) / slider_y->maximum()});
-  });
-  connect(line_edit_offset_z, &QLineEdit::editingFinished      , [&]
-  {
-    auto value = std::max(std::min(line_edit_utility::get_text<int>(line_edit_offset_z), int(slider_z->maximum())), int(slider_z->minimum()));
-    if (slider_z->upperValue() < value)
-      slider_z->setUpperValue(value + 1);
-    slider_z->setLowerValue(value);
-    line_edit_size_z->setText(QString::fromStdString(std::to_string(slider_z->upperValue() - value)));
-  });
-  connect(line_edit_size_z  , &QLineEdit::editingFinished      , [&]
-  {
-    auto value = std::min(line_edit_utility::get_text<int>(line_edit_size_z), int(slider_z->maximum() - slider_z->minimum()));
-    slider_z->setUpperValue(slider_z->lowerValue() + value);
-  });
-  connect(button_update     , &QAbstractButton::clicked        , [&]
-  {
-    on_change(selection_offset(), selection_size(), selection_stride());
-  });
-
-  connect(image, &overview_image::on_selection_change, [&](const std::array<float, 2> offset_perc, const std::array<float, 2> size_perc)
-  {
-    std::array<int, 2> offset { int(offset_perc[0] * slider_x->maximum()), int(offset_perc[1] * slider_y->maximum()) };
-    std::array<int, 2> size   { int(size_perc  [0] * slider_x->maximum()), int(size_perc  [1] * slider_y->maximum()) };
-
-    line_edit_offset_x->setText      (QString::fromStdString(std::to_string(offset[0])));
-    line_edit_size_x  ->setText      (QString::fromStdString(std::to_string(size  [0])));
-    slider_x          ->setLowerValue(offset[0]);
-    slider_x          ->setUpperValue(offset[0] + size[0]);
-    line_edit_offset_y->setText      (QString::fromStdString(std::to_string(offset[1])));
-    line_edit_size_y  ->setText      (QString::fromStdString(std::to_string(size  [1])));
-    slider_y          ->setLowerValue(offset[1]);
-    slider_y          ->setUpperValue(offset[1] + size[1]);
-  });
-}
-
-std::array<std::size_t, 3> selector_plugin::selection_offset() const
-{
-  return
-  {
-    line_edit_utility::get_text<std::size_t>(line_edit_offset_x),
-    line_edit_utility::get_text<std::size_t>(line_edit_offset_y),
-    line_edit_utility::get_text<std::size_t>(line_edit_offset_z)
-  };
-}
-std::array<std::size_t, 3> selector_plugin::selection_size  () const
-{
-  return
-  {
-    line_edit_utility::get_text<std::size_t>(line_edit_size_x),
-    line_edit_utility::get_text<std::size_t>(line_edit_size_y),
-    line_edit_utility::get_text<std::size_t>(line_edit_size_z)
-  };
-}
-std::array<std::size_t, 3> selector_plugin::selection_stride() const
-{
-  return
-  {
-    line_edit_utility::get_text<std::size_t>(line_edit_stride_x),
-    line_edit_utility::get_text<std::size_t>(line_edit_stride_y),
-    line_edit_utility::get_text<std::size_t>(line_edit_stride_z)
-  };
-}
-
-void selector_plugin::start ()
-{
-  set_sink(std::make_shared<qt_text_browser_sink>(owner_->console));
-
-  connect(owner_->get_plugin<data_plugin>(), &data_plugin::on_change, [&]
-  {
-    logger_->info(std::string("Updating selector..."));
-
-    auto io = owner_->get_plugin<data_plugin>()->io();
-    if (io)
-    {
-      // Adjust slider boundaries.
-      auto bounds = io->load_retardation_dataset_bounds();
-      slider_x->setMinimum(int(bounds.first[0])); slider_x->setMaximum(int(bounds.second[0]));
-      slider_y->setMinimum(int(bounds.first[1])); slider_y->setMaximum(int(bounds.second[1]));
-      slider_z->setMinimum(int(bounds.first[2])); slider_z->setMaximum(int(bounds.second[2]));
-      slider_z->setSpan   (int(bounds.first[2]) , int(bounds.first[2] + 1));
-
-      // Generate preview image.
-      std::array<std::size_t, 3> size   = {1, 1, 1};
-      std::array<std::size_t, 3> stride = {1, 1, 1};
-      size  [0] = std::min(int(bounds.second[0]), 2048);
-      stride[0] = bounds.second[0] / size  [0];
-      size  [1] = bounds.second[1] / stride[0];
-      stride[1] = stride[0];
-      auto data = io->load_retardation_dataset(bounds.first, size, stride);
-      boost::multi_array<unsigned char, 3> converted(boost::extents[size[0]][size[1]][size[2]], boost::fortran_storage_order());
-      for(auto i = 0; i < size[0]; i++)
-        for(auto j = 0; j < size[1]; j++)
-          for (auto k = 0; k < size[2]; k++)
-            converted[i][j][k] = data[i][j][k] * 255.0;
-      image->setPixmap(QPixmap::fromImage(QImage(converted.data(), int(size[0]), int(size[1]), QImage::Format::Format_Grayscale8)));
-
-      // Adjust widget size.
-      image    ->setSizeIncrement(int(size[0]), int(size[1]));
-      letterbox->setWidget       (image);
-      image    ->update          ();
-    }
-    else
-    {
-      slider_x->setMinimum(0); slider_x->setMaximum(0);
-      slider_y->setMinimum(0); slider_y->setMaximum(0);
-      slider_z->setMinimum(0); slider_z->setMaximum(0);
-
-      image->setPixmap(QPixmap());
-    }
-
-    update();
-    image->update();
-
-    logger_->info(std::string("Update successful."));
-  });
-
-  logger_->info(std::string("Start successful."));
-}
-}
diff --git a/source/ui/plugins/tractography_plugin.cpp b/source/ui/plugins/tractography_plugin.cpp
deleted file mode 100644
index de5c0e931ada5a81ff57c5b3b8c222cf81bae128..0000000000000000000000000000000000000000
--- a/source/ui/plugins/tractography_plugin.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include /* implements */ <ui/plugins/tractography_plugin.hpp>
-
-#define _USE_MATH_DEFINES
-#include <math.h>
-
-#include <sh/convert.h>
-#include <ui/plugins/data_plugin.hpp>
-#include <ui/plugins/selector_plugin.hpp>
-#include <ui/window.hpp>
-#include <utility/line_edit_utility.hpp>
-#include <utility/qt_text_browser_sink.hpp>
-
-namespace pli
-{
-tractography_plugin::tractography_plugin(QWidget* parent) : plugin(parent)
-{
-  setupUi(this);
-  
-  connect(button_trace_selection, &QPushButton::clicked, [&]
-  {
-    trace();
-  });
-}
-
-void tractography_plugin::start()
-{
-  set_sink(std::make_shared<qt_text_browser_sink>(owner_->console));
-
-  logger_->info(std::string("Start successful."));
-}
-void tractography_plugin::trace()
-{
-  logger_->info(std::string("Tracing..."));
-
-  try
-  {
-    auto data_plugin     = owner_->get_plugin<pli::data_plugin>();
-    auto io              = data_plugin->io();
-    if(io == nullptr)
-    {
-      logger_->info(std::string("Trace failed: No data."));
-      return;
-    }
-
-    auto selector_plugin = owner_->get_plugin<pli::selector_plugin>();
-    auto offset          = selector_plugin->selection_offset();
-    auto size            = selector_plugin->selection_size  ();
-      
-    auto fiber_direction_map   = io->load_fiber_direction_dataset  (offset, size);
-    auto fiber_inclination_map = io->load_fiber_inclination_dataset(offset, size);
-    auto spacing               = io->load_vector_spacing();
-    auto shape                 = fiber_direction_map.shape();
-    
-    // TODO!
-
-    owner_->viewer->update();
-
-    logger_->info(std::string("Trace successful."));
-  }
-  catch (std::exception& exception)
-  {
-    logger_->error(std::string(exception.what()));
-  }
-}
-}
diff --git a/source/ui/plugins/volume_rendering_plugin.cpp b/source/ui/plugins/volume_rendering_plugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a43fe656b4e9f181917d8f42731f97d448d3ac9
--- /dev/null
+++ b/source/ui/plugins/volume_rendering_plugin.cpp
@@ -0,0 +1,82 @@
+#include <pli_vis/ui/plugins/volume_rendering_plugin.hpp>
+
+#include <boost/format.hpp>
+#include <vector_functions.hpp>
+
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+#include <pli_vis/visualization/algorithms/volume_renderer.hpp>
+
+namespace pli
+{
+volume_rendering_plugin::volume_rendering_plugin(QWidget* parent) : plugin(parent)
+{
+  line_edit_step_size->setText(QString::fromStdString((boost::format("%.4f") % (double(slider_step_size->value()) / slider_step_size->maximum())).str()));
+
+  connect(checkbox_enabled        , &QCheckBox::stateChanged            , [&](bool state)
+  {
+    logger_->info(std::string(state ? "Enabled." : "Disabled."));
+    volume_renderer_->set_active(state);
+  });
+  connect(slider_step_size        , &QSlider::valueChanged              , [&]
+  {
+    line_edit_step_size->setText(QString::fromStdString((boost::format("%.4f") % (float(slider_step_size->value()) / slider_step_size->maximum())).str()));
+  });
+  connect(slider_step_size        , &QSlider::sliderReleased            , [&]
+  {
+    volume_renderer_->set_step_size(line_edit::get_text<double>(line_edit_step_size));
+  });
+  connect(line_edit_step_size     , &QLineEdit::editingFinished         , [&]
+  {
+    auto step_size = line_edit::get_text<double>(line_edit_step_size);
+    slider_step_size->setValue(step_size * slider_step_size->maximum());
+    volume_renderer_->set_step_size(step_size);
+  });
+  connect(transfer_function_editor, &transfer_function_editor::on_change, [&]
+  {
+    volume_renderer_->set_transfer_function(transfer_function_editor->get_function());
+  });
+}
+
+void volume_rendering_plugin::start ()
+{
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+
+  connect(owner_->get_plugin<data_plugin>(), &data_plugin::on_load, [&]
+  {
+    upload();
+  });
+  
+  auto step_size = double(slider_step_size->value()) / slider_step_size->maximum();
+  volume_renderer_ = owner_->viewer->add_renderable<volume_renderer>();
+  volume_renderer_->set_active           (checkbox_enabled->isChecked());
+  volume_renderer_->set_step_size        (step_size);
+  volume_renderer_->set_transfer_function(transfer_function_editor->get_function());
+}
+void volume_rendering_plugin::upload()
+{
+  logger_->info(std::string("Updating viewer..."));
+
+  auto& retardation = owner_->get_plugin<data_plugin>()->retardation();
+
+  std::vector<std::size_t> histogram(255);
+  std::for_each(retardation.origin(), retardation.origin() + retardation.num_elements(),
+  [&histogram] (const float& value)
+  {
+    histogram[value]++;
+  });
+
+  auto ratio = 255.0F / *std::max_element(histogram.begin(), histogram.end());
+  std::transform(histogram.begin(), histogram.end(), histogram.begin(),
+  [&ratio](const std::size_t& value)
+  {
+    return float(value) * ratio;
+  });
+
+  volume_renderer_        ->set_data             (make_uint3(retardation.shape()[0], retardation.shape()[1], retardation.shape()[2]), retardation.data());
+  transfer_function_editor->set_histogram_entries(histogram);
+
+  logger_->info(std::string("Update successful."));
+}
+}
diff --git a/source/ui/plugins/zernike_plugin.cpp b/source/ui/plugins/zernike_plugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..24bb53cfb6d79b89f400853fa83cb0effdf2c578
--- /dev/null
+++ b/source/ui/plugins/zernike_plugin.cpp
@@ -0,0 +1,157 @@
+#include <pli_vis/ui/plugins/zernike_plugin.hpp>
+
+#include <pli_vis/cuda/utility/vector_ops.h>
+#include <pli_vis/cuda/zernike/launch.h>
+#include <pli_vis/cuda/zernike/zernike.h>
+#include <pli_vis/ui/utility/line_edit.hpp>
+#include <pli_vis/ui/utility/text_browser_sink.hpp>
+#include <pli_vis/ui/application.hpp>
+#include <pli_vis/visualization/algorithms/zernike_field.hpp>
+
+namespace pli
+{
+zernike_plugin::zernike_plugin(QWidget* parent)
+{
+  line_edit_superpixel_x    ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_superpixel_y    ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_partitions_theta->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_partitions_rho  ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  line_edit_maximum_degree  ->setValidator(new QIntValidator(0, std::numeric_limits<int>::max(), this));
+  
+  line_edit_superpixel_x    ->setText(QString::fromStdString(std::to_string(slider_superpixel_x    ->value())));
+  line_edit_superpixel_y    ->setText(QString::fromStdString(std::to_string(slider_superpixel_y    ->value())));
+  line_edit_partitions_theta->setText(QString::fromStdString(std::to_string(slider_partitions_theta->value())));
+  line_edit_partitions_rho  ->setText(QString::fromStdString(std::to_string(slider_partitions_rho  ->value())));
+  line_edit_maximum_degree  ->setText(QString::fromStdString(std::to_string(slider_maximum_degree  ->value())));
+
+  connect(checkbox_enabled          , &QCheckBox::stateChanged    , [&](int state)
+  {
+    logger_->info(std::string(state ? "Enabled." : "Disabled."));
+    zernike_field_->set_active(state);
+  });
+                                    
+  connect(slider_superpixel_x       , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_superpixel_x    ->setText(QString::fromStdString(std::to_string(slider_superpixel_x    ->value())));
+  });
+  connect(slider_superpixel_y       , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_superpixel_y    ->setText(QString::fromStdString(std::to_string(slider_superpixel_y    ->value())));
+  });
+  connect(slider_partitions_theta   , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_partitions_theta->setText(QString::fromStdString(std::to_string(slider_partitions_theta->value())));
+  });
+  connect(slider_partitions_rho     , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_partitions_rho  ->setText(QString::fromStdString(std::to_string(slider_partitions_rho  ->value())));
+  });
+  connect(slider_maximum_degree     , &QxtSpanSlider::valueChanged, [&]
+  {
+    line_edit_maximum_degree  ->setText(QString::fromStdString(std::to_string(slider_maximum_degree  ->value())));
+  });
+
+  connect(line_edit_superpixel_x    , &QLineEdit::editingFinished , [&]
+  {
+    slider_superpixel_x    ->setValue(line_edit::get_text<int>(line_edit_superpixel_x));
+  });
+  connect(line_edit_superpixel_y    , &QLineEdit::editingFinished , [&]
+  {
+    slider_superpixel_y    ->setValue(line_edit::get_text<int>(line_edit_superpixel_y));
+  });
+  connect(line_edit_partitions_theta, &QLineEdit::editingFinished , [&]
+  {
+    slider_partitions_theta->setValue(line_edit::get_text<int>(line_edit_partitions_theta));
+  });
+  connect(line_edit_partitions_rho  , &QLineEdit::editingFinished , [&]
+  {
+    slider_partitions_rho  ->setValue(line_edit::get_text<int>(line_edit_partitions_rho));
+  });
+  connect(line_edit_maximum_degree  , &QLineEdit::editingFinished , [&]
+  {
+    slider_maximum_degree  ->setValue(line_edit::get_text<int>(line_edit_maximum_degree));
+  });
+
+  connect(button_calculate          , &QAbstractButton::clicked   , [&]
+  {
+    owner_ ->set_is_loading(true);
+    logger_->info(std::string("Updating viewer..."));
+
+    auto  parameters            = get_parameters();
+    auto  vectors               = owner_->get_plugin<data_plugin>()->generate_vectors(false);
+    uint2 superpixel_dimensions = 
+    {
+      unsigned(vectors.shape()[0]) / parameters.superpixel_size.x,
+      unsigned(vectors.shape()[1]) / parameters.superpixel_size.y,
+    };
+    std::vector<float> coefficients;
+
+    future_ = std::async(std::launch::async, [&]
+    {
+      try
+      {
+        auto  vector_dimensions     = parameters.superpixel_size * superpixel_dimensions;
+        vectors.resize(boost::extents[vector_dimensions.x][vector_dimensions.y][1]);
+
+        std::vector<float3> vectors_linear(vectors.num_elements());
+        std::copy_n(vectors.data(), vectors.num_elements(), vectors_linear.begin());
+        coefficients = zer::launch(
+          vectors_linear            ,
+          parameters.vectors_size   ,
+          parameters.superpixel_size, 
+          parameters.partitions     , 
+          parameters.maximum_degree ,
+          parameters.symmetric      ,
+          parameters.normalize      ,
+          parameters.even_only      ,
+          parameters.edge_only      );
+      }
+      catch (std::exception& exception)
+      {
+        logger_->error(std::string(exception.what()));
+      }
+    });
+    while (future_.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
+      QApplication::processEvents();
+
+    zernike_field_->set_data(superpixel_dimensions, parameters.superpixel_size, zer::expansion_size(parameters.maximum_degree), coefficients);
+
+    logger_->info(std::string("Update successful."));
+    owner_ ->set_is_loading(false);
+  });
+}
+
+void                       zernike_plugin::start         ()
+{
+  set_sink(std::make_shared<text_browser_sink>(owner_->console));
+
+  zernike_field_ = owner_->viewer->add_renderable<zernike_field>();
+  connect(owner_->get_plugin<color_plugin>(), &color_plugin::on_change, [&](int mode, float k, bool inverted)
+  {
+    zernike_field_->set_color_mapping(mode, k, inverted);
+  });
+}
+zernike_plugin::parameters zernike_plugin::get_parameters() const
+{
+  return 
+  {
+    {
+      unsigned(owner_->get_plugin<data_plugin>()->selection_size()[0]),
+      unsigned(owner_->get_plugin<data_plugin>()->selection_size()[1])
+    },
+    {
+      line_edit::get_text<unsigned>(line_edit_superpixel_x),
+      line_edit::get_text<unsigned>(line_edit_superpixel_y) 
+    },
+    {
+      line_edit::get_text<unsigned>(line_edit_partitions_theta),
+      line_edit::get_text<unsigned>(line_edit_partitions_rho  ) 
+    },
+    line_edit::get_text<unsigned>(line_edit_maximum_degree),
+    checkbox_symmetric->isChecked(),
+    checkbox_normalize->isChecked(),
+    checkbox_even_only->isChecked(),
+    checkbox_edge_only->isChecked()
+  };
+}
+}
diff --git a/source/ui/utility/plot_interactor.cpp b/source/ui/utility/plot_interactor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..68dd7f69e7c9e1a78a87e81dcc1c23cf6c2b11b3
--- /dev/null
+++ b/source/ui/utility/plot_interactor.cpp
@@ -0,0 +1,390 @@
+#include <pli_vis/ui/utility/plot_interactor.hpp>
+
+#include <qapplication.h>
+#include <qevent.h>
+#include <qwhatsthis.h>
+#include <qpainter.h>
+
+#include <qwt/qwt_plot.h>
+#include <qwt/qwt_symbol.h>
+#include <qwt/qwt_plot_canvas.h>
+#include <qwt/qwt_plot_curve.h>
+#include <qwt/qwt_plot_directpainter.h>
+
+namespace pli
+{
+plot_interactor::plot_interactor(QwtPlot* plot) : QObject(plot), selected_curve_(nullptr), selected_point_(-1)
+{
+  auto canvas = qobject_cast<QwtPlotCanvas*>(plot->canvas());
+  canvas->installEventFilter(this);
+
+  canvas->setFocusPolicy   (Qt::StrongFocus);
+  canvas->setCursor        (Qt::PointingHandCursor);
+  canvas->setFocusIndicator(QwtPlotCanvas::ItemFocusIndicator);
+  canvas->setFocus         ();
+
+  shift_curve_cursor(true);
+}
+
+bool plot_interactor::eventFilter(QObject* object, QEvent* event)
+{
+  if (plot() == nullptr || object != plot()->canvas())
+    return false;
+
+  switch (event->type())
+  {
+  case QEvent::FocusIn:
+  {
+    show_cursor(true);
+    break;
+  }
+  case QEvent::FocusOut:
+  {
+    show_cursor(false);
+    break;
+  }
+  case QEvent::Paint:
+  {
+    QApplication::postEvent(this, new QEvent(QEvent::User));
+    break;
+  }
+  case QEvent::MouseButtonPress:
+  {
+    const QMouseEvent* mouse_event = static_cast<QMouseEvent *>(event);
+
+    if (mouse_event->buttons() == Qt::LeftButton)
+      select_or_add(mouse_event->pos());
+    else if (mouse_event->buttons() == Qt::RightButton)
+      remove(mouse_event->pos());
+    return true;
+  }
+  case QEvent::MouseMove:
+  {
+    const QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
+    move(mouse_event->pos());
+    return true;
+  }
+  case QEvent::KeyPress:
+  {
+    const QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
+
+    const auto delta = 5;
+    switch (key_event->key())
+    {
+    case Qt::Key_Up:
+    {
+      shift_curve_cursor(true);
+      return true;
+    }
+    case Qt::Key_Down:
+    {
+      shift_curve_cursor(false);
+      return true;
+    }
+    case Qt::Key_Right:
+    case Qt::Key_Plus:
+    {
+      if (selected_curve_)
+        shift_point_cursor(true);
+      else
+        shift_curve_cursor(true);
+      return true;
+    }
+    case Qt::Key_Left:
+    case Qt::Key_Minus:
+    {
+      if (selected_curve_)
+        shift_point_cursor(false);
+      else
+        shift_curve_cursor(true);
+      return true;
+    }
+
+    case Qt::Key_1:
+    {
+      move_by(-delta, delta);
+      break;
+    }
+    case Qt::Key_2:
+    {
+      move_by(0, delta);
+      break;
+    }
+    case Qt::Key_3:
+    {
+      move_by(delta, delta);
+      break;
+    }
+    case Qt::Key_4:
+    {
+      move_by(-delta, 0);
+      break;
+    }
+    case Qt::Key_6:
+    {
+      move_by(delta, 0);
+      break;
+    }
+    case Qt::Key_7:
+    {
+      move_by(-delta, -delta);
+      break;
+    }
+    case Qt::Key_8:
+    {
+      move_by(0, -delta);
+      break;
+    }
+    case Qt::Key_9:
+    {
+      move_by(delta, -delta);
+      break;
+    }
+    default:
+      break;
+    }
+  }
+  default:
+    break;
+  }
+
+  return QObject::eventFilter(object, event);
+}
+bool plot_interactor::event      (QEvent*  event)
+{
+  if (event->type() == QEvent::User)
+  {
+    show_cursor(true);
+    return true;
+  }
+  return QObject::event(event);
+}
+
+void plot_interactor::select_or_add(const QPoint& pos)
+{
+  QwtPlotCurve* curve = nullptr;
+  auto dist  = 10e10;
+  auto index = -1;
+
+  const auto& item_list = plot()->itemList();
+  for (auto it = item_list.begin(); it != item_list.end(); ++it)
+  {
+    if ((*it)->rtti() == QwtPlotItem::Rtti_PlotCurve)
+    {
+      auto c = static_cast<QwtPlotCurve*>(*it);
+      
+      double d;
+      auto idx = c->closestPoint(pos, &d);
+      if (d < dist && idx != 0 && idx != c->dataSize() - 1)
+      {
+        curve = c;
+        index = idx;
+        dist  = d;
+      }
+    }
+  }
+
+  show_cursor(false);
+  selected_curve_ = nullptr;
+  selected_point_ = -1;
+
+  if (curve)
+  {
+    selected_curve_ = curve;
+    selected_point_ = index;
+    show_cursor(true);
+
+    if(dist >= 8)
+    {
+      auto x = plot()->invTransform(selected_curve_->xAxis(), pos.x());
+      auto y = plot()->invTransform(selected_curve_->yAxis(), pos.y());
+      QPointF sample(x, y);
+
+      selected_point_ = curve->sample(index).x() < sample.x() ? index + 1 : index;
+
+      QVector<QPointF> samples;
+      for (auto i = 0; i < curve->dataSize(); ++i)
+        samples.push_back(curve->sample(i));
+      samples.insert(selected_point_, sample);
+      curve->setSamples(samples);
+    }
+  }
+
+  on_change();
+}
+void plot_interactor::remove       (const QPoint& pos)
+{
+  QwtPlotCurve* curve = nullptr;
+  auto dist  = 10e10;
+  auto index = -1;
+
+  const auto& item_list = plot()->itemList();
+  for (auto it = item_list.begin(); it != item_list.end(); ++it)
+  {
+    if ((*it)->rtti() == QwtPlotItem::Rtti_PlotCurve)
+    {
+      auto c = static_cast<QwtPlotCurve*>(*it);
+
+      double d;
+      auto idx = c->closestPoint(pos, &d);
+      if (d < dist && idx != 0 && idx != c->dataSize() - 1)
+      {
+        curve = c;
+        index = idx;
+        dist  = d;
+      }
+    }
+  }
+
+  show_cursor(false);
+  selected_curve_ = nullptr;
+  selected_point_ = -1;
+
+  if (curve && dist < 4)
+  {
+    QVector<QPointF> samples;
+    for(auto i = 0; i < curve->dataSize(); ++i)
+      if(i != index)
+        samples.push_back(curve->sample(i));
+    curve->setSamples(samples);
+  }
+
+  on_change();
+}
+void plot_interactor::move         (const QPoint& pos)
+{
+  if (!selected_curve_)
+    return;
+
+  QVector<double> xData(selected_curve_->dataSize());
+  QVector<double> yData(selected_curve_->dataSize());
+
+  for (auto i = 0; i < static_cast<int>(selected_curve_->dataSize()); i++)
+  {
+    if (i == selected_point_)
+    {
+      xData[i] = plot()->invTransform(selected_curve_->xAxis(), pos.x());
+      yData[i] = plot()->invTransform(selected_curve_->yAxis(), pos.y());
+    }
+    else
+    {
+      const auto sample = selected_curve_->sample(i);
+      xData[i] = sample.x();
+      yData[i] = sample.y();
+    }
+  }
+  selected_curve_->setSamples(xData, yData);
+
+  auto plotCanvas = qobject_cast<QwtPlotCanvas *>(plot()->canvas());
+
+  plotCanvas->setPaintAttribute(QwtPlotCanvas::ImmediatePaint, true);
+  plot()->replot();
+  plotCanvas->setPaintAttribute(QwtPlotCanvas::ImmediatePaint, false);
+
+  show_cursor(true);
+
+  on_change();
+}
+void plot_interactor::move_by      (int dx, int dy)
+{
+  if (dx == 0 && dy == 0)
+    return;
+  if (!selected_curve_)
+    return;
+  const auto sample = selected_curve_->sample(selected_point_);
+  const auto x      = plot()->transform(selected_curve_->xAxis(), sample.x());
+  const auto y      = plot()->transform(selected_curve_->yAxis(), sample.y());
+  move(QPoint(qRound(x + dx), qRound(y + dy)));
+
+  on_change();
+}
+
+void plot_interactor::show_cursor       (bool enable)
+{
+  if (!selected_curve_)
+    return;
+
+  auto symbol = const_cast<QwtSymbol *>(selected_curve_->symbol());
+
+  const auto brush = symbol->brush();
+  if (enable)
+    symbol->setBrush(symbol->brush().color().dark(180));
+
+  QwtPlotDirectPainter direct_painter;
+  direct_painter.drawSeries(selected_curve_, selected_point_, selected_point_);
+
+  if (enable)
+    symbol->setBrush(brush);
+}
+void plot_interactor::shift_curve_cursor(bool up    )
+{
+  QwtPlotItemIterator it;
+
+  const auto& item_list = plot()->itemList();
+
+  QwtPlotItemList curve_list;
+  for (it = item_list.begin(); it != item_list.end(); ++it)
+  {
+    if ((*it)->rtti() == QwtPlotItem::Rtti_PlotCurve)
+      curve_list += *it;
+  }
+  if (curve_list.isEmpty())
+    return;
+
+  it = curve_list.begin();
+
+  if (selected_curve_)
+  {
+    for (it = curve_list.begin(); it != curve_list.end(); ++it)
+    {
+      if (selected_curve_ == *it)
+        break;
+    }
+    if (it == curve_list.end())
+      it = curve_list.begin();
+
+    if (up)
+    {
+      ++it;
+      if (it == curve_list.end())
+        it = curve_list.begin();
+    }
+    else
+    {
+      if (it == curve_list.begin())
+        it = curve_list.end();
+      --it;
+    }
+  }
+
+  show_cursor(false);
+  selected_point_ = 0;
+  selected_curve_ = static_cast<QwtPlotCurve *>(*it);
+  show_cursor(true );
+}
+void plot_interactor::shift_point_cursor(bool up    )
+{
+  if (!selected_curve_)
+    return;
+
+  auto index = selected_point_ + (up ? 1 : -1);
+  index = (index + selected_curve_->dataSize()) % selected_curve_->dataSize();
+
+  if (index != selected_point_)
+  {
+    show_cursor(false);
+    selected_point_ = index;
+    show_cursor(true);
+  }
+}
+  
+      QwtPlot* plot_interactor::plot()
+{
+  return qobject_cast<QwtPlot*>(parent());
+}
+const QwtPlot* plot_interactor::plot() const
+{
+  return qobject_cast<const QwtPlot *>(parent());
+}
+
+}
\ No newline at end of file
diff --git a/source/ui/selection_square.cpp b/source/ui/widgets/roi_rectangle.cpp
similarity index 71%
rename from source/ui/selection_square.cpp
rename to source/ui/widgets/roi_rectangle.cpp
index b22cc349c660979fbd02d938352743b3e41e953a..a9aa8a62cc1a1f02b0223e1542e43a91753c5c46 100644
--- a/source/ui/selection_square.cpp
+++ b/source/ui/widgets/roi_rectangle.cpp
@@ -1,11 +1,11 @@
-#include /* implements */ <ui/selection_square.hpp>
+#include <pli_vis/ui/widgets/roi_rectangle.hpp>
 
 #include <QHBoxLayout>
 #include <QSizeGrip>
 
 namespace pli
 {
-selection_square::selection_square(QWidget* parent) : QWidget(parent), rubber_band_(new QRubberBand(QRubberBand::Rectangle, this))
+roi_rectangle::roi_rectangle(QWidget* parent) : QWidget(parent), rubber_band_(new QRubberBand(QRubberBand::Rectangle, this))
 {
   setWindowFlags(Qt::SubWindow);
   
@@ -23,7 +23,7 @@ selection_square::selection_square(QWidget* parent) : QWidget(parent), rubber_ba
   move(0, 0);
   show();
 }
-void selection_square::resizeEvent(QResizeEvent* event)
+void roi_rectangle::resizeEvent(QResizeEvent* event)
 {
   rubber_band_->resize(size());
 }
diff --git a/source/ui/overview_image.cpp b/source/ui/widgets/roi_selector.cpp
similarity index 63%
rename from source/ui/overview_image.cpp
rename to source/ui/widgets/roi_selector.cpp
index e912891bc0add722205b3a91aeffccc50bfef17f..de22fc3a92f0c08ee75a4743b64b1201b8485890 100644
--- a/source/ui/overview_image.cpp
+++ b/source/ui/widgets/roi_selector.cpp
@@ -1,35 +1,35 @@
-#include /* implements */ <ui/overview_image.hpp>
+#include <pli_vis/ui/widgets/roi_selector.hpp>
 
 #include <QMouseEvent>
 
 namespace pli
 {
-overview_image::overview_image(QWidget* parent) : QLabel(parent), selection_square_(new selection_square(this))
+roi_selector::roi_selector(QWidget* parent) : QLabel(parent), selection_square_(new roi_rectangle(this))
 {
   selection_square_->setMouseTracking(true);
 }
 
-void overview_image::set_selection_offset_percentage(const std::array<float, 2>& perc)
+void roi_selector::set_selection_offset_percentage(const std::array<float, 2>& perc)
 {
   selection_square_->move  (perc[0] * width(), perc[1] * height());
 }
-void overview_image::set_selection_size_percentage  (const std::array<float, 2>& perc)
+void roi_selector::set_selection_size_percentage  (const std::array<float, 2>& perc)
 {
   selection_square_->resize(perc[0] * width(), perc[1] * height());
 }
 
-std::array<float, 2>  overview_image::selection_offset_percentage() const
+std::array<float, 2>  roi_selector::selection_offset_percentage() const
 {
   auto val = selection_square_->pos();
   return {static_cast<float>(val.x    ()) / width(), static_cast<float>(val.y     ()) / height()};
 }
-std::array<float, 2>  overview_image::selection_size_percentage  () const
+std::array<float, 2>  roi_selector::selection_size_percentage  () const
 {
   auto val = selection_square_->size();
   return {static_cast<float>(val.width()) / width(), static_cast<float>(val.height()) / height()};
 }
 
-void overview_image::mousePressEvent(QMouseEvent* event)
+void roi_selector::mousePressEvent(QMouseEvent* event)
 {
   auto position = event->localPos();
   selection_square_->resize(selection_square_->minimumSize());
@@ -37,12 +37,12 @@ void overview_image::mousePressEvent(QMouseEvent* event)
   dragging_ = true;
   on_selection_change(selection_offset_percentage(), selection_size_percentage());
 }
-void overview_image::mouseReleaseEvent(QMouseEvent* event)
+void roi_selector::mouseReleaseEvent(QMouseEvent* event)
 {
   dragging_ = false;
   on_selection_change(selection_offset_percentage(), selection_size_percentage());
 }
-void overview_image::mouseMoveEvent(QMouseEvent* event)
+void roi_selector::mouseMoveEvent(QMouseEvent* event)
 {
   if (dragging_)
   {
diff --git a/source/ui/widgets/transfer_function_editor.cpp b/source/ui/widgets/transfer_function_editor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e1f734f3db5e1e618939350f87825df3a0181e85
--- /dev/null
+++ b/source/ui/widgets/transfer_function_editor.cpp
@@ -0,0 +1,90 @@
+#include <pli_vis/ui/widgets/transfer_function_editor.hpp>
+
+#include <qwt/qwt_curve_fitter.h>
+#include <qwt/qwt_plot_curve.h>
+#include <qwt/qwt_plot_grid.h>
+#include <qwt/qwt_plot_histogram.h>
+#include <qwt/qwt_symbol.h>
+
+#include <pli_vis/ui/utility/plot_interactor.hpp>
+
+namespace pli
+{
+transfer_function_editor::transfer_function_editor(QWidget* parent) : QwtPlot(parent)
+{
+  auto font = axisFont(0);
+  font.setPointSize(8);
+  setAxisFont  (0, font);
+  setAxisFont  (2, font);
+  setAxisScale (0, 0, 255);
+  setAxisScale (2, 0, 255);
+  setAutoReplot(true);
+
+  auto grid = new QwtPlotGrid;
+  grid->enableXMin (true);
+  grid->enableYMin (true);
+  grid->setMajorPen(QPen(Qt::black, 0, Qt::DotLine));
+  grid->setMinorPen(QPen(Qt::gray , 0, Qt::DotLine));
+  grid->attach     (this);
+
+  histogram_ = new QwtPlotHistogram;
+  histogram_->setPen  (QPen  (Qt::gray, 0));
+  histogram_->setBrush(QBrush(Qt::gray));
+  histogram_->setStyle(QwtPlotHistogram::HistogramStyle::Columns);
+  histogram_->attach  (this);
+
+  for (auto i = 0; i < 4; i++)
+  {
+    curves_[i] = new QwtPlotCurve();
+    curves_[i]->setStyle         (QwtPlotCurve::CurveStyle::Lines);
+    curves_[i]->setCurveAttribute(QwtPlotCurve::CurveAttribute::Fitted);
+    curves_[i]->setRenderHint    (QwtPlotItem::RenderHint::RenderAntialiased);
+    curves_[i]->attach           (this);
+
+    auto curve_fitter = new QwtSplineCurveFitter;
+    curve_fitter->setFitMode    (QwtSplineCurveFitter::FitMode::ParametricSpline);
+    curve_fitter->setSplineSize (100);
+    curves_[i]  ->setCurveFitter(curve_fitter);
+
+    QVector<QPointF> values;
+    values.push_back(QPointF(0  , 0     ));
+    values.push_back(QPointF(125, 10 * i));
+    values.push_back(QPointF(250, 0     ));
+    curves_[i]->setSamples(values); 
+  }
+  curves_[0]->setPen   (Qt::red);
+  curves_[0]->setSymbol(new QwtSymbol(QwtSymbol::Ellipse, QBrush(Qt::red  ), QPen(Qt::red  , 1), QSize(4, 4)));
+  curves_[1]->setPen   (Qt::green);
+  curves_[1]->setSymbol(new QwtSymbol(QwtSymbol::Ellipse, QBrush(Qt::green), QPen(Qt::green, 1), QSize(4, 4)));
+  curves_[2]->setPen   (Qt::blue );
+  curves_[2]->setSymbol(new QwtSymbol(QwtSymbol::Ellipse, QBrush(Qt::blue ), QPen(Qt::blue , 1), QSize(4, 4)));
+  curves_[3]->setPen   (Qt::black);
+  curves_[3]->setSymbol(new QwtSymbol(QwtSymbol::Ellipse, QBrush(Qt::black), QPen(Qt::black, 1), QSize(4, 4)));
+
+  auto point_picker = new plot_interactor(this);
+  connect(point_picker, SIGNAL(on_change()), this, SIGNAL(on_change()));
+}
+
+std::vector<float4> transfer_function_editor::get_function()
+{
+  std::vector<float4> function(256, float4{0.0, 0.0, 0.0, 0.0});
+  QwtSplineCurveFitter fitter;
+  fitter.setFitMode   (QwtSplineCurveFitter::FitMode::ParametricSpline);
+  fitter.setSplineSize(256);
+  QVector<QPointF> red_points  ; for (auto i = 0; i < curves_[0]->dataSize(); i++) red_points  .push_back(curves_[0]->sample(i)); auto reds   = fitter.fitCurve(red_points  );
+  QVector<QPointF> green_points; for (auto i = 0; i < curves_[1]->dataSize(); i++) green_points.push_back(curves_[1]->sample(i)); auto greens = fitter.fitCurve(green_points);
+  QVector<QPointF> blue_points ; for (auto i = 0; i < curves_[2]->dataSize(); i++) blue_points .push_back(curves_[2]->sample(i)); auto blues  = fitter.fitCurve(blue_points );
+  QVector<QPointF> alpha_points; for (auto i = 0; i < curves_[3]->dataSize(); i++) alpha_points.push_back(curves_[3]->sample(i)); auto alphas = fitter.fitCurve(alpha_points);
+  for (auto i = 0; i < 256; i++)
+    function[i] = float4{float(reds[i].y()) / 255.0F, float(greens[i].y()) / 255.0F, float(blues[i].y()) / 255.0F, float(alphas[i].y()) / 255.0F};
+  return function;
+}
+
+void transfer_function_editor::set_histogram_entries(const std::vector<std::size_t>& histogram_entries)
+{
+  QVector<QwtIntervalSample> samples;
+  for (auto i = 0; i < histogram_entries.size(); i++)
+    samples.push_back(QwtIntervalSample(histogram_entries[i], i, i + 1));
+  histogram_->setSamples(samples);
+}
+}
diff --git a/source/ui/viewer.cpp b/source/ui/widgets/viewer.cpp
similarity index 70%
rename from source/ui/viewer.cpp
rename to source/ui/widgets/viewer.cpp
index f7041d21fa6c1b40cd93243d543c5378f1c85230..aa3b566bb68ed25c2e041eb5ea98fdd5ea84892f 100644
--- a/source/ui/viewer.cpp
+++ b/source/ui/widgets/viewer.cpp
@@ -1,15 +1,15 @@
-#include /* implements */ <ui/viewer.hpp>
+#include <pli_vis/ui/widgets/viewer.hpp>
 
 #include <QKeyEvent>
 #include <QTimer>
 
-#include <utility/qt_text_browser_sink.hpp>
+#include <pli_vis/visualization/interactors/simple_interactor.hpp>
 
 namespace pli
 {
-viewer::viewer(QWidget* parent) : QOpenGLWidget(parent), interactor_(&camera_), wait_spinner_(new wait_spinner(this, true, false))
+viewer::viewer(QWidget* parent) : QOpenGLWidget(parent), interactor_(std::make_unique<simple_interactor>(&camera_))
 {
-  camera_.set_translation(vec3f(0, 0, 1));
+  reset_camera_transform();
 
   setFocusPolicy(Qt::StrongFocus);
 
@@ -30,6 +30,13 @@ void viewer::remove_renderable(renderable* renderable)
     renderables_.end  ());
 }
 
+void viewer::reset_camera_transform()
+{
+  camera_.set_orthographic_size(100);
+  camera_.set_translation      ({ 0, 0, -100 });
+  camera_.look_at              ({ 0, 0,    0 }, { 0, -1, 0 });
+}
+
 void viewer::initializeGL   ()
 {
   makeCurrent ();
@@ -49,7 +56,7 @@ void viewer::paintGL        ()
 {
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
-  interactor_.update_transform();
+  interactor_->update_transform();
   for (auto& renderable : renderables_)
     if (renderable->active())
       renderable->render(&camera_);
@@ -61,26 +68,21 @@ void viewer::resizeGL       (int w, int h)
 }
 void viewer::keyPressEvent  (QKeyEvent*   event)
 {
-  interactor_.key_press_handler(event);
+  interactor_->key_press_handler(event);
   update();
 }
 void viewer::keyReleaseEvent(QKeyEvent*   event)
 {
-  interactor_.key_release_handler(event);
+  interactor_->key_release_handler(event);
   update();
 }
 void viewer::mousePressEvent(QMouseEvent* event)
 {
-  interactor_.mouse_press_handler(event);
+  interactor_->mouse_press_handler(event);
   update();
 }
 void viewer::mouseMoveEvent (QMouseEvent* event)
 {
-  interactor_.mouse_move_handler(event);
-}
-
-void viewer::set_wait_spinner_enabled(bool enabled) const
-{
-  enabled ? wait_spinner_->start() : wait_spinner_->stop();
+  interactor_->mouse_move_handler(event);
 }
 }
diff --git a/source/ui/wait_spinner.cpp b/source/ui/widgets/wait_spinner.cpp
similarity index 99%
rename from source/ui/wait_spinner.cpp
rename to source/ui/widgets/wait_spinner.cpp
index 83baa787a0cdd502f839db3daf17243ce5bf12ea..f69703a19551ee1dc63ca29689424a70dd062bcc 100644
--- a/source/ui/wait_spinner.cpp
+++ b/source/ui/widgets/wait_spinner.cpp
@@ -1,4 +1,4 @@
-#include "ui/wait_spinner.hpp"
+#include "pli_vis/ui/widgets/wait_spinner.hpp"
 
 #include <cmath>
 #include <algorithm>
diff --git a/source/ui/window.cpp b/source/ui/window.cpp
deleted file mode 100644
index a6d5d2416eb2d1ab57a5fb3b353deafb82566a3d..0000000000000000000000000000000000000000
--- a/source/ui/window.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include /* implements */ <ui/window.hpp>
-
-#include <cuda_runtime_api.h>
-
-#include <ui/plugins/plugin.hpp>
-#include <utility/qt_text_browser_sink.hpp>
-
-namespace pli
-{
-window:: window()
-{
-  setupUi(this);
-  showMaximized();
-
-  set_sink    (std::make_shared<qt_text_browser_sink>(console));
-  bind_actions();
-
-  //splitter_vertical_left->setSizes(QList<int>{height(), 0});
-
-  plugins_ = findChildren<plugin*>(QRegExp("plugin")).toVector().toStdVector();
-  for (auto plugin : plugins_)
-    plugin->set_owner(this);
-  for (auto plugin : plugins_)
-    plugin->awake();
-  for (auto plugin : plugins_)
-    plugin->start();
-
-  action_help_version ->trigger();
-  action_help_gpu_info->trigger();
-}
-window::~window()
-{
-  for (auto plugin : plugins_)
-    plugin->destroy();
-}
-
-void window::bind_actions     ()
-{  
-  connect(action_fullscreen   , &QAction::triggered, [&] 
-  {
-    logger_->info(std::string("Toggling fullscreen mode."));
-    isFullScreen() ? showNormal() : showFullScreen();
-  });
-  connect(action_file_exit    , &QAction::triggered, [&] 
-  {
-    logger_->info(std::string("Closing window."));
-    close();
-  });
-  connect(action_help_version , &QAction::triggered, [&] 
-  {
-    logger_->info(std::string("Version ") + __DATE__);
-  });
-  connect(action_help_gpu_info, &QAction::triggered, [&]
-  {
-    std::size_t free, total;
-    cudaMemGetInfo(&free, &total);
-    logger_->info("Available GPU memory: {} MB. Total GPU memory: {} MB.", free * 1E-6, total * 1E-6);
-  });
-}
-}
diff --git a/source/visualization/algorithms/lineao_streamline_renderer.cpp b/source/visualization/algorithms/lineao_streamline_renderer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b86ff39c547e91506e49ec7b6b504241aa95d8df
--- /dev/null
+++ b/source/visualization/algorithms/lineao_streamline_renderer.cpp
@@ -0,0 +1,290 @@
+#include <pli_vis/visualization/algorithms/lineao_streamline_renderer.hpp>
+
+#include <pli_vis/visualization/primitives/camera.hpp>
+#include <shaders/lineao_color_pass.vert.glsl>
+#include <shaders/lineao_color_pass.frag.glsl>
+#include <shaders/lineao_main_pass.vert.glsl>
+#include <shaders/lineao_main_pass.frag.glsl>
+#include <shaders/lineao_normal_depth_pass.vert.glsl>
+#include <shaders/lineao_normal_depth_pass.frag.glsl>
+#include <shaders/lineao_zoom_pass.vert.glsl>
+#include <shaders/lineao_zoom_pass.frag.glsl>
+
+namespace pli
+{
+void lineao_streamline_renderer::initialize()
+{
+  vertex_buffer_   .reset(new gl::array_buffer);
+  direction_buffer_.reset(new gl::array_buffer);
+
+  auto screen_size = this->screen_size();
+  initialize_normal_depth_pass(screen_size);
+  initialize_color_pass       (screen_size);
+  initialize_zoom_pass        (screen_size);
+  initialize_main_pass        (screen_size);
+}
+void lineao_streamline_renderer::render    (const camera* camera)
+{
+  auto screen_size = this->screen_size();
+  render_normal_depth_pass(camera, screen_size);
+  render_color_pass       (camera, screen_size);
+  render_zoom_pass        (camera, screen_size);
+  render_main_pass        (camera, screen_size);
+}
+  
+void lineao_streamline_renderer::set_data(
+  const std::vector<float3>& points        , 
+  const std::vector<float3>& directions    ,
+  const std::vector<float4>& random_vectors)
+{
+  auto screen_size = this->screen_size();
+
+  draw_count_ = points.size();
+  
+  vertex_buffer_   ->bind    ();
+  vertex_buffer_   ->set_data(draw_count_ * sizeof(float3), points    .data());
+  vertex_buffer_   ->unbind  ();
+  
+  direction_buffer_->bind    ();
+  direction_buffer_->set_data(draw_count_ * sizeof(float3), directions.data());
+  direction_buffer_->unbind  ();
+  
+  random_texture_->bind     ();
+  random_texture_->set_image(GL_RGBA32F, screen_size[0], screen_size[1], ao_samples_, GL_RGBA, GL_FLOAT, random_vectors.data());
+  random_texture_->unbind   ();
+}
+void lineao_streamline_renderer::set_ao_samples(const std::size_t& ao_samples)
+{
+  ao_samples_ = ao_samples;
+}
+
+glm::uvec2 lineao_streamline_renderer::screen_size() const
+{
+  glm::ivec4 viewport;
+  glGetIntegerv(GL_VIEWPORT, reinterpret_cast<GLint*>(&viewport));
+  return glm::uvec2(viewport.z, viewport.w);
+}
+std::size_t lineao_streamline_renderer::ao_samples() const
+{
+  return ao_samples_;
+}
+
+void lineao_streamline_renderer::initialize_normal_depth_pass(const glm::uvec2& screen_size)
+{
+  normal_depth_program_     .reset(new gl::program     );
+  normal_depth_vertex_array_.reset(new gl::vertex_array);
+  normal_depth_map_         .reset(new render_target(screen_size));
+
+  auto color_texture = normal_depth_map_->color_texture();
+  color_texture->bind      ();
+  color_texture->min_filter(GL_LINEAR_MIPMAP_LINEAR);
+  color_texture->mag_filter(GL_LINEAR);
+  color_texture->unbind    ();
+  
+  normal_depth_program_->attach_shader(gl::vertex_shader  (shaders::lineao_normal_depth_pass_vert));
+  normal_depth_program_->attach_shader(gl::fragment_shader(shaders::lineao_normal_depth_pass_frag));
+  normal_depth_program_->link();
+  
+  normal_depth_vertex_array_->bind  ();
+  normal_depth_program_     ->bind  ();
+  vertex_buffer_            ->bind  ();
+  normal_depth_program_     ->set_attribute_buffer  ("vertex", 3, GL_FLOAT);
+  normal_depth_program_     ->enable_attribute_array("vertex");
+  vertex_buffer_            ->unbind();
+  direction_buffer_         ->bind  ();
+  normal_depth_program_     ->set_attribute_buffer  ("direction" , 3, GL_FLOAT);
+  normal_depth_program_     ->enable_attribute_array("direction");
+  direction_buffer_         ->unbind();
+  normal_depth_program_     ->unbind();
+  normal_depth_vertex_array_->unbind();
+}
+void lineao_streamline_renderer::initialize_color_pass       (const glm::uvec2& screen_size)
+{
+  color_program_     .reset(new gl::program     );
+  color_vertex_array_.reset(new gl::vertex_array);
+  color_map_         .reset(new render_target(screen_size));
+  
+  color_program_->attach_shader(gl::vertex_shader  (shaders::lineao_color_pass_vert));
+  color_program_->attach_shader(gl::fragment_shader(shaders::lineao_color_pass_frag));
+  color_program_->link();
+  
+  color_vertex_array_->bind  ();
+  color_program_     ->bind  ();
+  vertex_buffer_     ->bind  ();
+  color_program_     ->set_attribute_buffer  ("vertex", 3, GL_FLOAT);
+  color_program_     ->enable_attribute_array("vertex");
+  vertex_buffer_     ->unbind();
+  direction_buffer_  ->bind  ();
+  color_program_     ->set_attribute_buffer  ("direction" , 3, GL_FLOAT);
+  color_program_     ->enable_attribute_array("direction");
+  direction_buffer_  ->unbind();
+  color_program_     ->unbind();
+  color_vertex_array_->unbind();
+}
+void lineao_streamline_renderer::initialize_zoom_pass        (const glm::uvec2& screen_size)
+{
+  zoom_program_     .reset(new gl::program     );
+  zoom_vertex_array_.reset(new gl::vertex_array);
+  zoom_map_         .reset(new render_target(screen_size));
+  
+  zoom_program_->attach_shader(gl::vertex_shader  (shaders::lineao_zoom_pass_vert));
+  zoom_program_->attach_shader(gl::fragment_shader(shaders::lineao_zoom_pass_frag));
+  zoom_program_->link();
+  
+  zoom_vertex_array_->bind  ();
+  zoom_program_     ->bind  ();
+  vertex_buffer_    ->bind  ();
+  zoom_program_     ->set_attribute_buffer  ("vertex", 3, GL_FLOAT);
+  zoom_program_     ->enable_attribute_array("vertex");
+  vertex_buffer_    ->unbind();
+  zoom_program_     ->unbind();
+  zoom_vertex_array_->unbind();
+}
+void lineao_streamline_renderer::initialize_main_pass        (const glm::uvec2& screen_size)
+{
+  float3 vertices [6] = {{-1,-1,0}, {1,-1,0}, {1,1,0}, {-1,-1,0}, {1,1,0}, {-1,1,0}};
+  gl::array_buffer vertex_buffer;
+  vertex_buffer.bind    ();
+  vertex_buffer.set_data(6 * sizeof(float3), vertices);
+  vertex_buffer.unbind  ();
+  
+  float2 texcoords[6] = {{0,0}, {1,0}, {1,1}, {0,0}, {1,1}, {0,1}};
+  gl::array_buffer texcoord_buffer;
+  texcoord_buffer.bind    ();
+  texcoord_buffer.set_data(6 * sizeof(float2), texcoords);
+  texcoord_buffer.unbind  ();
+  
+  program_       .reset(new gl::program     );
+  vertex_array_  .reset(new gl::vertex_array);
+  random_texture_.reset(new gl::texture_3d  );
+
+  random_texture_->bind      ();
+  random_texture_->wrap_s    (GL_REPEAT );
+  random_texture_->wrap_t    (GL_REPEAT );
+  random_texture_->wrap_r    (GL_REPEAT );
+  random_texture_->min_filter(GL_NEAREST);
+  random_texture_->mag_filter(GL_NEAREST);
+  random_texture_->unbind    ();
+
+  program_->attach_shader(gl::vertex_shader  (shaders::lineao_main_pass_vert));
+  program_->attach_shader(gl::fragment_shader(shaders::lineao_main_pass_frag));
+  program_->link();
+  
+  vertex_array_  ->bind  ();
+  program_       ->bind  ();
+  vertex_buffer  . bind  ();
+  program_       ->set_attribute_buffer  ("vertex", 3, GL_FLOAT);
+  program_       ->enable_attribute_array("vertex");
+  vertex_buffer  . unbind();
+  texcoord_buffer. bind  ();
+  program_       ->set_attribute_buffer  ("texcoords", 2, GL_FLOAT);
+  program_       ->enable_attribute_array("texcoords");
+  texcoord_buffer. unbind();
+
+  program_       ->unbind();
+  vertex_array_  ->unbind();
+}
+
+void lineao_streamline_renderer::render_normal_depth_pass(const camera* camera, const glm::uvec2& screen_size) const
+{
+  normal_depth_map_->resize(screen_size);
+  normal_depth_map_->bind  ();
+
+  normal_depth_vertex_array_->bind  ();
+  normal_depth_program_     ->bind  ();
+  normal_depth_program_     ->set_uniform("screen_size", screen_size                      );
+  normal_depth_program_     ->set_uniform("model"      , absolute_matrix                ());
+  normal_depth_program_     ->set_uniform("view"       , camera->inverse_absolute_matrix());
+  normal_depth_program_     ->set_uniform("projection" , camera->projection_matrix      ());
+
+  glEnable    (GL_LINE_SMOOTH);
+  glHint      (GL_LINE_SMOOTH_HINT, GL_NICEST);
+  glEnable    (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glViewport  (0, 0, screen_size.x, screen_size.y);
+  glClear     (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  glDrawArrays(GL_LINES, 0, GLsizei(draw_count_));
+  glDisable   (GL_BLEND);
+  glDisable   (GL_LINE_SMOOTH);
+  
+  normal_depth_program_     ->unbind();
+  normal_depth_vertex_array_->unbind();
+
+  normal_depth_map_->color_texture()->generate_mipmaps();
+  normal_depth_map_->unbind();
+}
+void lineao_streamline_renderer::render_color_pass       (const camera* camera, const glm::uvec2& screen_size) const
+{
+  color_map_->resize(screen_size);
+  color_map_->bind  ();
+
+  color_vertex_array_->bind  ();
+  color_program_     ->bind  ();
+  color_program_     ->set_uniform("model"     , absolute_matrix                ());
+  color_program_     ->set_uniform("view"      , camera->inverse_absolute_matrix());
+  color_program_     ->set_uniform("projection", camera->projection_matrix      ());
+
+  glEnable    (GL_LINE_SMOOTH);
+  glHint      (GL_LINE_SMOOTH_HINT, GL_NICEST);
+  glEnable    (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glViewport  (0, 0, screen_size.x, screen_size.y);
+  glClear     (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  glDrawArrays(GL_LINES, 0, GLsizei(draw_count_));
+  glDisable   (GL_BLEND);
+  glDisable   (GL_LINE_SMOOTH);
+  
+  color_program_     ->unbind();
+  color_vertex_array_->unbind();
+
+  color_map_->unbind();
+}
+void lineao_streamline_renderer::render_zoom_pass        (const camera* camera, const glm::uvec2& screen_size) const
+{
+  zoom_map_->resize(screen_size);
+  zoom_map_->bind  ();
+
+  zoom_vertex_array_->bind  ();
+  zoom_program_     ->bind  ();
+  zoom_program_     ->set_uniform("model"     , absolute_matrix                ());
+  zoom_program_     ->set_uniform("view"      , camera->inverse_absolute_matrix());
+  zoom_program_     ->set_uniform("projection", camera->projection_matrix      ());
+
+  glEnable    (GL_LINE_SMOOTH);
+  glHint      (GL_LINE_SMOOTH_HINT, GL_NICEST);
+  glEnable    (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glViewport  (0, 0, screen_size.x, screen_size.y);
+  glClear     (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  glDrawArrays(GL_LINES, 0, GLsizei(draw_count_));
+  glDisable   (GL_BLEND);
+  glDisable   (GL_LINE_SMOOTH);
+  
+  zoom_program_     ->unbind();
+  zoom_vertex_array_->unbind();
+
+  zoom_map_->unbind();
+}
+void lineao_streamline_renderer::render_main_pass        (const camera* camera, const glm::uvec2& screen_size) const
+{
+  vertex_array_->bind  ();
+  program_     ->bind  ();
+  program_     ->set_uniform("sample_count", unsigned(ao_samples_));
+  program_     ->set_uniform("screen_size" , screen_size);
+
+  gl::texture_2d::set_active(GL_TEXTURE0); normal_depth_map_->color_texture()->bind(); program_->set_uniform("normal_depth_texture", 0);
+  gl::texture_2d::set_active(GL_TEXTURE1); color_map_       ->color_texture()->bind(); program_->set_uniform("color_texture"       , 1);
+  gl::texture_2d::set_active(GL_TEXTURE2); zoom_map_        ->color_texture()->bind(); program_->set_uniform("zoom_texture"        , 2);
+  gl::texture_2d::set_active(GL_TEXTURE3); random_texture_                   ->bind(); program_->set_uniform("random_texture"      , 3);
+  
+  glDrawArrays(GL_TRIANGLES, 0, 6);
+  
+  gl::texture_2d::set_active(GL_TEXTURE3); random_texture_                   ->unbind();
+  gl::texture_2d::set_active(GL_TEXTURE2); zoom_map_        ->color_texture()->unbind();
+  gl::texture_2d::set_active(GL_TEXTURE1); color_map_       ->color_texture()->unbind();
+  gl::texture_2d::set_active(GL_TEXTURE0); normal_depth_map_->color_texture()->unbind();
+  
+  program_     ->unbind();
+  vertex_array_->unbind();
+}
+}
diff --git a/source/visualization/odf_field.cpp b/source/visualization/algorithms/odf_field.cpp
similarity index 66%
rename from source/visualization/odf_field.cpp
rename to source/visualization/algorithms/odf_field.cpp
index 483f4a13ff1e0b061c1ea37c6ec8e8d4ca8adf45..5af7cba161310dadcc3a82e8be0fd30b7e125ce2 100644
--- a/source/visualization/odf_field.cpp
+++ b/source/visualization/algorithms/odf_field.cpp
@@ -1,9 +1,11 @@
-#include /* implements */ <visualization/odf_field.hpp>
+#include <pli_vis/visualization/algorithms/odf_field.hpp>
 
-#include <cuda/odf_field.h>
-#include <math/camera.hpp>
-#include <shaders/odf_field.vert.glsl>
-#include <shaders/odf_field.frag.glsl>
+#include <algorithm>
+
+#include <pli_vis/cuda/odf_field.h>
+#include <pli_vis/visualization/primitives/camera.hpp>
+#include <shaders/odf_field_renderer.vert.glsl>
+#include <shaders/odf_field_renderer.frag.glsl>
 
 namespace pli
 {
@@ -15,8 +17,8 @@ void odf_field::initialize()
   color_buffer_  .reset(new gl::array_buffer);
   index_buffer_  .reset(new gl::index_buffer);
 
-  shader_program_->attach_shader(gl::vertex_shader  (shaders::odf_field_vert));
-  shader_program_->attach_shader(gl::fragment_shader(shaders::odf_field_frag));
+  shader_program_->attach_shader(gl::vertex_shader  (shaders::odf_field_renderer_vert));
+  shader_program_->attach_shader(gl::fragment_shader(shaders::odf_field_renderer_frag));
   shader_program_->link();
   
   shader_program_->bind();
@@ -28,8 +30,8 @@ void odf_field::initialize()
   vertex_buffer_ ->unbind();
 
   color_buffer_  ->bind();
-  shader_program_->set_attribute_buffer  ("color" , 4, GL_FLOAT);
-  shader_program_->enable_attribute_array("color");
+  shader_program_->set_attribute_buffer  ("direction" , 3, GL_FLOAT);
+  shader_program_->enable_attribute_array("direction");
   color_buffer_  ->unbind();
 
   vertex_array_  ->unbind();
@@ -41,14 +43,18 @@ void odf_field::render    (const camera* camera)
   vertex_array_  ->bind();
   index_buffer_  ->bind();
 
-  shader_program_->set_uniform("projection", camera->projection_matrix      ());
-  shader_program_->set_uniform("view"      , camera->inverse_absolute_matrix());
+  shader_program_->set_uniform("color_mode"    , color_mode_);
+  shader_program_->set_uniform("color_k"       , color_k_);
+  shader_program_->set_uniform("color_inverted", color_inverted_);
+  shader_program_->set_uniform("model"         , absolute_matrix                ());
+  shader_program_->set_uniform("view"          , camera->inverse_absolute_matrix());
+  shader_program_->set_uniform("projection"    , camera->projection_matrix      ());
 
   // Select by visible layers.
   auto dimension_count  = dimensions_.z > 1 ? 3 : 2;
-  auto min_dimension    = min(dimensions_.x, dimensions_.y);
+  auto min_dimension    = std::min(dimensions_.x, dimensions_.y);
   if (dimension_count == 3)
-    min_dimension = min(min_dimension, dimensions_.z);
+    min_dimension = std::min(min_dimension, dimensions_.z);
   auto max_layer        = int(log(min_dimension) / log(2));
   auto layer_offset     = 0;
   auto layer_dimensions = dimensions_;
@@ -86,12 +92,12 @@ void odf_field::render    (const camera* camera)
 
 void odf_field::set_data(
   const uint3&   dimensions       ,
-  const unsigned coefficient_count,
+  const unsigned maximum_degree   ,
   const float*   coefficients     ,
-  const uint2&   tessellations    , 
-  const float3&  vector_spacing   , 
+  const uint2&   tessellations    ,
   const uint3&   vector_dimensions, 
   const float    scale            ,
+  const bool     hierarchical     ,
   const bool     clustering       ,
   const float    cluster_threshold,
   std::function<void(const std::string&)> status_callback)
@@ -99,15 +105,18 @@ void odf_field::set_data(
   dimensions_    = dimensions;
   tessellations_ = tessellations;
 
-  auto base_voxel_count = dimensions_.x * dimensions_.y * dimensions_.z;
-  auto dimension_count  = dimensions_.z > 1 ? 3 : 2;
-  auto min_dimension    = min(dimensions_.x, dimensions_.y);
-  if (dimension_count == 3)
-    min_dimension = min(min_dimension, dimensions_.z);
-  auto max_layer        = int(log(min_dimension) / log(2));
-  auto voxel_count      = unsigned(base_voxel_count * 
-    ((1.0 - pow(1.0 / pow(2, dimension_count), max_layer + 1)) / 
-     (1.0 -     1.0 / pow(2, dimension_count))));
+  auto voxel_count = dimensions_.x * dimensions_.y * dimensions_.z;
+  if(hierarchical)
+  {
+    auto dimension_count  = dimensions_.z > 1 ? 3 : 2;
+    auto min_dimension    = std::min(dimensions_.x, dimensions_.y);
+    if (dimension_count == 3)
+      min_dimension       = std::min(min_dimension, dimensions_.z);
+    auto max_layer        = int(log(min_dimension) / log(2));
+    voxel_count           = unsigned(voxel_count * 
+      ((1.0 - pow(1.0 / pow(2, dimension_count), max_layer + 1)) / 
+       (1.0 -     1.0 / pow(2, dimension_count))));
+  }
 
   auto tessellation_count = tessellations.x * tessellations.y;
   auto point_count        = voxel_count * tessellation_count;
@@ -119,7 +128,7 @@ void odf_field::set_data(
   vertex_buffer_->cuda_register  (cudaGraphicsMapFlagsNone);
 
   color_buffer_->bind           ();
-  color_buffer_->allocate       (point_count * sizeof(float4));
+  color_buffer_->allocate       (point_count * sizeof(float3));
   color_buffer_->unbind         ();
   color_buffer_->cuda_register  (cudaGraphicsMapFlagsNone);
 
@@ -129,20 +138,20 @@ void odf_field::set_data(
   index_buffer_ ->cuda_register  (cudaGraphicsMapFlagsNone);
 
   auto cuda_vertex_buffer = vertex_buffer_->cuda_map<float3  >();
-  auto cuda_color_buffer  = color_buffer_ ->cuda_map<float4  >();
+  auto cuda_color_buffer  = color_buffer_ ->cuda_map<float3  >();
   auto cuda_index_buffer  = index_buffer_ ->cuda_map<unsigned>();
 
   sample_odfs(
     dimensions_       ,
-    coefficient_count ,
+    maximum_degree    ,
     coefficients      ,
     tessellations     ,
-    vector_spacing    ,
     vector_dimensions ,
     scale             ,
     cuda_vertex_buffer,
     cuda_color_buffer ,
     cuda_index_buffer ,
+    hierarchical      ,
     clustering        ,
     cluster_threshold ,
     status_callback   );
@@ -156,8 +165,7 @@ void odf_field::set_data(
   vertex_buffer_->cuda_unregister();
 }
 
-void odf_field::set_visible_layers(
-  const std::vector<bool>& visible_layers)
+void odf_field::set_visible_layers(const std::vector<bool>& visible_layers)
 {
   visible_layers_ = visible_layers;
 }
diff --git a/source/visualization/algorithms/polar_plot_field.cpp b/source/visualization/algorithms/polar_plot_field.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d795e7aacedc456cc8b4fe197a281ec2903d4a7c
--- /dev/null
+++ b/source/visualization/algorithms/polar_plot_field.cpp
@@ -0,0 +1,63 @@
+#include <pli_vis/visualization/algorithms/polar_plot_field.hpp>
+
+#include <pli_vis/visualization/primitives/camera.hpp>
+#include <shaders/polar_plot.vert.glsl>
+#include <shaders/polar_plot.frag.glsl>
+
+namespace pli
+{
+void polar_plot_field::initialize()
+{
+  shader_program_  .reset(new gl::program     );
+  vertex_array_    .reset(new gl::vertex_array);
+  vertex_buffer_   .reset(new gl::array_buffer);
+  direction_buffer_.reset(new gl::array_buffer);
+  
+  shader_program_  ->attach_shader         (gl::vertex_shader  (shaders::polar_plot_vert));
+  shader_program_  ->attach_shader         (gl::fragment_shader(shaders::polar_plot_frag));
+  shader_program_  ->link                  ();
+                   
+  shader_program_  ->bind                  ();
+  vertex_array_    ->bind                  ();
+                   
+  vertex_buffer_   ->bind                  ();
+  shader_program_  ->set_attribute_buffer  ("vertex"   , 3, GL_FLOAT);
+  shader_program_  ->enable_attribute_array("vertex");
+  vertex_buffer_   ->unbind                ();
+  
+  direction_buffer_->bind                  ();
+  shader_program_  ->set_attribute_buffer  ("direction", 3, GL_FLOAT);
+  shader_program_  ->enable_attribute_array("direction");
+  direction_buffer_->unbind                ();
+
+  vertex_array_    ->unbind                ();
+  shader_program_  ->unbind                ();
+}
+void polar_plot_field::render    (const camera* camera)
+{
+  shader_program_  ->bind                  ();
+  vertex_array_    ->bind                  ();
+  shader_program_  ->set_uniform           ("color_mode"    , color_mode_    );
+  shader_program_  ->set_uniform           ("color_k"       , color_k_       );
+  shader_program_  ->set_uniform           ("color_inverted", color_inverted_);
+  shader_program_  ->set_uniform           ("model"         , absolute_matrix                ());
+  shader_program_  ->set_uniform           ("view"          , camera->inverse_absolute_matrix());
+  shader_program_  ->set_uniform           ("projection"    , camera->projection_matrix      ());
+  glDrawArrays                             (GL_TRIANGLES    , 0, GLsizei(draw_count_));
+  vertex_array_    ->unbind                ();
+  shader_program_  ->unbind                ();
+}
+
+void polar_plot_field::set_data(const std::vector<float3>& vertices, const std::vector<float3>& directions)
+{        
+  vertex_buffer_   ->bind                  ();
+  vertex_buffer_   ->set_data              (vertices  .size() * sizeof float3, vertices  .data());
+  vertex_buffer_   ->unbind                ();
+  
+  direction_buffer_->bind                  ();
+  direction_buffer_->set_data              (directions.size() * sizeof float3, directions.data());
+  direction_buffer_->unbind                ();
+
+  draw_count_ = vertices.size();
+}
+}
diff --git a/source/visualization/scalar_field.cpp b/source/visualization/algorithms/scalar_field.cpp
similarity index 79%
rename from source/visualization/scalar_field.cpp
rename to source/visualization/algorithms/scalar_field.cpp
index b0c3c35b89516fe28b0fbede2faaca31919dc41c..188c5c7cdded88ef364709eb727327a31cd44d79 100644
--- a/source/visualization/scalar_field.cpp
+++ b/source/visualization/algorithms/scalar_field.cpp
@@ -1,9 +1,9 @@
-#include /* implements */ <visualization/scalar_field.hpp>
+#include <pli_vis/visualization/algorithms/scalar_field.hpp>
 
-#include <math/camera.hpp>
-#include <sh/vector_ops.h>
-#include <shaders/scalar_field.vert.glsl>
-#include <shaders/scalar_field.frag.glsl>
+#include <pli_vis/cuda/utility/vector_ops.h>
+#include <pli_vis/visualization/primitives/camera.hpp>
+#include <shaders/simple_texture.vert.glsl>
+#include <shaders/simple_texture.frag.glsl>
 
 namespace pli
 {
@@ -15,8 +15,8 @@ void scalar_field::initialize()
   texcoord_buffer_.reset(new gl::array_buffer);
   texture_        .reset(new gl::texture_2d  );
 
-  shader_program_ ->attach_shader(gl::vertex_shader  (shaders::scalar_field_vert));
-  shader_program_ ->attach_shader(gl::fragment_shader(shaders::scalar_field_frag));
+  shader_program_ ->attach_shader(gl::vertex_shader  (shaders::simple_texture_vert));
+  shader_program_ ->attach_shader(gl::fragment_shader(shaders::simple_texture_frag));
   shader_program_ ->link();
   
   shader_program_ ->bind();
@@ -50,13 +50,15 @@ void scalar_field::render    (const camera* camera)
   shader_program_->bind  ();
   vertex_array_  ->bind  ();
   texture_       ->bind  ();
-
-  shader_program_->set_uniform("projection", camera->projection_matrix      ());
+  
+  shader_program_->set_uniform("model"     , absolute_matrix                ());
   shader_program_->set_uniform("view"      , camera->inverse_absolute_matrix());
+  shader_program_->set_uniform("projection", camera->projection_matrix      ());
 
   glEnable       (GL_POLYGON_OFFSET_FILL);
   glPolygonOffset(1.0, 10.0);
   glDrawArrays   (GL_TRIANGLES, 0, GLsizei(draw_count_));
+  glPolygonOffset(1.0, 0.0);
   glDisable      (GL_POLYGON_OFFSET_FILL);
 
   texture_       ->unbind();
@@ -67,18 +69,17 @@ void scalar_field::render    (const camera* camera)
 
 void scalar_field::set_data(
   const uint3&  dimensions  ,
-  const float*  scalars     ,
-  const float3& spacing     )
+  const float*  scalars     )
 {
   draw_count_ = 6 * dimensions.z;
 
-  float3 size         = {spacing.x * dimensions.x, spacing.y * dimensions.y, spacing.z * dimensions.z};
-  float3 vertices [6] = {{0,0,0}, {size.x,0,0}, {size.x,-size.y,0}, {0,0,0}, {size.x,-size.y,0}, {0,-size.y,0}};
+  float3 size         = {dimensions.x, dimensions.y, dimensions.z};
+  float3 vertices [6] = {{0,0,0}, {size.x,0,0}, {size.x,size.y,0}, {0,0,0}, {size.x,size.y,0}, {0,size.y,0}};
   float2 texcoords[6] = {{0,0}, {0,1}, {1,1}, {0,0}, {1,1}, {1,0}};
 
   // Adjust vertices to offset for the center of the voxels.
   for(auto i = 0; i < 6; i++)
-    vertices[i] = vertices[i] + float3{-spacing.x / 2, spacing.y / 2, 0};
+    vertices[i] = vertices[i] + float3{-0.5, -0.5, 0};
 
   vertex_buffer_  ->bind    ();
   vertex_buffer_  ->set_data(draw_count_ * sizeof(float3), vertices);
diff --git a/source/visualization/algorithms/streamline_renderer.cpp b/source/visualization/algorithms/streamline_renderer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bd754a41dae445edbc9026ba6dd0c2053a5b9c03
--- /dev/null
+++ b/source/visualization/algorithms/streamline_renderer.cpp
@@ -0,0 +1,70 @@
+#include <pli_vis/visualization/algorithms/streamline_renderer.hpp>
+
+#include <pli_vis/visualization/primitives/camera.hpp>
+#include <shaders/streamline_renderer.vert.glsl>
+#include <shaders/streamline_renderer.frag.glsl>
+
+namespace pli
+{
+void streamline_renderer::initialize()
+{
+  program_         .reset(new gl::program     );
+  vertex_array_    .reset(new gl::vertex_array);
+  vertex_buffer_   .reset(new gl::array_buffer);
+  direction_buffer_.reset(new gl::array_buffer);
+
+  program_->attach_shader(gl::vertex_shader  (shaders::streamline_renderer_vert));
+  program_->attach_shader(gl::fragment_shader(shaders::streamline_renderer_frag));
+  program_->link();
+  
+  vertex_array_    ->bind  ();
+  program_         ->bind  ();
+  vertex_buffer_   ->bind  ();
+  program_         ->set_attribute_buffer  ("vertex", 3, GL_FLOAT);
+  program_         ->enable_attribute_array("vertex");
+  vertex_buffer_   ->unbind();
+  direction_buffer_->bind  ();
+  program_         ->set_attribute_buffer  ("direction" , 3, GL_FLOAT);
+  program_         ->enable_attribute_array("direction");
+  direction_buffer_->unbind();
+  program_         ->unbind();
+  vertex_array_    ->unbind();
+}
+void streamline_renderer::render    (const camera* camera)
+{
+  vertex_array_->bind  ();
+  program_     ->bind  ();
+  program_     ->set_uniform("color_mode"    , color_mode_);
+  program_     ->set_uniform("color_k"       , color_k_);
+  program_     ->set_uniform("color_inverted", color_inverted_);
+  program_     ->set_uniform("model"         , absolute_matrix                ());
+  program_     ->set_uniform("view"          , camera->inverse_absolute_matrix());
+  program_     ->set_uniform("projection"    , camera->projection_matrix      ());
+
+  glEnable    (GL_LINE_SMOOTH);
+  glHint      (GL_LINE_SMOOTH_HINT, GL_NICEST);
+  glEnable    (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glDrawArrays(GL_LINES, 0, GLsizei(draw_count_));
+  glDisable   (GL_BLEND);
+  glDisable   (GL_LINE_SMOOTH);
+  
+  program_     ->unbind();
+  vertex_array_->unbind();
+}
+  
+void streamline_renderer::set_data(
+  const std::vector<float3>& points    , 
+  const std::vector<float3>& directions)
+{
+  draw_count_ = points.size();
+  
+  vertex_buffer_   ->bind    ();
+  vertex_buffer_   ->set_data(draw_count_ * sizeof(float3), points    .data());
+  vertex_buffer_   ->unbind  ();
+  
+  direction_buffer_->bind    ();
+  direction_buffer_->set_data(draw_count_ * sizeof(float3), directions.data());
+  direction_buffer_->unbind  ();
+}
+}
diff --git a/source/visualization/algorithms/vector_field.cpp b/source/visualization/algorithms/vector_field.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ffa6f3c5cb847ea20f4e6b683b703b4e0652f2e5
--- /dev/null
+++ b/source/visualization/algorithms/vector_field.cpp
@@ -0,0 +1,86 @@
+#include <pli_vis/visualization/algorithms/vector_field.hpp>
+
+#include <pli_vis/visualization/primitives/camera.hpp>
+#include <shaders/view_dependent_vector_field.vert.glsl>
+#include <shaders/view_dependent_vector_field.geom.glsl>
+#include <shaders/view_dependent_vector_field.frag.glsl>
+
+namespace pli
+{
+void vector_field::initialize()
+{
+  shader_program_  .reset(new gl::program     );
+  vertex_array_    .reset(new gl::vertex_array);
+  direction_buffer_.reset(new gl::array_buffer);
+
+  shader_program_->attach_shader(gl::vertex_shader  (shaders::view_dependent_vector_field_vert));
+  shader_program_->attach_shader(gl::geometry_shader(shaders::view_dependent_vector_field_geom));
+  shader_program_->attach_shader(gl::fragment_shader(shaders::view_dependent_vector_field_frag));
+  shader_program_->link();
+  
+  shader_program_->bind();
+  vertex_array_  ->bind();
+
+  direction_buffer_->bind();
+  shader_program_  ->set_attribute_buffer  ("direction", 3, GL_FLOAT);
+  shader_program_  ->enable_attribute_array("direction");
+  direction_buffer_->unbind();
+
+  vertex_array_  ->unbind();
+  shader_program_->unbind();
+}
+void vector_field::render    (const camera* camera)
+{
+  shader_program_->bind  ();
+  vertex_array_  ->bind  ();
+  
+  shader_program_->set_uniform("color_mode"       , color_mode_);
+  shader_program_->set_uniform("color_k"          , color_k_);
+  shader_program_->set_uniform("color_inverted"   , color_inverted_);
+  shader_program_->set_uniform("model"            , absolute_matrix                ());
+  shader_program_->set_uniform("view"             , camera->inverse_absolute_matrix());
+  shader_program_->set_uniform("projection"       , camera->projection_matrix      ());
+  shader_program_->set_uniform("dimensions"       , dimensions_);
+  shader_program_->set_uniform("vectors_per_point", vectors_per_point_);
+  shader_program_->set_uniform("scale"            , scale_);
+  shader_program_->set_uniform("view_dependent"   , view_dependent_transparency_ );
+  shader_program_->set_uniform("rate_of_decay"    , view_dependent_rate_of_decay_);
+  
+  glEnable    (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glDrawArrays(GL_POINTS, 0, GLsizei(draw_count_));
+  glDisable   (GL_BLEND);
+
+  vertex_array_  ->unbind();
+  shader_program_->unbind();
+}
+
+void vector_field::set_data(
+  const uint3&   dimensions       ,
+  const unsigned vectors_per_point,
+  const float3*  unit_vectors     ,
+  std::function<void(const std::string&)> status_callback)
+{
+  dimensions_        = glm::uvec3(dimensions.x, dimensions.y, dimensions.z);
+  vectors_per_point_ = vectors_per_point;
+  draw_count_        = dimensions.x * dimensions.y * dimensions.z * vectors_per_point;
+
+  direction_buffer_->bind    ();
+  direction_buffer_->set_data(draw_count_ * sizeof(float3), unit_vectors);
+  direction_buffer_->unbind  ();
+}
+
+void vector_field::set_scale                       (float    scale            )
+{
+  scale_ = scale;
+}
+void vector_field::set_view_dependent_transparency (bool     enabled          )
+{
+  view_dependent_transparency_  = enabled;
+}
+void vector_field::set_view_dependent_rate_of_decay(float    value            )
+{
+  view_dependent_rate_of_decay_ = value  ;
+}
+}
+
diff --git a/source/visualization/algorithms/volume_renderer.cpp b/source/visualization/algorithms/volume_renderer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..633785999bd07748ca417236fd83c736ba861739
--- /dev/null
+++ b/source/visualization/algorithms/volume_renderer.cpp
@@ -0,0 +1,218 @@
+#include <pli_vis/visualization/algorithms/volume_renderer.hpp>
+
+#include <pli_vis/visualization/primitives/camera.hpp>
+#include <shaders/volume_renderer_prepass.vert.glsl>
+#include <shaders/volume_renderer_prepass.frag.glsl>
+#include <shaders/volume_renderer.vert.glsl>
+#include <shaders/volume_renderer.frag.glsl>
+
+namespace pli
+{
+void volume_renderer::initialize()
+{
+  GLint default_framebuffer_id;
+  glGetIntegerv(GL_FRAMEBUFFER_BINDING, &default_framebuffer_id);
+  gl::framebuffer default_framebuffer(default_framebuffer_id);
+
+  glm::ivec4 viewport;
+  glGetIntegerv(GL_VIEWPORT, reinterpret_cast<GLint*>(&viewport));
+
+  prepass_shader_program_   .reset(new gl::program     );
+  shader_program_           .reset(new gl::program     );
+  prepass_vertex_array_     .reset(new gl::vertex_array);
+  vertex_array_             .reset(new gl::vertex_array);
+  vertex_buffer_            .reset(new gl::array_buffer);
+  color_buffer_             .reset(new gl::array_buffer);
+  index_buffer_             .reset(new gl::index_buffer);
+  transfer_function_texture_.reset(new gl::texture_1d  );
+  volume_texture_           .reset(new gl::texture_3d  );
+  framebuffer_              .reset(new gl::framebuffer );
+  exit_points_color_texture_.reset(new gl::texture_2d  );
+  exit_points_depth_texture_.reset(new gl::texture_2d  );
+
+  prepass_shader_program_->attach_shader(gl::vertex_shader  (shaders::volume_renderer_prepass_vert));
+  prepass_shader_program_->attach_shader(gl::fragment_shader(shaders::volume_renderer_prepass_frag));
+  prepass_shader_program_->link();
+
+  shader_program_        ->attach_shader(gl::vertex_shader  (shaders::volume_renderer_vert));
+  shader_program_        ->attach_shader(gl::fragment_shader(shaders::volume_renderer_frag));
+  shader_program_        ->link();
+  
+  // Initialize prepass shader program and vertex array.
+  prepass_shader_program_->bind();
+  prepass_vertex_array_  ->bind();
+                         
+  vertex_buffer_         ->bind();
+  prepass_shader_program_->set_attribute_buffer  ("vertex", 3, GL_FLOAT);
+  prepass_shader_program_->enable_attribute_array("vertex");
+  vertex_buffer_         ->unbind();
+                
+  prepass_vertex_array_  ->unbind();
+  prepass_shader_program_->unbind();
+
+  // Initialize main pass shader program and vertex array.
+  shader_program_        ->bind();
+  vertex_array_          ->bind();
+                         
+  vertex_buffer_         ->bind();
+  shader_program_        ->set_attribute_buffer  ("vertex", 3, GL_FLOAT);
+  shader_program_        ->enable_attribute_array("vertex");
+  vertex_buffer_         ->unbind();
+             
+  vertex_array_          ->unbind();
+  shader_program_        ->unbind();
+  
+  // Set buffers for a unit cube.
+  std::vector<float>    vertices = {
+    0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 
+    0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0,
+    1.0, 1.0, 0.0, 1.0, 1.0, 1.0
+  };
+  std::vector<unsigned> indices  = {
+    1, 5, 7, 7, 3, 1, 0, 2, 6, 6, 4, 0,
+    0, 1, 3, 3, 2, 0, 7, 5, 4, 4, 6, 7,
+    2, 3, 7, 7, 6, 2, 1, 0, 4, 4, 5, 1
+  };
+
+  vertex_buffer_->bind    ();
+  vertex_buffer_->set_data(sizeof(float)    * vertices.size(), vertices.data());
+  vertex_buffer_->unbind  ();
+
+  index_buffer_ ->bind    ();
+  index_buffer_ ->set_data(sizeof(unsigned) * indices .size(), indices .data());
+  index_buffer_ ->unbind  ();
+
+  draw_count_ = indices.size();
+
+  // Set textures.
+  transfer_function_texture_->bind      ();
+  transfer_function_texture_->wrap_s    (GL_REPEAT );
+  transfer_function_texture_->min_filter(GL_NEAREST);
+  transfer_function_texture_->mag_filter(GL_NEAREST);
+  transfer_function_texture_->set_image (GL_RGBA32F, 256, GL_RGBA, GL_FLOAT);
+  transfer_function_texture_->unbind    ();
+
+  exit_points_color_texture_->bind      ();
+  exit_points_color_texture_->wrap_s    (GL_REPEAT);
+  exit_points_color_texture_->wrap_t    (GL_REPEAT);
+  exit_points_color_texture_->min_filter(GL_NEAREST);
+  exit_points_color_texture_->mag_filter(GL_NEAREST);
+  exit_points_color_texture_->set_image (GL_RGBA32F, viewport[2], viewport[3], GL_RGBA, GL_FLOAT);
+  exit_points_color_texture_->unbind    ();
+
+  exit_points_depth_texture_->bind      ();
+  exit_points_depth_texture_->wrap_s    (GL_REPEAT);
+  exit_points_depth_texture_->wrap_t    (GL_REPEAT);
+  exit_points_depth_texture_->min_filter(GL_NEAREST);
+  exit_points_depth_texture_->mag_filter(GL_NEAREST);
+  exit_points_depth_texture_->set_image (GL_DEPTH_COMPONENT24, viewport[2], viewport[3], GL_DEPTH_COMPONENT, GL_FLOAT);
+  exit_points_depth_texture_->unbind    ();
+
+  volume_texture_           ->bind      ();
+  volume_texture_           ->wrap_s    (GL_REPEAT);
+  volume_texture_           ->wrap_t    (GL_REPEAT);
+  volume_texture_           ->wrap_r    (GL_REPEAT);
+  volume_texture_           ->min_filter(GL_LINEAR);
+  volume_texture_           ->mag_filter(GL_LINEAR);
+  volume_texture_           ->set_image (GL_R32F, 1, 1, 1, GL_RED, GL_FLOAT);
+  volume_texture_           ->unbind    ();
+  
+  // Setup the framebuffer.
+  framebuffer_->bind           ();
+  framebuffer_->set_texture    (GL_COLOR_ATTACHMENT0, *exit_points_color_texture_.get());
+  framebuffer_->set_texture    (GL_DEPTH_ATTACHMENT , *exit_points_depth_texture_.get());
+  assert(framebuffer_->is_valid() && framebuffer_->is_complete());
+  default_framebuffer.bind();
+}
+void volume_renderer::render    (const camera* camera)
+{
+  GLint default_framebuffer_id;
+  glGetIntegerv(GL_FRAMEBUFFER_BINDING, &default_framebuffer_id);
+  gl::framebuffer default_framebuffer(default_framebuffer_id);
+
+  glm::ivec4 viewport;
+  glGetIntegerv(GL_VIEWPORT, reinterpret_cast<GLint*>(&viewport));
+
+  // Apply prepass.
+  exit_points_color_texture_->bind     ();
+  exit_points_color_texture_->set_image(GL_RGBA32F, viewport[2], viewport[3], GL_RGBA, GL_FLOAT);
+  exit_points_color_texture_->unbind   ();
+  exit_points_depth_texture_->bind     ();
+  exit_points_depth_texture_->set_image(GL_DEPTH_COMPONENT24, viewport[2], viewport[3], GL_DEPTH_COMPONENT, GL_FLOAT);
+  exit_points_depth_texture_->unbind   ();
+  
+  framebuffer_              ->bind();
+  prepass_shader_program_   ->bind();
+  prepass_vertex_array_     ->bind();
+  index_buffer_             ->bind();
+
+  prepass_shader_program_   ->set_uniform("projection", camera->projection_matrix      ());
+  prepass_shader_program_   ->set_uniform("view"      , camera->inverse_absolute_matrix());
+
+  glViewport    (viewport[0], viewport[1], viewport[2], viewport[3]);
+  glClear       (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  glEnable      (GL_CULL_FACE);
+  glCullFace    (GL_FRONT);
+  glDrawElements(GL_TRIANGLES, draw_count_, GL_UNSIGNED_INT, nullptr);
+  glDisable     (GL_CULL_FACE);
+
+  prepass_shader_program_   ->unbind();
+  prepass_vertex_array_     ->unbind();
+  index_buffer_             ->unbind();
+  default_framebuffer       .bind();
+  
+  // Apply main pass.
+  shader_program_           ->bind();
+  vertex_array_             ->bind();
+  index_buffer_             ->bind();
+       
+  gl::texture_1d::set_active(GL_TEXTURE0);
+  transfer_function_texture_->bind();
+  gl::texture_2d::set_active(GL_TEXTURE1);
+  exit_points_color_texture_->bind();
+  gl::texture_3d::set_active(GL_TEXTURE2);
+  volume_texture_           ->bind();
+                       
+  shader_program_           ->set_uniform("projection"       , camera->projection_matrix      ());
+  shader_program_           ->set_uniform("view"             , camera->inverse_absolute_matrix());
+  shader_program_           ->set_uniform("screen_size"      , glm::uvec2(viewport[2], viewport[3]));
+  shader_program_           ->set_uniform("transfer_function", 0);
+  shader_program_           ->set_uniform("exit_points"      , 1);
+  shader_program_           ->set_uniform("volume"           , 2);
+  
+  glEnable      (GL_CULL_FACE);
+  glCullFace    (GL_BACK);
+  glDrawElements(GL_TRIANGLES, draw_count_, GL_UNSIGNED_INT, nullptr);
+  glDisable     (GL_CULL_FACE);
+  
+  gl::texture_3d::set_active(GL_TEXTURE2);
+  volume_texture_           ->unbind();
+  gl::texture_2d::set_active(GL_TEXTURE1);
+  exit_points_color_texture_->unbind();
+  gl::texture_1d::set_active(GL_TEXTURE0);
+  transfer_function_texture_->unbind();
+ 
+  shader_program_           ->unbind();
+  vertex_array_             ->unbind();
+  index_buffer_             ->unbind();
+}
+
+void volume_renderer::set_data             (const uint3& dimensions, const float* data)
+{
+  volume_texture_->bind     ();
+  volume_texture_->set_image(GL_R32F, dimensions.x, dimensions.y, dimensions.z, GL_RED, GL_FLOAT, data);
+  volume_texture_->unbind   ();
+}
+void volume_renderer::set_transfer_function(const std::vector<float4>& transfer_function)
+{                                    
+  transfer_function_texture_->bind     ();
+  transfer_function_texture_->set_image(GL_RGBA32F, 256, GL_RGBA, GL_FLOAT, transfer_function.data());
+  transfer_function_texture_->unbind   ();
+}
+void volume_renderer::set_step_size        (float step_size)
+{
+  shader_program_->bind       ();
+  shader_program_->set_uniform("step_size", step_size);
+  shader_program_->unbind     ();
+}
+}
diff --git a/source/visualization/algorithms/zernike_field.cpp b/source/visualization/algorithms/zernike_field.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a2d8139575ef6f8163523aa39ee6f1720c2d39b
--- /dev/null
+++ b/source/visualization/algorithms/zernike_field.cpp
@@ -0,0 +1,134 @@
+#include <pli_vis/visualization/algorithms/zernike_field.hpp>
+
+#include <pli_vis/visualization/primitives/camera.hpp>
+#include <shaders/simple_color_texture.vert.glsl>
+#include <shaders/simple_color_texture.frag.glsl>
+#include <shaders/zernike.vert.glsl>
+#include <shaders/zernike.frag.glsl>
+
+namespace pli
+{
+void zernike_field::initialize()
+{
+  std::vector<float>    vertices  = {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f};
+  std::vector<float>    texcoords = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
+  std::vector<unsigned> indices   = {0u, 1u, 2u, 0u, 2u, 3u};
+
+  prepass_program_      = std::make_unique<gl::program>              ();
+  prepass_vertex_array_ = std::make_unique<gl::vertex_array>         ();
+  main_program_         = std::make_unique<gl::program>              ();
+  main_vertex_array_    = std::make_unique<gl::vertex_array>         ();
+
+  vertex_buffer_        = std::make_unique<gl::array_buffer>         ();
+  texcoord_buffer_      = std::make_unique<gl::array_buffer>         ();
+  index_buffer_         = std::make_unique<gl::index_buffer>         ();
+  coefficient_buffer_   = std::make_unique<gl::shader_storage_buffer>();
+
+  render_target_        = std::make_unique<render_target>            ();
+  
+  prepass_program_     ->attach_shader         (gl::vertex_shader  (shaders::zernike_vert));
+  prepass_program_     ->attach_shader         (gl::fragment_shader(shaders::zernike_frag));
+  prepass_program_     ->link                  ();
+  prepass_vertex_array_->bind                  ();
+  prepass_program_     ->bind                  ();
+  vertex_buffer_       ->bind                  ();
+  vertex_buffer_       ->set_data              (vertices.size() * sizeof(float), vertices.data());
+  prepass_program_     ->set_attribute_buffer  ("position", 3, GL_FLOAT);
+  prepass_program_     ->enable_attribute_array("position");
+  vertex_buffer_       ->unbind                ();
+  index_buffer_        ->bind                  ();
+  index_buffer_        ->set_data              (indices.size() * sizeof(unsigned), indices.data());
+  index_buffer_        ->unbind                ();
+  prepass_vertex_array_->set_element_buffer    (*index_buffer_.get());
+  prepass_program_     ->unbind                ();
+  prepass_vertex_array_->unbind                ();
+  
+  main_program_        ->attach_shader         (gl::vertex_shader  (shaders::simple_color_texture_vert));
+  main_program_        ->attach_shader         (gl::fragment_shader(shaders::simple_color_texture_frag));
+  main_program_        ->link                  ();
+  main_vertex_array_   ->bind                  ();
+  main_program_        ->bind                  ();
+  vertex_buffer_       ->bind                  ();
+  vertex_buffer_       ->set_data              (vertices.size() * sizeof(float), vertices.data());
+  main_program_        ->set_attribute_buffer  ("position", 3, GL_FLOAT);
+  main_program_        ->enable_attribute_array("position");
+  vertex_buffer_       ->unbind                ();
+  texcoord_buffer_     ->bind                  ();
+  texcoord_buffer_     ->set_data              (texcoords.size() * sizeof(float), texcoords.data());
+  main_program_        ->set_attribute_buffer  ("texcoords", 2, GL_FLOAT);
+  main_program_        ->enable_attribute_array("texcoords");
+  texcoord_buffer_     ->unbind                ();
+  index_buffer_        ->bind                  ();
+  index_buffer_        ->set_data              (indices.size() * sizeof(unsigned), indices.data());
+  index_buffer_        ->unbind                ();
+  main_vertex_array_   ->set_element_buffer    (*index_buffer_.get());
+  main_program_        ->unbind                ();
+  main_vertex_array_   ->unbind                ();
+
+  draw_count_ = indices.size();
+} 
+void zernike_field::render    (const camera* camera)
+{
+  if(needs_update_)
+  {
+    render_target_       ->resize     ({8192u, 8192u});
+    render_target_       ->bind       ();
+    prepass_program_     ->bind       ();
+    prepass_vertex_array_->bind       ();
+    coefficient_buffer_  ->bind_base  (0);
+
+    prepass_program_     ->set_uniform("dimensions"            , dimensions_            );
+    prepass_program_     ->set_uniform("spacing"               , spacing_               );
+    prepass_program_     ->set_uniform("coefficients_per_voxel", coefficients_per_voxel_);
+    prepass_program_     ->set_uniform("color_mode"            , color_mode_            );
+    prepass_program_     ->set_uniform("color_k"               , color_k_               );
+    prepass_program_     ->set_uniform("color_inverted"        , color_inverted_        );
+    glViewport             (0, 0, 8192, 8192);
+    glClearColor           (0.0, 0.0, 0.0, 0.0);
+    glClear                (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    glDrawElementsInstanced(GL_TRIANGLES, draw_count_, GL_UNSIGNED_INT, nullptr, primitive_count_);
+  
+    prepass_vertex_array_->unbind     ();
+    prepass_program_     ->unbind     ();
+    render_target_       ->unbind     ();
+
+    render_target_->color_texture()->bind            ();
+    render_target_->color_texture()->generate_mipmaps();
+    render_target_->color_texture()->unbind          ();
+
+    needs_update_ = false;
+  }
+  
+  gl::texture_2d::set_active(GL_TEXTURE0);
+  
+  render_target_    ->color_texture()->bind();
+  main_program_     ->bind();
+  main_vertex_array_->bind();
+  
+  main_program_     ->set_uniform("texture_unit", 0);
+  main_program_     ->set_uniform("model"       , absolute_matrix                 ());
+  main_program_     ->set_uniform("view"        , camera->inverse_absolute_matrix ());
+  main_program_     ->set_uniform("projection"  , camera->projection_matrix       ());
+  main_program_     ->set_uniform("size"        , glm::vec2(dimensions_ * spacing_ ));
+  glDrawElements(GL_TRIANGLES, draw_count_, GL_UNSIGNED_INT, nullptr);
+  
+  main_vertex_array_->unbind();
+  main_program_     ->unbind();
+  render_target_    ->color_texture()->unbind();
+}
+
+void zernike_field::set_data  (const uint2& dimensions, const uint2& spacing, const unsigned coefficients_per_voxel, const std::vector<float>& coefficients)
+{
+  dimensions_             = {dimensions.x , dimensions.y};
+  spacing_                = {spacing   .x , spacing   .y};
+  coefficients_per_voxel_ = coefficients_per_voxel;
+  primitive_count_        = dimensions_.x * dimensions_.y;
+
+  coefficient_buffer_->bind     () ;
+  coefficient_buffer_->set_data (coefficients.size() * sizeof(float), coefficients.data());
+  coefficient_buffer_->bind_base(0);
+  coefficient_buffer_->unbind   () ;
+  
+  needs_update_ = true;
+}
+}
diff --git a/source/visualization/interactors/first_person_interactor.cpp b/source/visualization/interactors/first_person_interactor.cpp
index fd7576809a389a65bdcd674b25dbe40d968a91ea..e77677726c49b02c97536d6fd41dd03484a4b03d 100644
--- a/source/visualization/interactors/first_person_interactor.cpp
+++ b/source/visualization/interactors/first_person_interactor.cpp
@@ -1,12 +1,12 @@
-#include /* implements */ <visualization/interactors/first_person_interactor.hpp>
+#include <pli_vis/visualization/interactors/first_person_interactor.hpp>
 
 #include <QKeyEvent>
 
-#include <math/transform.hpp>
+#include <pli_vis/visualization/primitives/camera.hpp>
 
 namespace pli
 {
-first_person_interactor::first_person_interactor(transform* transform) : transform_(transform)
+first_person_interactor::first_person_interactor(camera* camera) : interactor(camera)
 {
 
 }
@@ -14,17 +14,27 @@ first_person_interactor::first_person_interactor(transform* transform) : transfo
 void first_person_interactor::update_transform()
 {
   if (key_map_[Qt::Key_W])
-    transform_->translate(- transform_->forward() * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+  {
+    if (camera_->orthographic())
+      camera_->set_scale(camera_->scale() - glm::vec3(1.0) * ((1.0F / (2.0F * camera_->orthographic_size())) * move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+    else
+      camera_->translate(-camera_->forward() * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+  }
   if (key_map_[Qt::Key_A])
-    transform_->translate(- transform_->right  () * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+    camera_->translate(- camera_->right  () * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
   if (key_map_[Qt::Key_S])
-    transform_->translate(  transform_->forward() * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+  {
+    if(camera_->orthographic())
+      camera_->set_scale(camera_->scale() + glm::vec3(1.0) * ((1.0F / (2.0F * camera_->orthographic_size())) * move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+    else
+      camera_->translate(camera_->forward() * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+  }
   if (key_map_[Qt::Key_D])
-    transform_->translate(  transform_->right  () * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+    camera_->translate(  camera_->right  () * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
   if (key_map_[Qt::Key_Z])
-    transform_->translate(- transform_->up     () * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+    camera_->translate(- camera_->up     () * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
   if (key_map_[Qt::Key_X])
-    transform_->translate(  transform_->up     () * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
+    camera_->translate(  camera_->up     () * (move_speed_ * (key_map_[Qt::Key_Shift] ? 2 : 1)));
 }
 
 void first_person_interactor::key_press_handler  (QKeyEvent*   event)
@@ -95,8 +105,8 @@ void first_person_interactor::mouse_move_handler (QMouseEvent* event)
   auto dy = event->y() - last_mouse_position_.y();
   if (event->buttons() & Qt::LeftButton)
   {
-    transform_->rotate(angleAxis(radians(-look_speed_ * dx), vec3f(0.0, 0.0, 1.0)));
-    transform_->rotate(angleAxis(radians(-look_speed_ * dy), transform_->right()));
+    camera_->rotate(glm::angleAxis(glm::radians( look_speed_ * dx), glm::vec3(0.0, 0.0, 1.0)));
+    camera_->rotate(glm::angleAxis(glm::radians(-look_speed_ * dy), camera_->right()));
   }
 
   last_mouse_position_ = event->pos();
diff --git a/source/visualization/interactors/orbit_interactor.cpp b/source/visualization/interactors/orbit_interactor.cpp
index 84382acb6c3b407e6fb75b94b3069cf954da6c3a..59987f36c3680c96384773a7a0b9b9cfaee564b8 100644
--- a/source/visualization/interactors/orbit_interactor.cpp
+++ b/source/visualization/interactors/orbit_interactor.cpp
@@ -1,29 +1,16 @@
-#include /* implements */ <visualization/interactors/orbit_interactor.hpp>
+#include <pli_vis/visualization/interactors/orbit_interactor.hpp>
 
 #include <QKeyEvent>
 
-#include <math/transform.hpp>
+#include <pli_vis/visualization/primitives/camera.hpp>
 
 namespace pli
 {
-orbit_interactor::orbit_interactor(transform* transform) : transform_(transform)
+orbit_interactor::orbit_interactor(camera* camera) : interactor(camera)
 {
   
 }
 
-void orbit_interactor::update_transform   ()
-{
-
-}
-
-void orbit_interactor::key_press_handler  (QKeyEvent*   event)
-{
-
-}
-void orbit_interactor::key_release_handler(QKeyEvent*   event)
-{
-
-}
 void orbit_interactor::mouse_press_handler(QMouseEvent* event)
 {
   last_mouse_position_ = event->pos();
@@ -35,15 +22,18 @@ void orbit_interactor::mouse_move_handler (QMouseEvent* event)
 
   if (event->buttons() & Qt::LeftButton)
   {
-    auto translation = transform_->translation();
-    transform_->translate(-translation);
-    transform_->rotate   (angleAxis(radians(-look_speed_ * dx), vec3f(0.0, 0.0, 1.0)));
-    transform_->rotate   (angleAxis(radians(-look_speed_ * dy), transform_->right()));
-    transform_->translate(length(translation) * transform_->forward());
+    auto translation = camera_->translation();
+    camera_->translate(-translation);
+    camera_->rotate   (glm::angleAxis(glm::radians( look_speed_ * dx), glm::vec3(0.0, 0.0, 1.0)));
+    camera_->rotate   (glm::angleAxis(glm::radians(-look_speed_ * dy), camera_->right()));
+    camera_->translate(glm::length(translation) * camera_->forward());
   }
   if (event->buttons() & Qt::RightButton)
   {
-    transform_->translate(move_speed_ * dy * transform_->forward());
+    if(camera_->orthographic())
+      camera_->set_scale(camera_->scale() + glm::vec3(1.0) * ((1.0F / (2.0F * camera_->orthographic_size())) * move_speed_ * dy));
+    else
+      camera_->translate(move_speed_ * dy * camera_->forward());
   }
 
   last_mouse_position_ = event->pos();
diff --git a/source/visualization/interactors/simple_interactor.cpp b/source/visualization/interactors/simple_interactor.cpp
index b391295a0e9590e6bfe630b28f046a5dfc8bced6..14cbb39728860ff525d3612bc1f01205901bec34 100644
--- a/source/visualization/interactors/simple_interactor.cpp
+++ b/source/visualization/interactors/simple_interactor.cpp
@@ -1,29 +1,16 @@
-#include /* implements */ <visualization/interactors/simple_interactor.hpp>
+#include <pli_vis/visualization/interactors/simple_interactor.hpp>
 
 #include <QKeyEvent>
 
-#include <math/transform.hpp>
+#include <pli_vis/visualization/primitives/camera.hpp>
 
 namespace pli
 {
-simple_interactor::simple_interactor(transform* transform) : transform_(transform)
+simple_interactor::simple_interactor(camera* camera) : interactor(camera)
 {
   
 }
 
-void simple_interactor::update_transform   ()
-{
-
-}
-
-void simple_interactor::key_press_handler  (QKeyEvent*   event)
-{
-
-}
-void simple_interactor::key_release_handler(QKeyEvent*   event)
-{
-
-}
 void simple_interactor::mouse_press_handler(QMouseEvent* event)
 {
   last_mouse_position_ = event->pos();
@@ -35,16 +22,19 @@ void simple_interactor::mouse_move_handler (QMouseEvent* event)
 
   if (event->buttons() & Qt::LeftButton)
   {
-    transform_->rotate(angleAxis(radians(-look_speed_ * dx), vec3f(0.0, 0.0, 1.0)));
-    transform_->rotate(angleAxis(radians(-look_speed_ * dy), transform_->right()));
+    camera_->rotate(glm::angleAxis(glm::radians( look_speed_ * dx), glm::vec3(0.0, 0.0, 1.0)));
+    camera_->rotate(glm::angleAxis(glm::radians(-look_speed_ * dy), camera_->right()));
   }
   if (event->buttons() & Qt::MiddleButton)
   {
-    transform_->translate(move_speed_ * (float(dx) * transform_->right() - float(dy) * transform_->up()));
+    camera_->translate(move_speed_ * (float(dx) * camera_->right() - float(dy) * camera_->up()));
   }
   if (event->buttons() & Qt::RightButton)
   {
-    transform_->translate(move_speed_ * dy * transform_->forward());
+    if(camera_->orthographic())
+      camera_->set_scale(camera_->scale() + glm::vec3(1.0) * ((1.0F / (2.0F * camera_->orthographic_size())) * move_speed_ * dy));
+    else
+      camera_->translate(move_speed_ * dy * camera_->forward());
   }
 
   last_mouse_position_ = event->pos();
diff --git a/source/math/camera.cpp b/source/visualization/primitives/camera.cpp
similarity index 61%
rename from source/math/camera.cpp
rename to source/visualization/primitives/camera.cpp
index b73b292159c643654701e1534ba84731f4bae649..5470f9ae62b42943195fd6af3509910844ad3c00 100644
--- a/source/math/camera.cpp
+++ b/source/visualization/primitives/camera.cpp
@@ -1,4 +1,6 @@
-#include /* implements */ <math/camera.hpp>
+#include <pli_vis/visualization/primitives/camera.hpp>
+
+#include <glm/gtx/transform.hpp>
 
 namespace pli
 {
@@ -44,19 +46,19 @@ void camera::update_projection_matrix()
   {
     auto ortho_half_height = orthographic_size_ * 2.0F;
     auto ortho_half_width  = ortho_half_height * aspect_ratio_;
-    projection_matrix_ = ortho(-ortho_half_width , 
-                                ortho_half_width , 
-                               -ortho_half_height, 
-                                ortho_half_height, 
-                                near_clip_plane_ , 
-                                far_clip_plane_  );
+    projection_matrix_ = glm::ortho(-ortho_half_width , 
+                                     ortho_half_width , 
+                                    -ortho_half_height, 
+                                     ortho_half_height, 
+                                     near_clip_plane_ , 
+                                     far_clip_plane_  );
   }
   else
   {
-    projection_matrix_ = perspective(vertical_fov_   , 
-                                     aspect_ratio_   , 
-                                     near_clip_plane_, 
-                                     far_clip_plane_ );
+    projection_matrix_ = glm::perspective(vertical_fov_   , 
+                                          aspect_ratio_   , 
+                                          near_clip_plane_, 
+                                          far_clip_plane_ );
   }
 }
 }
diff --git a/source/visualization/primitives/directional_light.cpp b/source/visualization/primitives/directional_light.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d2594c64018a4fa28e90376d162e80ce347cd7d
--- /dev/null
+++ b/source/visualization/primitives/directional_light.cpp
@@ -0,0 +1,6 @@
+#include <pli_vis/visualization/primitives/directional_light.hpp>
+
+namespace pli
+{
+
+}
diff --git a/source/visualization/primitives/light.cpp b/source/visualization/primitives/light.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7ff8507a7e58214708bc4d22491d2259b0335ecb
--- /dev/null
+++ b/source/visualization/primitives/light.cpp
@@ -0,0 +1,22 @@
+#include <pli_vis/visualization/primitives/light.hpp>
+
+namespace pli
+{
+const float&     light::intensity() const
+{
+  return intensity_;
+}
+const glm::vec3& light::color    () const
+{
+  return color_;
+}
+
+void light::set_intensity(float            intensity)
+{
+  intensity_ = intensity;
+}
+void light::set_color    (const glm::vec3& color    )
+{
+  color_ = color;
+}
+}
diff --git a/source/visualization/primitives/point_light.cpp b/source/visualization/primitives/point_light.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d058bebc607ca445608a8414c69041c52df8386
--- /dev/null
+++ b/source/visualization/primitives/point_light.cpp
@@ -0,0 +1,14 @@
+#include <pli_vis/visualization/primitives/point_light.hpp>
+
+namespace pli
+{
+const float& point_light::range() const
+{
+  return range_;
+}
+
+void point_light::set_range(float range)
+{
+  range_ = range;
+}
+}
diff --git a/source/visualization/primitives/spot_light.cpp b/source/visualization/primitives/spot_light.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a4a86efb0a78c05966b7a4dc63454918b1ca3f8
--- /dev/null
+++ b/source/visualization/primitives/spot_light.cpp
@@ -0,0 +1,22 @@
+#include <pli_vis/visualization/primitives/spot_light.hpp>
+
+namespace pli
+{
+const float& spot_light::range     () const
+{
+  return range_;
+}
+const float& spot_light::spot_angle() const
+{
+  return spot_angle_;
+}
+
+void spot_light::set_range     (float range     )
+{
+  range_ = range;
+}
+void spot_light::set_spot_angle(float spot_angle)
+{
+  spot_angle_ = spot_angle;
+}
+}
diff --git a/source/math/transform.cpp b/source/visualization/primitives/transform.cpp
similarity index 55%
rename from source/math/transform.cpp
rename to source/visualization/primitives/transform.cpp
index ff8878bb853f748073e109583d32a42f4ed15ab3..9c9ca5c4b7a96120310629df3b8c8836db974a6b 100644
--- a/source/math/transform.cpp
+++ b/source/visualization/primitives/transform.cpp
@@ -1,56 +1,59 @@
-#include /* implements */ <math/transform.hpp>
+#include <pli_vis/visualization/primitives/transform.hpp>
 
 #include <algorithm>
 
+#include <glm/gtx/transform.hpp>
+#include <glm/gtx/quaternion.hpp>
+
 namespace pli
 {
-transform::transform(const vec3f& translation, const quatf& rotation, const vec3f& scale)
+transform::transform(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale)
   : translation_(translation), rotation_(rotation), scale_(scale)
 {
   update_matrix();
 }
-transform::transform(const vec3f& translation, const vec3f& rotation_euler, const vec3f& scale)
-  : transform(translation, quatf(radians(rotation_euler)), scale)
+transform::transform(const glm::vec3& translation, const glm::vec3& rotation_euler, const glm::vec3& scale)
+  : transform(translation, glm::quat(glm::radians(rotation_euler)), scale)
 {
 
 }
 
-transform& transform::set_translation       (const vec3f& translation)
+transform& transform::set_translation       (const glm::vec3& translation)
 {
   translation_ = translation;
   update_matrix();
   return *this;
 }
-transform& transform::set_rotation          (const quatf& rotation)
+transform& transform::set_rotation          (const glm::quat& rotation)
 {
   rotation_ = rotation;
   update_matrix();
   return *this;
 }
-transform& transform::set_rotation_euler    (const vec3f& rotation_euler)
+transform& transform::set_rotation_euler    (const glm::vec3& rotation_euler)
 {
-  rotation_ = quatf(radians(rotation_euler));
+  rotation_ = glm::quat(radians(rotation_euler));
   update_matrix();
   return *this;
 }
-transform& transform::set_scale             (const vec3f& scale)
+transform& transform::set_scale             (const glm::vec3& scale)
 {
   scale_ = scale;
   update_matrix();
   return *this;
 }
 
-transform& transform::translate             (const vec3f& amount)
+transform& transform::translate             (const glm::vec3& amount)
 {
   return set_translation(amount + translation_);
 }
-transform& transform::rotate                (const quatf& amount)
+transform& transform::rotate                (const glm::quat& amount)
 {
   return set_rotation   (amount * rotation_);
 }
-transform& transform::look_at               (const vec3f& target, const vec3f& up_vector)
+transform& transform::look_at               (const glm::vec3& target, const glm::vec3& up_vector)
 {
-  return set_rotation(conjugate(toQuat(lookAt(translation_, target, up_vector))));
+  return set_rotation(glm::conjugate(glm::toQuat(lookAt(translation_, target, up_vector))));
 }
 
 void       transform::set_parent            (transform* parent)
@@ -76,7 +79,7 @@ transform* transform::child                 (std::size_t index) const
 
 void       transform::update_matrix         ()
 {
-  matrix_ = pli::translate(translation_) * mat4_cast(rotation_) * pli::scale(scale_);
+  matrix_ = glm::translate(translation_) * mat4_cast(rotation_) * glm::scale(scale_);
   // A change of local matrix implies a change of world matrix, hence:
   update_absolute_matrix();
 }
diff --git a/source/visualization/utility/render_target.cpp b/source/visualization/utility/render_target.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d5e16f46b2ee02b8178bc2e005f2d01782fb9ce7
--- /dev/null
+++ b/source/visualization/utility/render_target.cpp
@@ -0,0 +1,84 @@
+#include <pli_vis/visualization/utility/render_target.hpp>
+
+namespace pli
+{
+render_target::render_target(const glm::uvec2& size, mode mode) : mode_(mode)
+{
+  bind();
+  if(mode_ == mode::color_only || mode_ == mode::color_and_depth)
+  {
+    color_texture_.bind       ();
+    color_texture_.wrap_s     (GL_CLAMP_TO_EDGE);
+    color_texture_.wrap_t     (GL_CLAMP_TO_EDGE);
+    color_texture_.min_filter (GL_LINEAR);
+    color_texture_.mag_filter (GL_LINEAR);
+    color_texture_.set_image  (GL_RGBA32F, size[0], size[1], GL_RGBA, GL_FLOAT);
+    color_texture_.unbind     ();
+    framebuffer_  .set_texture(GL_COLOR_ATTACHMENT0, color_texture_);
+  }
+  if(mode_ == mode::depth_only || mode_ == mode::color_and_depth)
+  {
+    depth_texture_.bind       ();
+    depth_texture_.wrap_s     (GL_CLAMP_TO_EDGE);
+    depth_texture_.wrap_t     (GL_CLAMP_TO_EDGE);
+    depth_texture_.min_filter (GL_LINEAR);
+    depth_texture_.mag_filter (GL_LINEAR);
+    depth_texture_.set_image  (GL_DEPTH_COMPONENT32F, size[0], size[1], GL_DEPTH_COMPONENT, GL_FLOAT);
+    depth_texture_.unbind     ();
+    framebuffer_  .set_texture(GL_DEPTH_ATTACHMENT, depth_texture_);
+  }
+  assert(framebuffer_.is_valid() && framebuffer_.is_complete());
+  unbind();
+  
+  if (size == glm::uvec2{1u, 1u})
+    resize_to_viewport();
+}
+
+void render_target::resize(const glm::uvec2& size)
+{
+  if (mode_ == mode::color_only || mode_ == mode::color_and_depth)
+  {
+    color_texture_.bind     ();
+    color_texture_.set_image(GL_RGBA32F, size[0], size[1], GL_RGBA, GL_FLOAT);
+    color_texture_.unbind   ();
+  }
+  if (mode_ == mode::depth_only || mode_ == mode::color_and_depth)
+  {
+    depth_texture_.bind     ();
+    depth_texture_.set_image(GL_DEPTH_COMPONENT32F, size[0], size[1], GL_DEPTH_COMPONENT, GL_FLOAT);
+    depth_texture_.unbind   ();
+  }
+}
+void render_target::bind  ()
+{
+  GLint last_framebuffer_id;
+  glGetIntegerv(GL_FRAMEBUFFER_BINDING, &last_framebuffer_id);
+  last_framebuffer_ = gl::framebuffer(last_framebuffer_id);
+
+  framebuffer_.bind();
+}
+void render_target::unbind()
+{
+  last_framebuffer_.bind();
+}
+
+void render_target::resize_to_viewport()
+{
+  glm::ivec4 viewport;
+  glGetIntegerv(GL_VIEWPORT, reinterpret_cast<GLint*>(&viewport));
+  resize(glm::uvec2(viewport.z, viewport.w));
+}
+
+gl::framebuffer* render_target::framebuffer  ()
+{
+  return &framebuffer_;
+}
+gl::texture_2d*  render_target::color_texture()
+{
+  return &color_texture_;
+}
+gl::texture_2d*  render_target::depth_texture()
+{
+  return &depth_texture_;
+}
+}
diff --git a/source/visualization/vector_field.cpp b/source/visualization/vector_field.cpp
deleted file mode 100644
index 6ced4a781a9fae3a8e24226de8d2d9bc7c39bd6b..0000000000000000000000000000000000000000
--- a/source/visualization/vector_field.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-#include /* implements */ <visualization/vector_field.hpp>
-
-#include <cuda/vector_field.h>
-#include <math/camera.hpp>
-#include <shaders/vector_field.vert.glsl>
-#include <shaders/vector_field.frag.glsl>
-
-namespace pli
-{
-void vector_field::initialize()
-{
-  shader_program_.reset(new gl::program     );
-  vertex_array_  .reset(new gl::vertex_array);
-  vertex_buffer_ .reset(new gl::array_buffer);
-  color_buffer_  .reset(new gl::array_buffer);
-
-  shader_program_->attach_shader(gl::vertex_shader  (shaders::vector_field_vert));
-  shader_program_->attach_shader(gl::fragment_shader(shaders::vector_field_frag));
-  shader_program_->link();
-  
-  shader_program_->bind();
-  vertex_array_  ->bind();
-
-  vertex_buffer_ ->bind();
-  shader_program_->set_attribute_buffer  ("vertex", 3, GL_FLOAT);
-  shader_program_->enable_attribute_array("vertex");
-  vertex_buffer_ ->unbind();
-
-  color_buffer_  ->bind();
-  shader_program_->set_attribute_buffer  ("color" , 4, GL_FLOAT);
-  shader_program_->enable_attribute_array("color");
-  color_buffer_  ->unbind();
-
-  vertex_array_  ->unbind();
-  shader_program_->unbind();
-}
-void vector_field::render    (const camera* camera)
-{
-  shader_program_->bind  ();
-  vertex_array_  ->bind  ();
-  
-  shader_program_->set_uniform("projection", camera->projection_matrix      ());
-  shader_program_->set_uniform("view"      , camera->inverse_absolute_matrix());
-  glDrawArrays(GL_LINES, 0, GLsizei(draw_count_));
-
-  vertex_array_  ->unbind();
-  shader_program_->unbind();
-}
-
-void vector_field::set_data(
-  const uint3&  dimensions  ,
-  const float*  directions  ,
-  const float*  inclinations,
-  const float3& spacing     ,
-  float         scale       ,
-  std::function<void(const std::string&)> status_callback)
-{
-  draw_count_ = 2 * dimensions.x * dimensions.y * dimensions.z;
-
-  vertex_buffer_->bind         ();
-  vertex_buffer_->allocate     (draw_count_ * sizeof(float3));
-  vertex_buffer_->unbind       ();
-  vertex_buffer_->cuda_register();
-
-  color_buffer_ ->bind         ();
-  color_buffer_ ->allocate     (draw_count_ * sizeof(float4));
-  color_buffer_ ->unbind       ();
-  color_buffer_ ->cuda_register();
-
-  auto cuda_vertex_buffer = vertex_buffer_->cuda_map<float3>();
-  auto cuda_color_buffer  = color_buffer_ ->cuda_map<float4>();
-  create_vector_field(
-    dimensions        ,
-    directions        ,
-    inclinations      ,
-    spacing           ,
-    scale             ,
-    cuda_vertex_buffer,
-    cuda_color_buffer ,
-    status_callback   );
-
-  color_buffer_ ->cuda_unmap();
-  vertex_buffer_->cuda_unmap();
-}
-
-void vector_field::set_data(
-  const uint3&  dimensions  ,
-  const float3* unit_vectors,
-  const float3& spacing     ,
-  float         scale       ,
-  std::function<void(const std::string&)> status_callback)
-{
-  draw_count_ = 2 * dimensions.x * dimensions.y * dimensions.z;
-
-  vertex_buffer_->bind         ();
-  vertex_buffer_->allocate     (draw_count_ * sizeof(float3));
-  vertex_buffer_->unbind       ();
-  vertex_buffer_->cuda_register();
-
-  color_buffer_ ->bind         ();
-  color_buffer_ ->allocate     (draw_count_ * sizeof(float4));
-  color_buffer_ ->unbind       ();
-  color_buffer_ ->cuda_register();
-
-  auto cuda_vertex_buffer = vertex_buffer_->cuda_map<float3>();
-  auto cuda_color_buffer  = color_buffer_ ->cuda_map<float4>();
-  create_vector_field(
-    dimensions        ,
-    unit_vectors      ,
-    spacing           ,
-    scale             ,
-    cuda_vertex_buffer,
-    cuda_color_buffer ,
-    status_callback   );
-
-  color_buffer_ ->cuda_unmap();
-  vertex_buffer_->cuda_unmap();
-}
-}
-
diff --git a/tests/catch.hpp b/tests/catch.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6f9334ba52a7183b5c72ea5d6455cbef071df6c8
--- /dev/null
+++ b/tests/catch.hpp
@@ -0,0 +1,11282 @@
+/*
+ *  Catch v1.8.1
+ *  Generated: 2017-03-01 16:04:19.016511
+ *  ----------------------------------------------------------
+ *  This file has been merged from multiple headers. Please don't edit it directly
+ *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ *  Distributed under the Boost Software License, Version 1.0. (See accompanying
+ *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+#ifdef __clang__
+#    pragma clang system_header
+#elif defined __GNUC__
+#    pragma GCC system_header
+#endif
+
+// #included from: internal/catch_suppress_warnings.h
+
+#ifdef __clang__
+#   ifdef __ICC // icpc defines the __clang__ macro
+#       pragma warning(push)
+#       pragma warning(disable: 161 1682)
+#   else // __ICC
+#       pragma clang diagnostic ignored "-Wglobal-constructors"
+#       pragma clang diagnostic ignored "-Wvariadic-macros"
+#       pragma clang diagnostic ignored "-Wc99-extensions"
+#       pragma clang diagnostic ignored "-Wunused-variable"
+#       pragma clang diagnostic push
+#       pragma clang diagnostic ignored "-Wpadded"
+#       pragma clang diagnostic ignored "-Wc++98-compat"
+#       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#       pragma clang diagnostic ignored "-Wswitch-enum"
+#       pragma clang diagnostic ignored "-Wcovered-switch-default"
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic ignored "-Wvariadic-macros"
+#    pragma GCC diagnostic ignored "-Wunused-variable"
+
+     // For newer version we can use __Pragma to disable the warnings locally
+#    if __GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ <= 7
+#        pragma GCC diagnostic ignored "-Wparentheses"
+#    endif
+
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wpadded"
+#endif
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+#  define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+#  ifndef CLARA_CONFIG_MAIN
+#    define CLARA_CONFIG_MAIN_NOT_DEFINED
+#    define CLARA_CONFIG_MAIN
+#  endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
+// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported?
+// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported?
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
+
+#ifdef __cplusplus
+
+#  if __cplusplus >= 201103L
+#    define CATCH_CPP11_OR_GREATER
+#  endif
+
+#  if __cplusplus >= 201402L
+#    define CATCH_CPP14_OR_GREATER
+#  endif
+
+#endif
+
+#ifdef __clang__
+
+#  if __has_feature(cxx_nullptr)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  if __has_feature(cxx_noexcept)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#   if defined(CATCH_CPP11_OR_GREATER)
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "clang diagnostic push" ) \
+            _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "clang diagnostic pop" )
+#   endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+#   if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#   endif
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+#   define _BSD_SOURCE
+
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#   if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+#       define CATCH_GCC_HAS_NEW_PRAGMA
+#   endif
+
+#   if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#       define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   endif
+
+#   if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_GCC_HAS_NEW_PRAGMA)
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "GCC diagnostic push" ) \
+            _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "GCC diagnostic pop" )
+#   endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+
+#if (_MSC_VER >= 1600)
+#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+    ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+    ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+    ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+
+#endif
+
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+    ( defined __GNUC__  && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \
+    ( defined __clang__ && __clang_major__ >= 3 )
+
+#define CATCH_INTERNAL_CONFIG_COUNTER
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(CATCH_CPP11_OR_GREATER)
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#    define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#    define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#    define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+#    define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#    define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+#  endif
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#  endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
+#   define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#  endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS)
+#  define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+# endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_IS_ENUM
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_TUPLE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
+#   define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_UNIQUE_PTR
+#endif
+// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for
+// analytics) because, at time of writing, __COUNTER__ is not properly handled by it.
+// This does not affect compilation
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__)
+#   define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_SHUFFLE
+#endif
+# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11)
+#  define CATCH_CONFIG_CPP11_TYPE_TRAITS
+# endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
+#   define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#   define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#   define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+#  define CATCH_NOEXCEPT noexcept
+#  define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+#  define CATCH_NOEXCEPT throw()
+#  define CATCH_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+#   define CATCH_NULL nullptr
+#else
+#   define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+#   define CATCH_OVERRIDE override
+#else
+#   define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+#   define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+    struct IConfig;
+
+    struct CaseSensitive { enum Choice {
+        Yes,
+        No
+    }; };
+
+    class NonCopyable {
+#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        NonCopyable( NonCopyable const& )              = delete;
+        NonCopyable( NonCopyable && )                  = delete;
+        NonCopyable& operator = ( NonCopyable const& ) = delete;
+        NonCopyable& operator = ( NonCopyable && )     = delete;
+#else
+        NonCopyable( NonCopyable const& info );
+        NonCopyable& operator = ( NonCopyable const& );
+#endif
+
+    protected:
+        NonCopyable() {}
+        virtual ~NonCopyable();
+    };
+
+    class SafeBool {
+    public:
+        typedef void (SafeBool::*type)() const;
+
+        static type makeSafe( bool value ) {
+            return value ? &SafeBool::trueValue : 0;
+        }
+    private:
+        void trueValue() const {}
+    };
+
+    template<typename ContainerT>
+    inline void deleteAll( ContainerT& container ) {
+        typename ContainerT::const_iterator it = container.begin();
+        typename ContainerT::const_iterator itEnd = container.end();
+        for(; it != itEnd; ++it )
+            delete *it;
+    }
+    template<typename AssociativeContainerT>
+    inline void deleteAllValues( AssociativeContainerT& container ) {
+        typename AssociativeContainerT::const_iterator it = container.begin();
+        typename AssociativeContainerT::const_iterator itEnd = container.end();
+        for(; it != itEnd; ++it )
+            delete it->second;
+    }
+
+    bool startsWith( std::string const& s, std::string const& prefix );
+    bool startsWith( std::string const& s, char prefix );
+    bool endsWith( std::string const& s, std::string const& suffix );
+    bool endsWith( std::string const& s, char suffix );
+    bool contains( std::string const& s, std::string const& infix );
+    void toLowerInPlace( std::string& s );
+    std::string toLower( std::string const& s );
+    std::string trim( std::string const& str );
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+    struct pluralise {
+        pluralise( std::size_t count, std::string const& label );
+
+        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+        std::size_t m_count;
+        std::string m_label;
+    };
+
+    struct SourceLineInfo {
+
+        SourceLineInfo();
+        SourceLineInfo( char const* _file, std::size_t _line );
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        SourceLineInfo(SourceLineInfo const& other)          = default;
+        SourceLineInfo( SourceLineInfo && )                  = default;
+        SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+        SourceLineInfo& operator = ( SourceLineInfo && )     = default;
+#  endif
+        bool empty() const;
+        bool operator == ( SourceLineInfo const& other ) const;
+        bool operator < ( SourceLineInfo const& other ) const;
+
+        char const* file;
+        std::size_t line;
+    };
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+    // This is just here to avoid compiler warnings with macro constants and boolean literals
+    inline bool isTrue( bool value ){ return value; }
+    inline bool alwaysTrue() { return true; }
+    inline bool alwaysFalse() { return false; }
+
+    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+    void seedRng( IConfig const& config );
+    unsigned int rngSeed();
+
+    // Use this in variadic streaming macros to allow
+    //    >> +StreamEndStop
+    // as well as
+    //    >> stuff +StreamEndStop
+    struct StreamEndStop {
+        std::string operator+() {
+            return std::string();
+        }
+    };
+    template<typename T>
+    T const& operator + ( T const& value, StreamEndStop ) {
+        return value;
+    }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+namespace Catch {
+
+    class NotImplementedException : public std::exception
+    {
+    public:
+        NotImplementedException( SourceLineInfo const& lineInfo );
+        NotImplementedException( NotImplementedException const& ) {}
+
+        virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+        virtual const char* what() const CATCH_NOEXCEPT;
+
+    private:
+        std::string m_what;
+        SourceLineInfo m_lineInfo;
+    };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct IGeneratorInfo {
+        virtual ~IGeneratorInfo();
+        virtual bool moveNext() = 0;
+        virtual std::size_t getCurrentIndex() const = 0;
+    };
+
+    struct IGeneratorsForTest {
+        virtual ~IGeneratorsForTest();
+
+        virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+        virtual bool moveNext() = 0;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    // An intrusive reference counting smart pointer.
+    // T must implement addRef() and release() methods
+    // typically implementing the IShared interface
+    template<typename T>
+    class Ptr {
+    public:
+        Ptr() : m_p( CATCH_NULL ){}
+        Ptr( T* p ) : m_p( p ){
+            if( m_p )
+                m_p->addRef();
+        }
+        Ptr( Ptr const& other ) : m_p( other.m_p ){
+            if( m_p )
+                m_p->addRef();
+        }
+        ~Ptr(){
+            if( m_p )
+                m_p->release();
+        }
+        void reset() {
+            if( m_p )
+                m_p->release();
+            m_p = CATCH_NULL;
+        }
+        Ptr& operator = ( T* p ){
+            Ptr temp( p );
+            swap( temp );
+            return *this;
+        }
+        Ptr& operator = ( Ptr const& other ){
+            Ptr temp( other );
+            swap( temp );
+            return *this;
+        }
+        void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+        T* get() const{ return m_p; }
+        T& operator*() const { return *m_p; }
+        T* operator->() const { return m_p; }
+        bool operator !() const { return m_p == CATCH_NULL; }
+        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
+
+    private:
+        T* m_p;
+    };
+
+    struct IShared : NonCopyable {
+        virtual ~IShared();
+        virtual void addRef() const = 0;
+        virtual void release() const = 0;
+    };
+
+    template<typename T = IShared>
+    struct SharedImpl : T {
+
+        SharedImpl() : m_rc( 0 ){}
+
+        virtual void addRef() const {
+            ++m_rc;
+        }
+        virtual void release() const {
+            if( --m_rc == 0 )
+                delete this;
+        }
+
+        mutable unsigned int m_rc;
+    };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+    class TestCase;
+    class Stream;
+    struct IResultCapture;
+    struct IRunner;
+    struct IGeneratorsForTest;
+    struct IConfig;
+
+    struct IContext
+    {
+        virtual ~IContext();
+
+        virtual IResultCapture* getResultCapture() = 0;
+        virtual IRunner* getRunner() = 0;
+        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+        virtual bool advanceGeneratorsForCurrentTest() = 0;
+        virtual Ptr<IConfig const> getConfig() const = 0;
+    };
+
+    struct IMutableContext : IContext
+    {
+        virtual ~IMutableContext();
+        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+        virtual void setRunner( IRunner* runner ) = 0;
+        virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+    };
+
+    IContext& getCurrentContext();
+    IMutableContext& getCurrentMutableContext();
+    void cleanUpContext();
+    Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec;
+
+    struct ITestCase : IShared {
+        virtual void invoke () const = 0;
+    protected:
+        virtual ~ITestCase();
+    };
+
+    class TestCase;
+    struct IConfig;
+
+    struct ITestCaseRegistry {
+        virtual ~ITestCaseRegistry();
+        virtual std::vector<TestCase> const& getAllTests() const = 0;
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+    };
+
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+    MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+    virtual void invoke() const {
+        C obj;
+        (obj.*m_method)();
+    }
+
+private:
+    virtual ~MethodTestCase() {}
+
+    void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+    NameAndDesc( const char* _name = "", const char* _description= "" )
+    : name( _name ), description( _description )
+    {}
+
+    const char* name;
+    const char* description;
+};
+
+void registerTestCase
+    (   ITestCase* testCase,
+        char const* className,
+        NameAndDesc const& nameAndDesc,
+        SourceLineInfo const& lineInfo );
+
+struct AutoReg {
+
+    AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc );
+
+    template<typename C>
+    AutoReg
+        (   void (C::*method)(),
+            char const* className,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
+
+        registerTestCase
+            (   new MethodTestCase<C>( method ),
+                className,
+                nameAndDesc,
+                lineInfo );
+    }
+
+    ~AutoReg();
+
+private:
+    AutoReg( AutoReg const& );
+    void operator= ( AutoReg const& );
+};
+
+void registerTestCaseFunction
+    (   TestFunction function,
+        SourceLineInfo const& lineInfo,
+        NameAndDesc const& nameAndDesc );
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+        static void TestName(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE( ... ) \
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+        namespace{ \
+            struct TestName : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+        } \
+        void TestName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) );
+
+#else
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+        static void TestName(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
+        namespace{ \
+            struct TestCaseName : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+        } \
+        void TestCaseName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) );
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+    // ResultWas::OfType enum
+    struct ResultWas { enum OfType {
+        Unknown = -1,
+        Ok = 0,
+        Info = 1,
+        Warning = 2,
+
+        FailureBit = 0x10,
+
+        ExpressionFailed = FailureBit | 1,
+        ExplicitFailure = FailureBit | 2,
+
+        Exception = 0x100 | FailureBit,
+
+        ThrewException = Exception | 1,
+        DidntThrowException = Exception | 2,
+
+        FatalErrorCondition = 0x200 | FailureBit
+
+    }; };
+
+    inline bool isOk( ResultWas::OfType resultType ) {
+        return ( resultType & ResultWas::FailureBit ) == 0;
+    }
+    inline bool isJustInfo( int flags ) {
+        return flags == ResultWas::Info;
+    }
+
+    // ResultDisposition::Flags enum
+    struct ResultDisposition { enum Flags {
+        Normal = 0x01,
+
+        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
+        FalseTest = 0x04,           // Prefix expression with !
+        SuppressFail = 0x08         // Failures are reported but do not fail the test
+    }; };
+
+    inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+    }
+
+    inline bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+    inline bool isFalseTest( int flags )                { return ( flags & ResultDisposition::FalseTest ) != 0; }
+    inline bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+    struct DecomposedExpression
+    {
+        virtual ~DecomposedExpression() {}
+        virtual bool isBinaryExpression() const {
+            return false;
+        }
+        virtual void reconstructExpression( std::string& dest ) const = 0;
+
+        // Only simple binary comparisons can be decomposed.
+        // If more complex check is required then wrap sub-expressions in parentheses.
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
+
+	private:
+		DecomposedExpression& operator = (DecomposedExpression const&);
+    };
+
+    struct AssertionInfo
+    {
+        AssertionInfo() {}
+        AssertionInfo(  std::string const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        std::string const& _capturedExpression,
+                        ResultDisposition::Flags _resultDisposition );
+
+        std::string macroName;
+        SourceLineInfo lineInfo;
+        std::string capturedExpression;
+        ResultDisposition::Flags resultDisposition;
+    };
+
+    struct AssertionResultData
+    {
+        AssertionResultData() : decomposedExpression( CATCH_NULL )
+                              , resultType( ResultWas::Unknown )
+                              , negated( false )
+                              , parenthesized( false ) {}
+
+        void negate( bool parenthesize ) {
+            negated = !negated;
+            parenthesized = parenthesize;
+            if( resultType == ResultWas::Ok )
+                resultType = ResultWas::ExpressionFailed;
+            else if( resultType == ResultWas::ExpressionFailed )
+                resultType = ResultWas::Ok;
+        }
+
+        std::string const& reconstructExpression() const {
+            if( decomposedExpression != CATCH_NULL ) {
+                decomposedExpression->reconstructExpression( reconstructedExpression );
+                if( parenthesized ) {
+                    reconstructedExpression.insert( 0, 1, '(' );
+                    reconstructedExpression.append( 1, ')' );
+                }
+                if( negated ) {
+                    reconstructedExpression.insert( 0, 1, '!' );
+                }
+                decomposedExpression = CATCH_NULL;
+            }
+            return reconstructedExpression;
+        }
+
+        mutable DecomposedExpression const* decomposedExpression;
+        mutable std::string reconstructedExpression;
+        std::string message;
+        ResultWas::OfType resultType;
+        bool negated;
+        bool parenthesized;
+    };
+
+    class AssertionResult {
+    public:
+        AssertionResult();
+        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+        ~AssertionResult();
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+         AssertionResult( AssertionResult const& )              = default;
+         AssertionResult( AssertionResult && )                  = default;
+         AssertionResult& operator = ( AssertionResult const& ) = default;
+         AssertionResult& operator = ( AssertionResult && )     = default;
+#  endif
+
+        bool isOk() const;
+        bool succeeded() const;
+        ResultWas::OfType getResultType() const;
+        bool hasExpression() const;
+        bool hasMessage() const;
+        std::string getExpression() const;
+        std::string getExpressionInMacro() const;
+        bool hasExpandedExpression() const;
+        std::string getExpandedExpression() const;
+        std::string getMessage() const;
+        SourceLineInfo getSourceInfo() const;
+        std::string getTestMacroName() const;
+        void discardDecomposedExpression() const;
+        void expandDecomposedExpression() const;
+
+    protected:
+        AssertionInfo m_info;
+        AssertionResultData m_resultData;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+    namespace Impl {
+
+        template<typename ArgT> struct MatchAllOf;
+        template<typename ArgT> struct MatchAnyOf;
+        template<typename ArgT> struct MatchNotOf;
+
+        class MatcherUntypedBase {
+        public:
+            std::string toString() const {
+                if( m_cachedToString.empty() )
+                    m_cachedToString = describe();
+                return m_cachedToString;
+            }
+
+        protected:
+            virtual std::string describe() const = 0;
+            mutable std::string m_cachedToString;
+        private:
+            MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
+        };
+
+        template<typename ObjectT, typename ComparatorT = ObjectT>
+        struct MatcherBase : MatcherUntypedBase {
+
+            virtual bool match( ObjectT const& arg ) const = 0;
+
+            MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
+            MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
+            MatchNotOf<ComparatorT> operator ! () const;
+        };
+
+        template<typename ArgT>
+        struct MatchAllOf : MatcherBase<ArgT> {
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if (!m_matchers[i]->match(arg))
+                        return false;
+                }
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        description += " and ";
+                    description += m_matchers[i]->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+                m_matchers.push_back( &other );
+                return *this;
+            }
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+        template<typename ArgT>
+        struct MatchAnyOf : MatcherBase<ArgT> {
+
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if (m_matchers[i]->match(arg))
+                        return true;
+                }
+                return false;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        description += " or ";
+                    description += m_matchers[i]->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+                m_matchers.push_back( &other );
+                return *this;
+            }
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+
+        template<typename ArgT>
+        struct MatchNotOf : MatcherBase<ArgT> {
+
+            MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                return !m_underlyingMatcher.match( arg );
+            }
+
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "not " + m_underlyingMatcher.toString();
+            }
+            MatcherBase<ArgT> const& m_underlyingMatcher;
+        };
+
+        template<typename ObjectT, typename ComparatorT>
+        MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
+            return MatchAllOf<ComparatorT>() && *this && other;
+        }
+        template<typename ObjectT, typename ComparatorT>
+        MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
+            return MatchAnyOf<ComparatorT>() || *this || other;
+        }
+        template<typename ObjectT, typename ComparatorT>
+        MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
+            return MatchNotOf<ComparatorT>( *this );
+        }
+
+    } // namespace Impl
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    // - deprecated: prefer ||, && and !
+    template<typename T>
+    inline Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
+        return Impl::MatchNotOf<T>( underlyingMatcher );
+    }
+    template<typename T>
+    inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+        return Impl::MatchAllOf<T>() && m1 && m2;
+    }
+    template<typename T>
+    inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+        return Impl::MatchAllOf<T>() && m1 && m2 && m3;
+    }
+    template<typename T>
+    inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+        return Impl::MatchAnyOf<T>() || m1 || m2;
+    }
+    template<typename T>
+    inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+        return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
+    }
+
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+
+namespace Catch {
+
+    struct TestFailureException{};
+
+    template<typename T> class ExpressionLhs;
+
+    struct CopyableStream {
+        CopyableStream() {}
+        CopyableStream( CopyableStream const& other ) {
+            oss << other.oss.str();
+        }
+        CopyableStream& operator=( CopyableStream const& other ) {
+            oss.str(std::string());
+            oss << other.oss.str();
+            return *this;
+        }
+        std::ostringstream oss;
+    };
+
+    class ResultBuilder : public DecomposedExpression {
+    public:
+        ResultBuilder(  char const* macroName,
+                        SourceLineInfo const& lineInfo,
+                        char const* capturedExpression,
+                        ResultDisposition::Flags resultDisposition,
+                        char const* secondArg = "" );
+
+        template<typename T>
+        ExpressionLhs<T const&> operator <= ( T const& operand );
+        ExpressionLhs<bool> operator <= ( bool value );
+
+        template<typename T>
+        ResultBuilder& operator << ( T const& value ) {
+            m_stream.oss << value;
+            return *this;
+        }
+
+        ResultBuilder& setResultType( ResultWas::OfType result );
+        ResultBuilder& setResultType( bool result );
+
+        void endExpression( DecomposedExpression const& expr );
+
+        virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
+
+        AssertionResult build() const;
+        AssertionResult build( DecomposedExpression const& expr ) const;
+
+        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+        void captureResult( ResultWas::OfType resultType );
+        void captureExpression();
+        void captureExpectedException( std::string const& expectedMessage );
+        void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
+        void handleResult( AssertionResult const& result );
+        void react();
+        bool shouldDebugBreak() const;
+        bool allowThrows() const;
+
+        template<typename ArgT, typename MatcherT>
+        void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
+
+    private:
+        AssertionInfo m_assertionInfo;
+        AssertionResultData m_data;
+        CopyableStream m_stream;
+
+        bool m_shouldDebugBreak;
+        bool m_shouldThrow;
+    };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+    enum Operator {
+        IsEqualTo,
+        IsNotEqualTo,
+        IsLessThan,
+        IsGreaterThan,
+        IsLessThanOrEqualTo,
+        IsGreaterThanOrEqualTo
+    };
+
+    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
+    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
+    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
+    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
+    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
+    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
+    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+    template<typename T>
+    inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+    // So the compare overloads can be operator agnostic we convey the operator as a template
+    // enum, which is used to specialise an Evaluator for doing the comparison.
+    template<typename T1, typename T2, Operator Op>
+    class Evaluator{};
+
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs) {
+            return bool( opCast( lhs ) ==  opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsNotEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) != opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) < opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) > opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) >= opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) <= opCast( rhs ) );
+        }
+    };
+
+    template<Operator Op, typename T1, typename T2>
+    bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+    }
+
+    // This level of indirection allows us to specialise for integer types
+    // to avoid signed/ unsigned warnings
+
+    // "base" overload
+    template<Operator Op, typename T1, typename T2>
+    bool compare( T1 const& lhs, T2 const& rhs ) {
+        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+    }
+
+    // unsigned X to int
+    template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+
+    // unsigned X to long
+    template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+
+    // int to unsigned X
+    template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+
+    // long to unsigned X
+    template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // pointer to long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+
+    // pointer to int (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+    // long long to unsigned X
+    template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // unsigned long long to X
+    template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+
+    // pointer to long long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    // pointer to nullptr_t (when comparing against nullptr)
+    template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
+    }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+    [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+#include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template<typename T>
+std::string toString( T const& value );
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring );
+    std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
+    std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+    extern const std::string unprintableString;
+
+    struct BorgType {
+        template<typename T> BorgType( T const& );
+    };
+
+    struct TrueType { char sizer[1]; };
+    struct FalseType { char sizer[2]; };
+
+    TrueType& testStreamable( std::ostream& );
+    FalseType testStreamable( FalseType );
+
+    FalseType operator<<( std::ostream const&, BorgType const& );
+
+    template<typename T>
+    struct IsStreamInsertable {
+        static std::ostream &s;
+        static T  const&t;
+        enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+    };
+
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+    template<typename T,
+             bool IsEnum = std::is_enum<T>::value
+             >
+    struct EnumStringMaker
+    {
+        static std::string convert( T const& ) { return unprintableString; }
+    };
+
+    template<typename T>
+    struct EnumStringMaker<T,true>
+    {
+        static std::string convert( T const& v )
+        {
+            return ::Catch::toString(
+                static_cast<typename std::underlying_type<T>::type>(v)
+                );
+        }
+    };
+#endif
+    template<bool C>
+    struct StringMakerBase {
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+        template<typename T>
+        static std::string convert( T const& v )
+        {
+            return EnumStringMaker<T>::convert( v );
+        }
+#else
+        template<typename T>
+        static std::string convert( T const& ) { return unprintableString; }
+#endif
+    };
+
+    template<>
+    struct StringMakerBase<true> {
+        template<typename T>
+        static std::string convert( T const& _value ) {
+            std::ostringstream oss;
+            oss << _value;
+            return oss.str();
+        }
+    };
+
+    std::string rawMemoryToString( const void *object, std::size_t size );
+
+    template<typename T>
+    inline std::string rawMemoryToString( const T& object ) {
+      return rawMemoryToString( &object, sizeof(object) );
+    }
+
+} // end namespace Detail
+
+template<typename T>
+struct StringMaker :
+    Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+    template<typename U>
+    static std::string convert( U* p ) {
+        if( !p )
+            return "NULL";
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+    static std::string convert( R C::* p ) {
+        if( !p )
+            return "NULL";
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+namespace Detail {
+    template<typename InputIterator>
+    std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+//template<typename T, typename Allocator>
+//struct StringMaker<std::vector<T, Allocator> > {
+//    static std::string convert( std::vector<T,Allocator> const& v ) {
+//        return Detail::rangeToString( v.begin(), v.end() );
+//    }
+//};
+
+template<typename T, typename Allocator>
+std::string toString( std::vector<T,Allocator> const& v ) {
+    return Detail::rangeToString( v.begin(), v.end() );
+}
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+
+// toString for tuples
+namespace TupleDetail {
+  template<
+      typename Tuple,
+      std::size_t N = 0,
+      bool = (N < std::tuple_size<Tuple>::value)
+      >
+  struct ElementPrinter {
+      static void print( const Tuple& tuple, std::ostream& os )
+      {
+          os << ( N ? ", " : " " )
+             << Catch::toString(std::get<N>(tuple));
+          ElementPrinter<Tuple,N+1>::print(tuple,os);
+      }
+  };
+
+  template<
+      typename Tuple,
+      std::size_t N
+      >
+  struct ElementPrinter<Tuple,N,false> {
+      static void print( const Tuple&, std::ostream& ) {}
+  };
+
+}
+
+template<typename ...Types>
+struct StringMaker<std::tuple<Types...>> {
+
+    static std::string convert( const std::tuple<Types...>& tuple )
+    {
+        std::ostringstream os;
+        os << '{';
+        TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
+        os << " }";
+        return os.str();
+    }
+};
+#endif // CATCH_CONFIG_CPP11_TUPLE
+
+namespace Detail {
+    template<typename T>
+    std::string makeString( T const& value ) {
+        return StringMaker<T>::convert( value );
+    }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+    return StringMaker<T>::convert( value );
+}
+
+    namespace Detail {
+    template<typename InputIterator>
+    std::string rangeToString( InputIterator first, InputIterator last ) {
+        std::ostringstream oss;
+        oss << "{ ";
+        if( first != last ) {
+            oss << Catch::toString( *first );
+            for( ++first ; first != last ; ++first )
+                oss << ", " << Catch::toString( *first );
+        }
+        oss << " }";
+        return oss.str();
+    }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression;
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression;
+
+// Wraps the LHS of an expression and overloads comparison operators
+// for also capturing those and RHS (if any)
+template<typename T>
+class ExpressionLhs : public DecomposedExpression {
+public:
+    ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
+
+    ExpressionLhs& operator = ( const ExpressionLhs& );
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
+    operator == ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
+    operator != ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsLessThan, RhsT const&>
+    operator < ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThan>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
+    operator > ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThan>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
+    operator <= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
+    operator >= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+    }
+
+    BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    void endExpression() {
+        m_truthy = m_lhs ? true : false;
+        m_rb
+            .setResultType( m_truthy )
+            .endExpression( *this );
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        dest = Catch::toString( m_truthy );
+    }
+
+private:
+    template<Internal::Operator Op, typename RhsT>
+    BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
+        return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
+    }
+
+    template<Internal::Operator Op>
+    BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
+        return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
+    }
+
+private:
+    ResultBuilder& m_rb;
+    T m_lhs;
+    bool m_truthy;
+};
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression : public DecomposedExpression {
+public:
+    BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
+        : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+
+    BinaryExpression& operator = ( BinaryExpression& );
+
+    void endExpression() const {
+        m_rb
+            .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
+            .endExpression( *this );
+    }
+
+    virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+        return true;
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        std::string lhs = Catch::toString( m_lhs );
+        std::string rhs = Catch::toString( m_rhs );
+        char delim = lhs.size() + rhs.size() < 40 &&
+                     lhs.find('\n') == std::string::npos &&
+                     rhs.find('\n') == std::string::npos ? ' ' : '\n';
+        dest.reserve( 7 + lhs.size() + rhs.size() );
+                   // 2 for spaces around operator
+                   // 2 for operator
+                   // 2 for parentheses (conditionally added later)
+                   // 1 for negation (conditionally added later)
+        dest = lhs;
+        dest += delim;
+        dest += Internal::OperatorTraits<Op>::getName();
+        dest += delim;
+        dest += rhs;
+    }
+
+private:
+    ResultBuilder& m_rb;
+    LhsT m_lhs;
+    RhsT m_rhs;
+};
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression : public DecomposedExpression {
+public:
+    MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
+        : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
+
+    virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+        return true;
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        std::string matcherAsString = m_matcher.toString();
+        dest = Catch::toString( m_arg );
+        dest += ' ';
+        if( matcherAsString == Detail::unprintableString )
+            dest += m_matcherString;
+        else
+            dest += matcherAsString;
+    }
+
+private:
+    ArgT m_arg;
+    MatcherT m_matcher;
+    char const* m_matcherString;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+    template<typename T>
+    inline ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
+        return ExpressionLhs<T const&>( *this, operand );
+    }
+
+    inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
+        return ExpressionLhs<bool>( *this, value );
+    }
+
+    template<typename ArgT, typename MatcherT>
+    inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
+                                             char const* matcherString ) {
+        MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
+        setResultType( matcher.match( arg ) );
+        endExpression( expr );
+    }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct MessageInfo {
+        MessageInfo(    std::string const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        ResultWas::OfType _type );
+
+        std::string macroName;
+        SourceLineInfo lineInfo;
+        ResultWas::OfType type;
+        std::string message;
+        unsigned int sequence;
+
+        bool operator == ( MessageInfo const& other ) const {
+            return sequence == other.sequence;
+        }
+        bool operator < ( MessageInfo const& other ) const {
+            return sequence < other.sequence;
+        }
+    private:
+        static unsigned int globalCount;
+    };
+
+    struct MessageBuilder {
+        MessageBuilder( std::string const& macroName,
+                        SourceLineInfo const& lineInfo,
+                        ResultWas::OfType type )
+        : m_info( macroName, lineInfo, type )
+        {}
+
+        template<typename T>
+        MessageBuilder& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        MessageInfo m_info;
+        std::ostringstream m_stream;
+    };
+
+    class ScopedMessage {
+    public:
+        ScopedMessage( MessageBuilder const& builder );
+        ScopedMessage( ScopedMessage const& other );
+        ~ScopedMessage();
+
+        MessageInfo m_info;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class TestCase;
+    class AssertionResult;
+    struct AssertionInfo;
+    struct SectionInfo;
+    struct SectionEndInfo;
+    struct MessageInfo;
+    class ScopedMessageBuilder;
+    struct Counts;
+
+    struct IResultCapture {
+
+        virtual ~IResultCapture();
+
+        virtual void assertionEnded( AssertionResult const& result ) = 0;
+        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
+                                        Counts& assertions ) = 0;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+        virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+        virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+        virtual std::string getCurrentTestName() const = 0;
+        virtual const AssertionResult* getLastResult() const = 0;
+
+        virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+    };
+
+    IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#  define CATCH_PLATFORM_MAC
+#elif  defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#  define CATCH_PLATFORM_IPHONE
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+#  define CATCH_PLATFORM_LINUX
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#  define CATCH_PLATFORM_WINDOWS
+#  if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+#    define CATCH_DEFINES_NOMINMAX
+#  endif
+#  if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+#    define CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+#  endif
+#endif
+
+#include <string>
+
+namespace Catch{
+
+    bool isDebuggerActive();
+    void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+    // The following code snippet based on:
+    // http://cocoawithlove.com/2008/03/break-into-debugger.html
+    #if defined(__ppc64__) || defined(__ppc__)
+        #define CATCH_TRAP() \
+                __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+                : : : "memory","r0","r3","r4" )
+    #else
+        #define CATCH_TRAP() __asm__("int $3\n" : : )
+    #endif
+
+#elif defined(CATCH_PLATFORM_LINUX)
+    // If we can use inline assembler, do it because this allows us to break
+    // directly at the location of the failing check instead of breaking inside
+    // raise() called from it, i.e. one stack frame below.
+    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+        #define CATCH_TRAP() asm volatile ("int $3")
+    #else // Fall back to the generic way.
+        #include <signal.h>
+
+        #define CATCH_TRAP() raise(SIGTRAP)
+    #endif
+#elif defined(_MSC_VER)
+    #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+    #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifdef CATCH_TRAP
+    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+#else
+    #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+    class TestCase;
+
+    struct IRunner {
+        virtual ~IRunner();
+        virtual bool aborting() const = 0;
+    };
+}
+
+// #included from: catch_type_traits.hpp
+#define TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+
+     template <typename T>
+     using add_lvalue_reference = std::add_lvalue_reference<T>;
+
+     template <typename T>
+     using add_const = std::add_const<T>;
+
+#else
+
+    template <typename T>
+    struct add_const {
+        typedef const T type;
+    };
+
+    template <typename T>
+    struct add_lvalue_reference {
+        typedef T& type;
+    };
+    template <typename T>
+    struct add_lvalue_reference<T&> {
+        typedef T& type;
+    };
+    // No && overload, because that is C++11, in which case we have
+    // proper type_traits implementation from the standard library
+
+#endif
+
+}
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+///////////////////////////////////////////////////////////////////////////////
+// We can speedup compilation significantly by breaking into debugger lower in
+// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
+// macro in each assertion
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+    resultBuilder.react();
+#else
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+    if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+    resultBuilder.react();
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        try { \
+            CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            ( __catchResult <= expr ).endExpression(); \
+            CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+    // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
+    INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+    if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
+    INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+    if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        try { \
+            static_cast<void>(expr); \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                static_cast<void>(expr); \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( ... ) { \
+                __catchResult.captureExpectedException( matcher ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                static_cast<void>(expr); \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( Catch::add_const<Catch::add_lvalue_reference<exceptionType>::type>::type ) { \
+                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            } \
+            catch( ... ) { \
+                __catchResult.useActiveException( resultDisposition ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
+        do { \
+            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+            __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+            __catchResult.captureResult( messageType ); \
+            INTERNAL_CATCH_REACT( __catchResult ) \
+        } while( Catch::alwaysFalse() )
+#else
+    #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
+        do { \
+            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+            __catchResult << log + ::Catch::StreamEndStop(); \
+            __catchResult.captureResult( messageType ); \
+            INTERNAL_CATCH_REACT( __catchResult ) \
+        } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( log, macroName ) \
+    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+        try { \
+            __catchResult.captureMatch( arg, matcher, #matcher ); \
+        } catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+    struct Counts {
+        Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+        Counts operator - ( Counts const& other ) const {
+            Counts diff;
+            diff.passed = passed - other.passed;
+            diff.failed = failed - other.failed;
+            diff.failedButOk = failedButOk - other.failedButOk;
+            return diff;
+        }
+        Counts& operator += ( Counts const& other ) {
+            passed += other.passed;
+            failed += other.failed;
+            failedButOk += other.failedButOk;
+            return *this;
+        }
+
+        std::size_t total() const {
+            return passed + failed + failedButOk;
+        }
+        bool allPassed() const {
+            return failed == 0 && failedButOk == 0;
+        }
+        bool allOk() const {
+            return failed == 0;
+        }
+
+        std::size_t passed;
+        std::size_t failed;
+        std::size_t failedButOk;
+    };
+
+    struct Totals {
+
+        Totals operator - ( Totals const& other ) const {
+            Totals diff;
+            diff.assertions = assertions - other.assertions;
+            diff.testCases = testCases - other.testCases;
+            return diff;
+        }
+
+        Totals delta( Totals const& prevTotals ) const {
+            Totals diff = *this - prevTotals;
+            if( diff.assertions.failed > 0 )
+                ++diff.testCases.failed;
+            else if( diff.assertions.failedButOk > 0 )
+                ++diff.testCases.failedButOk;
+            else
+                ++diff.testCases.passed;
+            return diff;
+        }
+
+        Totals& operator += ( Totals const& other ) {
+            assertions += other.assertions;
+            testCases += other.testCases;
+            return *this;
+        }
+
+        Counts assertions;
+        Counts testCases;
+    };
+}
+
+#include <string>
+
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& _description = std::string() );
+
+        std::string name;
+        std::string description;
+        SourceLineInfo lineInfo;
+    };
+
+    struct SectionEndInfo {
+        SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+        : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+        {}
+
+        SectionInfo sectionInfo;
+        Counts prevAssertions;
+        double durationInSeconds;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef CATCH_PLATFORM_WINDOWS
+typedef unsigned long long uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+namespace Catch {
+
+    class Timer {
+    public:
+        Timer() : m_ticks( 0 ) {}
+        void start();
+        unsigned int getElapsedMicroseconds() const;
+        unsigned int getElapsedMilliseconds() const;
+        double getElapsedSeconds() const;
+
+    private:
+        uint64_t m_ticks;
+    };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+    class Section : NonCopyable {
+    public:
+        Section( SectionInfo const& info );
+        ~Section();
+
+        // This indicates whether the section should be executed or not
+        operator bool() const;
+
+    private:
+        SectionInfo m_info;
+
+        std::string m_name;
+        Counts m_assertions;
+        bool m_sectionIncluded;
+        Timer m_timer;
+    };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_SECTION( ... ) \
+        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+    #define INTERNAL_CATCH_SECTION( name, desc ) \
+        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <iterator>
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+namespace Catch {
+
+template<typename T>
+struct IGenerator {
+    virtual ~IGenerator() {}
+    virtual T getValue( std::size_t index ) const = 0;
+    virtual std::size_t size () const = 0;
+};
+
+template<typename T>
+class BetweenGenerator : public IGenerator<T> {
+public:
+    BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+    virtual T getValue( std::size_t index ) const {
+        return m_from+static_cast<int>( index );
+    }
+
+    virtual std::size_t size() const {
+        return static_cast<std::size_t>( 1+m_to-m_from );
+    }
+
+private:
+
+    T m_from;
+    T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+    ValuesGenerator(){}
+
+    void add( T value ) {
+        m_values.push_back( value );
+    }
+
+    virtual T getValue( std::size_t index ) const {
+        return m_values[index];
+    }
+
+    virtual std::size_t size() const {
+        return m_values.size();
+    }
+
+private:
+    std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+    CompositeGenerator() : m_totalSize( 0 ) {}
+
+    // *** Move semantics, similar to auto_ptr ***
+    CompositeGenerator( CompositeGenerator& other )
+    :   m_fileInfo( other.m_fileInfo ),
+        m_totalSize( 0 )
+    {
+        move( other );
+    }
+
+    CompositeGenerator& setFileInfo( const char* fileInfo ) {
+        m_fileInfo = fileInfo;
+        return *this;
+    }
+
+    ~CompositeGenerator() {
+        deleteAll( m_composed );
+    }
+
+    operator T () const {
+        size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+        typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
+        typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
+        for( size_t index = 0; it != itEnd; ++it )
+        {
+            const IGenerator<T>* generator = *it;
+            if( overallIndex >= index && overallIndex < index + generator->size() )
+            {
+                return generator->getValue( overallIndex-index );
+            }
+            index += generator->size();
+        }
+        CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+        return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+    }
+
+    void add( const IGenerator<T>* generator ) {
+        m_totalSize += generator->size();
+        m_composed.push_back( generator );
+    }
+
+    CompositeGenerator& then( CompositeGenerator& other ) {
+        move( other );
+        return *this;
+    }
+
+    CompositeGenerator& then( T value ) {
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( value );
+        add( valuesGen );
+        return *this;
+    }
+
+private:
+
+    void move( CompositeGenerator& other ) {
+        std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
+        m_totalSize += other.m_totalSize;
+        other.m_composed.clear();
+    }
+
+    std::vector<const IGenerator<T>*> m_composed;
+    std::string m_fileInfo;
+    size_t m_totalSize;
+};
+
+namespace Generators
+{
+    template<typename T>
+    CompositeGenerator<T> between( T from, T to ) {
+        CompositeGenerator<T> generators;
+        generators.add( new BetweenGenerator<T>( from, to ) );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2 ) {
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2, T val3 ){
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        valuesGen->add( val3 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        valuesGen->add( val3 );
+        valuesGen->add( val4 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+} // end namespace Generators
+
+using namespace Generators;
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_LINESTR2( line ) #line
+#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
+
+#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
+
+// #included from: internal/catch_interfaces_exception.h
+#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
+
+#include <string>
+#include <vector>
+
+// #included from: catch_interfaces_registry_hub.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class TestCase;
+    struct ITestCaseRegistry;
+    struct IExceptionTranslatorRegistry;
+    struct IExceptionTranslator;
+    struct IReporterRegistry;
+    struct IReporterFactory;
+
+    struct IRegistryHub {
+        virtual ~IRegistryHub();
+
+        virtual IReporterRegistry const& getReporterRegistry() const = 0;
+        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+        virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+    };
+
+    struct IMutableRegistryHub {
+        virtual ~IMutableRegistryHub();
+        virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
+        virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
+        virtual void registerTest( TestCase const& testInfo ) = 0;
+        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+    };
+
+    IRegistryHub& getRegistryHub();
+    IMutableRegistryHub& getMutableRegistryHub();
+    void cleanUp();
+    std::string translateActiveException();
+
+}
+
+namespace Catch {
+
+    typedef std::string(*exceptionTranslateFunction)();
+
+    struct IExceptionTranslator;
+    typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
+
+    struct IExceptionTranslator {
+        virtual ~IExceptionTranslator();
+        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+    };
+
+    struct IExceptionTranslatorRegistry {
+        virtual ~IExceptionTranslatorRegistry();
+
+        virtual std::string translateActiveException() const = 0;
+    };
+
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
+
+            ExceptionTranslator( std::string(*translateFunction)( T& ) )
+            : m_translateFunction( translateFunction )
+            {}
+
+            virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+                try {
+                    if( it == itEnd )
+                        throw;
+                    else
+                        return (*it)->translate( it+1, itEnd );
+                }
+                catch( T& ex ) {
+                    return m_translateFunction( ex );
+                }
+            }
+
+        protected:
+            std::string(*m_translateFunction)( T& );
+        };
+
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+            getMutableRegistryHub().registerTranslator
+                ( new ExceptionTranslator<T>( translateFunction ) );
+        }
+    };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+    static std::string translatorName( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+    static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+#include <type_traits>
+#endif
+
+namespace Catch {
+namespace Detail {
+
+    class Approx {
+    public:
+        explicit Approx ( double value )
+        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+            m_margin( 0.0 ),
+            m_scale( 1.0 ),
+            m_value( value )
+        {}
+
+        Approx( Approx const& other )
+        :   m_epsilon( other.m_epsilon ),
+            m_margin( other.m_margin ),
+            m_scale( other.m_scale ),
+            m_value( other.m_value )
+        {}
+
+        static Approx custom() {
+            return Approx( 0 );
+        }
+
+        Approx operator()( double value ) {
+            Approx approx( value );
+            approx.epsilon( m_epsilon );
+            approx.margin( m_margin );
+            approx.scale( m_scale );
+            return approx;
+        }
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator == ( const T& lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            auto lhs_v = double(lhs);
+            bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
+            if (relativeOK) {
+                return true;
+            }
+            return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator == ( Approx const& lhs, const T& rhs ) {
+            return operator==( rhs, lhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator != ( T lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator != ( Approx const& lhs, T rhs ) {
+            return !operator==( rhs, lhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator <= ( T lhs, Approx const& rhs )
+        {
+          return double(lhs) < rhs.m_value || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator <= ( Approx const& lhs, T rhs )
+        {
+          return lhs.m_value < double(rhs) || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator >= ( T lhs, Approx const& rhs )
+        {
+          return double(lhs) > rhs.m_value || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator >= ( Approx const& lhs, T rhs )
+        {
+          return lhs.m_value > double(rhs) || lhs == rhs;
+        }
+#else
+        friend bool operator == ( double lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+            if (relativeOK) {
+                return true;
+            }
+            return std::fabs(lhs - rhs.m_value) < rhs.m_margin;
+        }
+
+        friend bool operator == ( Approx const& lhs, double rhs ) {
+            return operator==( rhs, lhs );
+        }
+
+        friend bool operator != ( double lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
+
+        friend bool operator != ( Approx const& lhs, double rhs ) {
+            return !operator==( rhs, lhs );
+        }
+
+        friend bool operator <= ( double lhs, Approx const& rhs )
+        {
+          return lhs < rhs.m_value || lhs == rhs;
+        }
+
+        friend bool operator <= ( Approx const& lhs, double rhs )
+        {
+          return lhs.m_value < rhs || lhs == rhs;
+        }
+
+        friend bool operator >= ( double lhs, Approx const& rhs )
+        {
+          return lhs > rhs.m_value || lhs == rhs;
+        }
+
+        friend bool operator >= ( Approx const& lhs, double rhs )
+        {
+          return lhs.m_value > rhs || lhs == rhs;
+        }
+#endif
+
+        Approx& epsilon( double newEpsilon ) {
+            m_epsilon = newEpsilon;
+            return *this;
+        }
+
+        Approx& margin( double newMargin ) {
+            m_margin = newMargin;
+            return *this;
+        }
+
+        Approx& scale( double newScale ) {
+            m_scale = newScale;
+            return *this;
+        }
+
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << "Approx( " << Catch::toString( m_value ) << " )";
+            return oss.str();
+        }
+
+    private:
+        double m_epsilon;
+        double m_margin;
+        double m_scale;
+        double m_value;
+    };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+    return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers_string.h
+#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        struct CasedString
+        {
+            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+            std::string adjustString( std::string const& str ) const;
+            std::string caseSensitivitySuffix() const;
+
+            CaseSensitive::Choice m_caseSensitivity;
+            std::string m_str;
+        };
+
+        struct StringMatcherBase : MatcherBase<std::string> {
+            StringMatcherBase( std::string operation, CasedString const& comparator );
+            virtual std::string describe() const CATCH_OVERRIDE;
+
+            CasedString m_comparator;
+            std::string m_operation;
+        };
+
+        struct EqualsMatcher : StringMatcherBase {
+            EqualsMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct ContainsMatcher : StringMatcherBase {
+            ContainsMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct StartsWithMatcher : StringMatcherBase {
+            StartsWithMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct EndsWithMatcher : StringMatcherBase {
+            EndsWithMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+
+    } // namespace StdString
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_matchers_vector.h
+#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+    namespace Vector {
+
+        template<typename T>
+        struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
+
+            ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                return std::find(v.begin(), v.end(), m_comparator) != v.end();
+            }
+
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Contains: " + Catch::toString( m_comparator );
+            }
+
+            T const& m_comparator;
+        };
+
+        template<typename T>
+        struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+            ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                // !TBD: see note in EqualsMatcher
+                if (m_comparator.size() > v.size())
+                    return false;
+                for (size_t i = 0; i < m_comparator.size(); ++i)
+                    if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
+                        return false;
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Contains: " + Catch::toString( m_comparator );
+            }
+
+            std::vector<T> const& m_comparator;
+        };
+
+        template<typename T>
+        struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+            EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                // !TBD: This currently works if all elements can be compared using !=
+                // - a more general approach would be via a compare template that defaults
+                // to using !=. but could be specialised for, e.g. std::vector<T> etc
+                // - then just call that directly
+                if (m_comparator.size() != v.size())
+                    return false;
+                for (size_t i = 0; i < v.size(); ++i)
+                    if (m_comparator[i] != v[i])
+                        return false;
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Equals: " + Catch::toString( m_comparator );
+            }
+            std::vector<T> const& m_comparator;
+        };
+
+    } // namespace Vector
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    template<typename T>
+    Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+        return Vector::ContainsMatcher<T>( comparator );
+    }
+
+    template<typename T>
+    Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+        return Vector::ContainsElementMatcher<T>( comparator );
+    }
+
+    template<typename T>
+    Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+        return Vector::EqualsMatcher<T>( comparator );
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias {
+        TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+        std::string tag;
+        SourceLineInfo lineInfo;
+    };
+
+    struct RegistrarForTagAliases {
+        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+    };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
+// #included from: catch_option.hpp
+#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+
+namespace Catch {
+
+    // An optional type
+    template<typename T>
+    class Option {
+    public:
+        Option() : nullableValue( CATCH_NULL ) {}
+        Option( T const& _value )
+        : nullableValue( new( storage ) T( _value ) )
+        {}
+        Option( Option const& _other )
+        : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL )
+        {}
+
+        ~Option() {
+            reset();
+        }
+
+        Option& operator= ( Option const& _other ) {
+            if( &_other != this ) {
+                reset();
+                if( _other )
+                    nullableValue = new( storage ) T( *_other );
+            }
+            return *this;
+        }
+        Option& operator = ( T const& _value ) {
+            reset();
+            nullableValue = new( storage ) T( _value );
+            return *this;
+        }
+
+        void reset() {
+            if( nullableValue )
+                nullableValue->~T();
+            nullableValue = CATCH_NULL;
+        }
+
+        T& operator*() { return *nullableValue; }
+        T const& operator*() const { return *nullableValue; }
+        T* operator->() { return nullableValue; }
+        const T* operator->() const { return nullableValue; }
+
+        T valueOr( T const& defaultValue ) const {
+            return nullableValue ? *nullableValue : defaultValue;
+        }
+
+        bool some() const { return nullableValue != CATCH_NULL; }
+        bool none() const { return nullableValue == CATCH_NULL; }
+
+        bool operator !() const { return nullableValue == CATCH_NULL; }
+        operator SafeBool::type() const {
+            return SafeBool::makeSafe( some() );
+        }
+
+    private:
+        T* nullableValue;
+        char storage[sizeof(T)];
+    };
+
+} // end namespace Catch
+
+namespace Catch {
+
+    struct ITagAliasRegistry {
+        virtual ~ITagAliasRegistry();
+        virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+        static ITagAliasRegistry const& get();
+    };
+
+} // end namespace Catch
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// #included from: internal/catch_test_case_info.h
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    struct ITestCase;
+
+    struct TestCaseInfo {
+        enum SpecialProperties{
+            None = 0,
+            IsHidden = 1 << 1,
+            ShouldFail = 1 << 2,
+            MayFail = 1 << 3,
+            Throws = 1 << 4,
+            NonPortable = 1 << 5
+        };
+
+        TestCaseInfo(   std::string const& _name,
+                        std::string const& _className,
+                        std::string const& _description,
+                        std::set<std::string> const& _tags,
+                        SourceLineInfo const& _lineInfo );
+
+        TestCaseInfo( TestCaseInfo const& other );
+
+        friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+
+        bool isHidden() const;
+        bool throws() const;
+        bool okToFail() const;
+        bool expectedToFail() const;
+
+        std::string name;
+        std::string className;
+        std::string description;
+        std::set<std::string> tags;
+        std::set<std::string> lcaseTags;
+        std::string tagsAsString;
+        SourceLineInfo lineInfo;
+        SpecialProperties properties;
+    };
+
+    class TestCase : public TestCaseInfo {
+    public:
+
+        TestCase( ITestCase* testCase, TestCaseInfo const& info );
+        TestCase( TestCase const& other );
+
+        TestCase withName( std::string const& _newName ) const;
+
+        void invoke() const;
+
+        TestCaseInfo const& getTestCaseInfo() const;
+
+        void swap( TestCase& other );
+        bool operator == ( TestCase const& other ) const;
+        bool operator < ( TestCase const& other ) const;
+        TestCase& operator = ( TestCase const& other );
+
+    private:
+        Ptr<ITestCase> test;
+    };
+
+    TestCase makeTestCase(  ITestCase* testCase,
+                            std::string const& className,
+                            std::string const& name,
+                            std::string const& description,
+                            SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+@protocol OcFixture
+
+@optional
+
+-(void) setUp;
+-(void) tearDown;
+
+@end
+
+namespace Catch {
+
+    class OcMethod : public SharedImpl<ITestCase> {
+
+    public:
+        OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+        virtual void invoke() const {
+            id obj = [[m_cls alloc] init];
+
+            performOptionalSelector( obj, @selector(setUp)  );
+            performOptionalSelector( obj, m_sel );
+            performOptionalSelector( obj, @selector(tearDown)  );
+
+            arcSafeRelease( obj );
+        }
+    private:
+        virtual ~OcMethod() {}
+
+        Class m_cls;
+        SEL m_sel;
+    };
+
+    namespace Detail{
+
+        inline std::string getAnnotation(   Class cls,
+                                            std::string const& annotationName,
+                                            std::string const& testCaseName ) {
+            NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+            SEL sel = NSSelectorFromString( selStr );
+            arcSafeRelease( selStr );
+            id value = performOptionalSelector( cls, sel );
+            if( value )
+                return [(NSString*)value UTF8String];
+            return "";
+        }
+    }
+
+    inline size_t registerTestMethods() {
+        size_t noTestMethods = 0;
+        int noClasses = objc_getClassList( CATCH_NULL, 0 );
+
+        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+        objc_getClassList( classes, noClasses );
+
+        for( int c = 0; c < noClasses; c++ ) {
+            Class cls = classes[c];
+            {
+                u_int count;
+                Method* methods = class_copyMethodList( cls, &count );
+                for( u_int m = 0; m < count ; m++ ) {
+                    SEL selector = method_getName(methods[m]);
+                    std::string methodName = sel_getName(selector);
+                    if( startsWith( methodName, "Catch_TestCase_" ) ) {
+                        std::string testCaseName = methodName.substr( 15 );
+                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+                        const char* className = class_getName( cls );
+
+                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+                        noTestMethods++;
+                    }
+                }
+                free(methods);
+            }
+        }
+        return noTestMethods;
+    }
+
+    namespace Matchers {
+        namespace Impl {
+        namespace NSStringMatchers {
+
+            template<typename MatcherT>
+            struct StringHolder : MatcherImpl<MatcherT, NSString*>{
+                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+                StringHolder() {
+                    arcSafeRelease( m_substr );
+                }
+
+                NSString* m_substr;
+            };
+
+            struct Equals : StringHolder<Equals> {
+                Equals( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str isEqualToString:m_substr];
+                }
+
+                virtual std::string toString() const {
+                    return "equals string: " + Catch::toString( m_substr );
+                }
+            };
+
+            struct Contains : StringHolder<Contains> {
+                Contains( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location != NSNotFound;
+                }
+
+                virtual std::string toString() const {
+                    return "contains string: " + Catch::toString( m_substr );
+                }
+            };
+
+            struct StartsWith : StringHolder<StartsWith> {
+                StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == 0;
+                }
+
+                virtual std::string toString() const {
+                    return "starts with: " + Catch::toString( m_substr );
+                }
+            };
+            struct EndsWith : StringHolder<EndsWith> {
+                EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+                }
+
+                virtual std::string toString() const {
+                    return "ends with: " + Catch::toString( m_substr );
+                }
+            };
+
+        } // namespace NSStringMatchers
+        } // namespace Impl
+
+        inline Impl::NSStringMatchers::Equals
+            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+        inline Impl::NSStringMatchers::Contains
+            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+        inline Impl::NSStringMatchers::StartsWith
+            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+        inline Impl::NSStringMatchers::EndsWith
+            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+    } // namespace Matchers
+
+    using namespace Matchers;
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_TEST_CASE( name, desc )\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
+{\
+return @ name; \
+}\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
+{ \
+return @ desc; \
+} \
+-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
+
+#endif
+
+#ifdef CATCH_IMPL
+
+// !TBD: Move the leak detector code into a separate header
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+class LeakDetector {
+public:
+	LeakDetector() {
+		int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+		flag |= _CRTDBG_LEAK_CHECK_DF;
+		flag |= _CRTDBG_ALLOC_MEM_DF;
+		_CrtSetDbgFlag(flag);
+		_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+		_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+		// Change this to leaking allocation's number to break there
+		_CrtSetBreakAlloc(-1);
+	}
+};
+#else
+class LeakDetector {};
+#endif
+
+LeakDetector leakDetector;
+
+// #included from: internal/catch_impl.hpp
+#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
+
+// Collect all the implementation files together here
+// These are the equivalent of what would usually be cpp files
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: ../catch_session.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_wildcard_pattern.hpp
+#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+#include <stdexcept>
+
+namespace Catch
+{
+    class WildcardPattern {
+        enum WildcardPosition {
+            NoWildcard = 0,
+            WildcardAtStart = 1,
+            WildcardAtEnd = 2,
+            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+        };
+
+    public:
+
+        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_wildcard( NoWildcard ),
+            m_pattern( adjustCase( pattern ) )
+        {
+            if( startsWith( m_pattern, '*' ) ) {
+                m_pattern = m_pattern.substr( 1 );
+                m_wildcard = WildcardAtStart;
+            }
+            if( endsWith( m_pattern, '*' ) ) {
+                m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+                m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+            }
+        }
+        virtual ~WildcardPattern();
+        virtual bool matches( std::string const& str ) const {
+            switch( m_wildcard ) {
+                case NoWildcard:
+                    return m_pattern == adjustCase( str );
+                case WildcardAtStart:
+                    return endsWith( adjustCase( str ), m_pattern );
+                case WildcardAtEnd:
+                    return startsWith( adjustCase( str ), m_pattern );
+                case WildcardAtBothEnds:
+                    return contains( adjustCase( str ), m_pattern );
+            }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+            throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+        }
+    private:
+        std::string adjustCase( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+        }
+        CaseSensitive::Choice m_caseSensitivity;
+        WildcardPosition m_wildcard;
+        std::string m_pattern;
+    };
+}
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec {
+        struct Pattern : SharedImpl<> {
+            virtual ~Pattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+        };
+        class NamePattern : public Pattern {
+        public:
+            NamePattern( std::string const& name )
+            : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+            {}
+            virtual ~NamePattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const {
+                return m_wildcardPattern.matches( toLower( testCase.name ) );
+            }
+        private:
+            WildcardPattern m_wildcardPattern;
+        };
+
+        class TagPattern : public Pattern {
+        public:
+            TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+            virtual ~TagPattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const {
+                return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
+            }
+        private:
+            std::string m_tag;
+        };
+
+        class ExcludedPattern : public Pattern {
+        public:
+            ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+            virtual ~ExcludedPattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+        private:
+            Ptr<Pattern> m_underlyingPattern;
+        };
+
+        struct Filter {
+            std::vector<Ptr<Pattern> > m_patterns;
+
+            bool matches( TestCaseInfo const& testCase ) const {
+                // All patterns in a filter must match for the filter to be a match
+                for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) {
+                    if( !(*it)->matches( testCase ) )
+                        return false;
+                }
+                return true;
+            }
+        };
+
+    public:
+        bool hasFilters() const {
+            return !m_filters.empty();
+        }
+        bool matches( TestCaseInfo const& testCase ) const {
+            // A TestSpec matches if any filter matches
+            for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
+                if( it->matches( testCase ) )
+                    return true;
+            return false;
+        }
+
+    private:
+        std::vector<Filter> m_filters;
+
+        friend class TestSpecParser;
+    };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+    class TestSpecParser {
+        enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+        Mode m_mode;
+        bool m_exclusion;
+        std::size_t m_start, m_pos;
+        std::string m_arg;
+        std::vector<std::size_t> m_escapeChars;
+        TestSpec::Filter m_currentFilter;
+        TestSpec m_testSpec;
+        ITagAliasRegistry const* m_tagAliases;
+
+    public:
+        TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+        TestSpecParser& parse( std::string const& arg ) {
+            m_mode = None;
+            m_exclusion = false;
+            m_start = std::string::npos;
+            m_arg = m_tagAliases->expandAliases( arg );
+            m_escapeChars.clear();
+            for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+                visitChar( m_arg[m_pos] );
+            if( m_mode == Name )
+                addPattern<TestSpec::NamePattern>();
+            return *this;
+        }
+        TestSpec testSpec() {
+            addFilter();
+            return m_testSpec;
+        }
+    private:
+        void visitChar( char c ) {
+            if( m_mode == None ) {
+                switch( c ) {
+                case ' ': return;
+                case '~': m_exclusion = true; return;
+                case '[': return startNewMode( Tag, ++m_pos );
+                case '"': return startNewMode( QuotedName, ++m_pos );
+                case '\\': return escape();
+                default: startNewMode( Name, m_pos ); break;
+                }
+            }
+            if( m_mode == Name ) {
+                if( c == ',' ) {
+                    addPattern<TestSpec::NamePattern>();
+                    addFilter();
+                }
+                else if( c == '[' ) {
+                    if( subString() == "exclude:" )
+                        m_exclusion = true;
+                    else
+                        addPattern<TestSpec::NamePattern>();
+                    startNewMode( Tag, ++m_pos );
+                }
+                else if( c == '\\' )
+                    escape();
+            }
+            else if( m_mode == EscapedName )
+                m_mode = Name;
+            else if( m_mode == QuotedName && c == '"' )
+                addPattern<TestSpec::NamePattern>();
+            else if( m_mode == Tag && c == ']' )
+                addPattern<TestSpec::TagPattern>();
+        }
+        void startNewMode( Mode mode, std::size_t start ) {
+            m_mode = mode;
+            m_start = start;
+        }
+        void escape() {
+            if( m_mode == None )
+                m_start = m_pos;
+            m_mode = EscapedName;
+            m_escapeChars.push_back( m_pos );
+        }
+        std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+        template<typename T>
+        void addPattern() {
+            std::string token = subString();
+            for( size_t i = 0; i < m_escapeChars.size(); ++i )
+                token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+            m_escapeChars.clear();
+            if( startsWith( token, "exclude:" ) ) {
+                m_exclusion = true;
+                token = token.substr( 8 );
+            }
+            if( !token.empty() ) {
+                Ptr<TestSpec::Pattern> pattern = new T( token );
+                if( m_exclusion )
+                    pattern = new TestSpec::ExcludedPattern( pattern );
+                m_currentFilter.m_patterns.push_back( pattern );
+            }
+            m_exclusion = false;
+            m_mode = None;
+        }
+        void addFilter() {
+            if( !m_currentFilter.m_patterns.empty() ) {
+                m_testSpec.m_filters.push_back( m_currentFilter );
+                m_currentFilter = TestSpec::Filter();
+            }
+        }
+    };
+    inline TestSpec parseTestSpec( std::string const& arg ) {
+        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+    }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    struct Verbosity { enum Level {
+        NoOutput = 0,
+        Quiet,
+        Normal
+    }; };
+
+    struct WarnAbout { enum What {
+        Nothing = 0x00,
+        NoAssertions = 0x01
+    }; };
+
+    struct ShowDurations { enum OrNot {
+        DefaultForReporter,
+        Always,
+        Never
+    }; };
+    struct RunTests { enum InWhatOrder {
+        InDeclarationOrder,
+        InLexicographicalOrder,
+        InRandomOrder
+    }; };
+    struct UseColour { enum YesOrNo {
+        Auto,
+        Yes,
+        No
+    }; };
+
+    class TestSpec;
+
+    struct IConfig : IShared {
+
+        virtual ~IConfig();
+
+        virtual bool allowThrows() const = 0;
+        virtual std::ostream& stream() const = 0;
+        virtual std::string name() const = 0;
+        virtual bool includeSuccessfulResults() const = 0;
+        virtual bool shouldDebugBreak() const = 0;
+        virtual bool warnAboutMissingAssertions() const = 0;
+        virtual int abortAfter() const = 0;
+        virtual bool showInvisibles() const = 0;
+        virtual ShowDurations::OrNot showDurations() const = 0;
+        virtual TestSpec const& testSpec() const = 0;
+        virtual RunTests::InWhatOrder runOrder() const = 0;
+        virtual unsigned int rngSeed() const = 0;
+        virtual UseColour::YesOrNo useColour() const = 0;
+        virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+
+    };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+    class StreamBufBase : public std::streambuf {
+    public:
+        virtual ~StreamBufBase() CATCH_NOEXCEPT;
+    };
+}
+
+#include <streambuf>
+#include <ostream>
+#include <fstream>
+#include <memory>
+
+namespace Catch {
+
+    std::ostream& cout();
+    std::ostream& cerr();
+
+    struct IStream {
+        virtual ~IStream() CATCH_NOEXCEPT;
+        virtual std::ostream& stream() const = 0;
+    };
+
+    class FileStream : public IStream {
+        mutable std::ofstream m_ofs;
+    public:
+        FileStream( std::string const& filename );
+        virtual ~FileStream() CATCH_NOEXCEPT;
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class CoutStream : public IStream {
+        mutable std::ostream m_os;
+    public:
+        CoutStream();
+        virtual ~CoutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class DebugOutStream : public IStream {
+        CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf;
+        mutable std::ostream m_os;
+    public:
+        DebugOutStream();
+        virtual ~DebugOutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <stdexcept>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+    struct ConfigData {
+
+        ConfigData()
+        :   listTests( false ),
+            listTags( false ),
+            listReporters( false ),
+            listTestNamesOnly( false ),
+            showSuccessfulTests( false ),
+            shouldDebugBreak( false ),
+            noThrow( false ),
+            showHelp( false ),
+            showInvisibles( false ),
+            filenamesAsTags( false ),
+            abortAfter( -1 ),
+            rngSeed( 0 ),
+            verbosity( Verbosity::Normal ),
+            warnings( WarnAbout::Nothing ),
+            showDurations( ShowDurations::DefaultForReporter ),
+            runOrder( RunTests::InDeclarationOrder ),
+            useColour( UseColour::Auto )
+        {}
+
+        bool listTests;
+        bool listTags;
+        bool listReporters;
+        bool listTestNamesOnly;
+
+        bool showSuccessfulTests;
+        bool shouldDebugBreak;
+        bool noThrow;
+        bool showHelp;
+        bool showInvisibles;
+        bool filenamesAsTags;
+
+        int abortAfter;
+        unsigned int rngSeed;
+
+        Verbosity::Level verbosity;
+        WarnAbout::What warnings;
+        ShowDurations::OrNot showDurations;
+        RunTests::InWhatOrder runOrder;
+        UseColour::YesOrNo useColour;
+
+        std::string outputFilename;
+        std::string name;
+        std::string processName;
+
+        std::vector<std::string> reporterNames;
+        std::vector<std::string> testsOrTags;
+        std::vector<std::string> sectionsToRun;
+    };
+
+    class Config : public SharedImpl<IConfig> {
+    private:
+        Config( Config const& other );
+        Config& operator = ( Config const& other );
+        virtual void dummy();
+    public:
+
+        Config()
+        {}
+
+        Config( ConfigData const& data )
+        :   m_data( data ),
+            m_stream( openStream() )
+        {
+            if( !data.testsOrTags.empty() ) {
+                TestSpecParser parser( ITagAliasRegistry::get() );
+                for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
+                    parser.parse( data.testsOrTags[i] );
+                m_testSpec = parser.testSpec();
+            }
+        }
+
+        virtual ~Config() {}
+
+        std::string const& getFilename() const {
+            return m_data.outputFilename ;
+        }
+
+        bool listTests() const { return m_data.listTests; }
+        bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+        bool listTags() const { return m_data.listTags; }
+        bool listReporters() const { return m_data.listReporters; }
+
+        std::string getProcessName() const { return m_data.processName; }
+
+        std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; }
+        std::vector<std::string> const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; }
+
+        virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; }
+
+        bool showHelp() const { return m_data.showHelp; }
+
+        // IConfig interface
+        virtual bool allowThrows() const CATCH_OVERRIDE                 { return !m_data.noThrow; }
+        virtual std::ostream& stream() const CATCH_OVERRIDE             { return m_stream->stream(); }
+        virtual std::string name() const CATCH_OVERRIDE                 { return m_data.name.empty() ? m_data.processName : m_data.name; }
+        virtual bool includeSuccessfulResults() const CATCH_OVERRIDE    { return m_data.showSuccessfulTests; }
+        virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE  { return m_data.warnings & WarnAbout::NoAssertions; }
+        virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; }
+        virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE   { return m_data.runOrder; }
+        virtual unsigned int rngSeed() const CATCH_OVERRIDE             { return m_data.rngSeed; }
+        virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE     { return m_data.useColour; }
+        virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; }
+        virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; }
+        virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; }
+
+    private:
+
+        IStream const* openStream() {
+            if( m_data.outputFilename.empty() )
+                return new CoutStream();
+            else if( m_data.outputFilename[0] == '%' ) {
+                if( m_data.outputFilename == "%debug" )
+                    return new DebugOutStream();
+                else
+                    throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
+            }
+            else
+                return new FileStream( m_data.outputFilename );
+        }
+        ConfigData m_data;
+
+        CATCH_AUTO_PTR( IStream const ) m_stream;
+        TestSpec m_testSpec;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_clara.h
+#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
+#undef CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+// Declare Clara inside the Catch namespace
+#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
+// #included from: ../external/clara.h
+
+// Version 0.0.2.4
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#ifndef STITCH_CLARA_OPEN_NAMESPACE
+#define TWOBLUECUBES_CLARA_H_INCLUDED
+#define STITCH_CLARA_OPEN_NAMESPACE
+#define STITCH_CLARA_CLOSE_NAMESPACE
+#else
+#define STITCH_CLARA_CLOSE_NAMESPACE }
+#endif
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
+#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#define TBC_TEXT_FORMAT_H_INCLUDED
+#endif
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <algorithm>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+    struct TextAttributes {
+        TextAttributes()
+        :   initialIndent( std::string::npos ),
+            indent( 0 ),
+            width( consoleWidth-1 ),
+            tabChar( '\t' )
+        {}
+
+        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
+        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
+        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
+        TextAttributes& setTabChar( char _value )               { tabChar = _value; return *this; }
+
+        std::size_t initialIndent;  // indent of first line, or npos
+        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
+        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
+        char tabChar;               // If this char is seen the indent is changed to current pos
+    };
+
+    class Text {
+    public:
+        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+        : attr( _attr )
+        {
+            std::string wrappableChars = " [({.,/|\\-";
+            std::size_t indent = _attr.initialIndent != std::string::npos
+                ? _attr.initialIndent
+                : _attr.indent;
+            std::string remainder = _str;
+
+            while( !remainder.empty() ) {
+                if( lines.size() >= 1000 ) {
+                    lines.push_back( "... message truncated due to excessive size" );
+                    return;
+                }
+                std::size_t tabPos = std::string::npos;
+                std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+                std::size_t pos = remainder.find_first_of( '\n' );
+                if( pos <= width ) {
+                    width = pos;
+                }
+                pos = remainder.find_last_of( _attr.tabChar, width );
+                if( pos != std::string::npos ) {
+                    tabPos = pos;
+                    if( remainder[width] == '\n' )
+                        width--;
+                    remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+                }
+
+                if( width == remainder.size() ) {
+                    spliceLine( indent, remainder, width );
+                }
+                else if( remainder[width] == '\n' ) {
+                    spliceLine( indent, remainder, width );
+                    if( width <= 1 || remainder.size() != 1 )
+                        remainder = remainder.substr( 1 );
+                    indent = _attr.indent;
+                }
+                else {
+                    pos = remainder.find_last_of( wrappableChars, width );
+                    if( pos != std::string::npos && pos > 0 ) {
+                        spliceLine( indent, remainder, pos );
+                        if( remainder[0] == ' ' )
+                            remainder = remainder.substr( 1 );
+                    }
+                    else {
+                        spliceLine( indent, remainder, width-1 );
+                        lines.back() += "-";
+                    }
+                    if( lines.size() == 1 )
+                        indent = _attr.indent;
+                    if( tabPos != std::string::npos )
+                        indent += tabPos;
+                }
+            }
+        }
+
+        void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+            lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+            _remainder = _remainder.substr( _pos );
+        }
+
+        typedef std::vector<std::string>::const_iterator const_iterator;
+
+        const_iterator begin() const { return lines.begin(); }
+        const_iterator end() const { return lines.end(); }
+        std::string const& last() const { return lines.back(); }
+        std::size_t size() const { return lines.size(); }
+        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << *this;
+            return oss.str();
+        }
+
+        inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+                it != itEnd; ++it ) {
+                if( it != _text.begin() )
+                    _stream << "\n";
+                _stream << *it;
+            }
+            return _stream;
+        }
+
+    private:
+        std::string str;
+        TextAttributes attr;
+        std::vector<std::string> lines;
+    };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+// ----------- #included from clara_compilers.h -----------
+
+#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CLARA_CONFIG_CPP11_OVERRIDE : is override supported?
+// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+
+// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11
+
+#ifdef __clang__
+
+#if __has_feature(cxx_nullptr)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#if __has_feature(cxx_noexcept)
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+
+#define CLARA_CPP11_OR_GREATER
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE
+#endif
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+// noexcept support:
+#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT)
+#define CLARA_NOEXCEPT noexcept
+#  define CLARA_NOEXCEPT_IS(x) noexcept(x)
+#else
+#define CLARA_NOEXCEPT throw()
+#  define CLARA_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CLARA_CONFIG_CPP11_NULLPTR
+#define CLARA_NULL nullptr
+#else
+#define CLARA_NULL NULL
+#endif
+
+// override support
+#ifdef CLARA_CONFIG_CPP11_OVERRIDE
+#define CLARA_OVERRIDE override
+#else
+#define CLARA_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR
+#   define CLARA_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CLARA_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// ----------- end of #include from clara_compilers.h -----------
+// ........... back in clara.h
+
+#include <map>
+#include <stdexcept>
+#include <memory>
+
+#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CLARA_PLATFORM_WINDOWS
+#endif
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+    struct UnpositionalTag {};
+
+    extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+    UnpositionalTag _;
+#endif
+
+    namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+    const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+        using namespace Tbc;
+
+        inline bool startsWith( std::string const& str, std::string const& prefix ) {
+            return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+        }
+
+        template<typename T> struct RemoveConstRef{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+
+        template<typename T>    struct IsBool       { static const bool value = false; };
+        template<>              struct IsBool<bool> { static const bool value = true; };
+
+        template<typename T>
+        void convertInto( std::string const& _source, T& _dest ) {
+            std::stringstream ss;
+            ss << _source;
+            ss >> _dest;
+            if( ss.fail() )
+                throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
+        }
+        inline void convertInto( std::string const& _source, std::string& _dest ) {
+            _dest = _source;
+        }
+        char toLowerCh(char c) {
+            return static_cast<char>( ::tolower( c ) );
+        }
+        inline void convertInto( std::string const& _source, bool& _dest ) {
+            std::string sourceLC = _source;
+            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh );
+            if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
+                _dest = true;
+            else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
+                _dest = false;
+            else
+                throw std::runtime_error( "Expected a boolean value but did not recognise:\n  '" + _source + "'" );
+        }
+
+        template<typename ConfigT>
+        struct IArgFunction {
+            virtual ~IArgFunction() {}
+#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS
+            IArgFunction()                      = default;
+            IArgFunction( IArgFunction const& ) = default;
+#endif
+            virtual void set( ConfigT& config, std::string const& value ) const = 0;
+            virtual bool takesArg() const = 0;
+            virtual IArgFunction* clone() const = 0;
+        };
+
+        template<typename ConfigT>
+        class BoundArgFunction {
+        public:
+            BoundArgFunction() : functionObj( CLARA_NULL ) {}
+            BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {}
+            BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL;
+                delete functionObj;
+                functionObj = newFunctionObj;
+                return *this;
+            }
+            ~BoundArgFunction() { delete functionObj; }
+
+            void set( ConfigT& config, std::string const& value ) const {
+                functionObj->set( config, value );
+            }
+            bool takesArg() const { return functionObj->takesArg(); }
+
+            bool isSet() const {
+                return functionObj != CLARA_NULL;
+            }
+        private:
+            IArgFunction<ConfigT>* functionObj;
+        };
+
+        template<typename C>
+        struct NullBinder : IArgFunction<C>{
+            virtual void set( C&, std::string const& ) const {}
+            virtual bool takesArg() const { return true; }
+            virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
+        };
+
+        template<typename C, typename M>
+        struct BoundDataMember : IArgFunction<C>{
+            BoundDataMember( M C::* _member ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                convertInto( stringValue, p.*member );
+            }
+            virtual bool takesArg() const { return !IsBool<M>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
+            M C::* member;
+        };
+        template<typename C, typename M>
+        struct BoundUnaryMethod : IArgFunction<C>{
+            BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                typename RemoveConstRef<M>::type value;
+                convertInto( stringValue, value );
+                (p.*member)( value );
+            }
+            virtual bool takesArg() const { return !IsBool<M>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
+            void (C::*member)( M );
+        };
+        template<typename C>
+        struct BoundNullaryMethod : IArgFunction<C>{
+            BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                bool value;
+                convertInto( stringValue, value );
+                if( value )
+                    (p.*member)();
+            }
+            virtual bool takesArg() const { return false; }
+            virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+            void (C::*member)();
+        };
+
+        template<typename C>
+        struct BoundUnaryFunction : IArgFunction<C>{
+            BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
+            virtual void set( C& obj, std::string const& stringValue ) const {
+                bool value;
+                convertInto( stringValue, value );
+                if( value )
+                    function( obj );
+            }
+            virtual bool takesArg() const { return false; }
+            virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+            void (*function)( C& );
+        };
+
+        template<typename C, typename T>
+        struct BoundBinaryFunction : IArgFunction<C>{
+            BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
+            virtual void set( C& obj, std::string const& stringValue ) const {
+                typename RemoveConstRef<T>::type value;
+                convertInto( stringValue, value );
+                function( obj, value );
+            }
+            virtual bool takesArg() const { return !IsBool<T>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+            void (*function)( C&, T );
+        };
+
+    } // namespace Detail
+
+    inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
+        std::vector<std::string> args( static_cast<std::size_t>( argc ) );
+        for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
+            args[i] = argv[i];
+
+        return args;
+    }
+
+    class Parser {
+        enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
+        Mode mode;
+        std::size_t from;
+        bool inQuotes;
+    public:
+
+        struct Token {
+            enum Type { Positional, ShortOpt, LongOpt };
+            Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+            Type type;
+            std::string data;
+        };
+
+        Parser() : mode( None ), from( 0 ), inQuotes( false ){}
+
+        void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
+            const std::string doubleDash = "--";
+            for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
+                parseIntoTokens( args[i], tokens);
+        }
+
+        void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
+            for( std::size_t i = 0; i <= arg.size(); ++i ) {
+                char c = arg[i];
+                if( c == '"' )
+                    inQuotes = !inQuotes;
+                mode = handleMode( i, c, arg, tokens );
+            }
+        }
+        Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            switch( mode ) {
+                case None: return handleNone( i, c );
+                case MaybeShortOpt: return handleMaybeShortOpt( i, c );
+                case ShortOpt:
+                case LongOpt:
+                case SlashOpt: return handleOpt( i, c, arg, tokens );
+                case Positional: return handlePositional( i, c, arg, tokens );
+                default: throw std::logic_error( "Unknown mode" );
+            }
+        }
+
+        Mode handleNone( std::size_t i, char c ) {
+            if( inQuotes ) {
+                from = i;
+                return Positional;
+            }
+            switch( c ) {
+                case '-': return MaybeShortOpt;
+#ifdef CLARA_PLATFORM_WINDOWS
+                case '/': from = i+1; return SlashOpt;
+#endif
+                default: from = i; return Positional;
+            }
+        }
+        Mode handleMaybeShortOpt( std::size_t i, char c ) {
+            switch( c ) {
+                case '-': from = i+1; return LongOpt;
+                default: from = i; return ShortOpt;
+            }
+        }
+        Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            if( std::string( ":=\0", 3 ).find( c ) == std::string::npos )
+                return mode;
+
+            std::string optName = arg.substr( from, i-from );
+            if( mode == ShortOpt )
+                for( std::size_t j = 0; j < optName.size(); ++j )
+                    tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
+            else if( mode == SlashOpt && optName.size() == 1 )
+                tokens.push_back( Token( Token::ShortOpt, optName ) );
+            else
+                tokens.push_back( Token( Token::LongOpt, optName ) );
+            return None;
+        }
+        Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos )
+                return mode;
+
+            std::string data = arg.substr( from, i-from );
+            tokens.push_back( Token( Token::Positional, data ) );
+            return None;
+        }
+    };
+
+    template<typename ConfigT>
+    struct CommonArgProperties {
+        CommonArgProperties() {}
+        CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+        Detail::BoundArgFunction<ConfigT> boundField;
+        std::string description;
+        std::string detail;
+        std::string placeholder; // Only value if boundField takes an arg
+
+        bool takesArg() const {
+            return !placeholder.empty();
+        }
+        void validate() const {
+            if( !boundField.isSet() )
+                throw std::logic_error( "option not bound" );
+        }
+    };
+    struct OptionArgProperties {
+        std::vector<std::string> shortNames;
+        std::string longName;
+
+        bool hasShortName( std::string const& shortName ) const {
+            return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
+        }
+        bool hasLongName( std::string const& _longName ) const {
+            return _longName == longName;
+        }
+    };
+    struct PositionalArgProperties {
+        PositionalArgProperties() : position( -1 ) {}
+        int position; // -1 means non-positional (floating)
+
+        bool isFixedPositional() const {
+            return position != -1;
+        }
+    };
+
+    template<typename ConfigT>
+    class CommandLine {
+
+        struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+            Arg() {}
+            Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+            using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+            std::string dbgName() const {
+                if( !longName.empty() )
+                    return "--" + longName;
+                if( !shortNames.empty() )
+                    return "-" + shortNames[0];
+                return "positional args";
+            }
+            std::string commands() const {
+                std::ostringstream oss;
+                bool first = true;
+                std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
+                for(; it != itEnd; ++it ) {
+                    if( first )
+                        first = false;
+                    else
+                        oss << ", ";
+                    oss << "-" << *it;
+                }
+                if( !longName.empty() ) {
+                    if( !first )
+                        oss << ", ";
+                    oss << "--" << longName;
+                }
+                if( !placeholder.empty() )
+                    oss << " <" << placeholder << ">";
+                return oss.str();
+            }
+        };
+
+        typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr;
+
+        friend void addOptName( Arg& arg, std::string const& optName )
+        {
+            if( optName.empty() )
+                return;
+            if( Detail::startsWith( optName, "--" ) ) {
+                if( !arg.longName.empty() )
+                    throw std::logic_error( "Only one long opt may be specified. '"
+                        + arg.longName
+                        + "' already specified, now attempting to add '"
+                        + optName + "'" );
+                arg.longName = optName.substr( 2 );
+            }
+            else if( Detail::startsWith( optName, "-" ) )
+                arg.shortNames.push_back( optName.substr( 1 ) );
+            else
+                throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
+        }
+        friend void setPositionalArg( Arg& arg, int position )
+        {
+            arg.position = position;
+        }
+
+        class ArgBuilder {
+        public:
+            ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+            // Bind a non-boolean data member (requires placeholder string)
+            template<typename C, typename M>
+            void bind( M C::* field, std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
+                m_arg->placeholder = placeholder;
+            }
+            // Bind a boolean data member (no placeholder required)
+            template<typename C>
+            void bind( bool C::* field ) {
+                m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
+            }
+
+            // Bind a method taking a single, non-boolean argument (requires a placeholder string)
+            template<typename C, typename M>
+            void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
+                m_arg->placeholder = placeholder;
+            }
+
+            // Bind a method taking a single, boolean argument (no placeholder string required)
+            template<typename C>
+            void bind( void (C::* unaryMethod)( bool ) ) {
+                m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
+            }
+
+            // Bind a method that takes no arguments (will be called if opt is present)
+            template<typename C>
+            void bind( void (C::* nullaryMethod)() ) {
+                m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
+            }
+
+            // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
+            template<typename C>
+            void bind( void (* unaryFunction)( C& ) ) {
+                m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
+            }
+
+            // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
+            template<typename C, typename T>
+            void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
+                m_arg->placeholder = placeholder;
+            }
+
+            ArgBuilder& describe( std::string const& description ) {
+                m_arg->description = description;
+                return *this;
+            }
+            ArgBuilder& detail( std::string const& detail ) {
+                m_arg->detail = detail;
+                return *this;
+            }
+
+        protected:
+            Arg* m_arg;
+        };
+
+        class OptBuilder : public ArgBuilder {
+        public:
+            OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+            OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+            OptBuilder& operator[]( std::string const& optName ) {
+                addOptName( *ArgBuilder::m_arg, optName );
+                return *this;
+            }
+        };
+
+    public:
+
+        CommandLine()
+        :   m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
+            m_highestSpecifiedArgPosition( 0 ),
+            m_throwOnUnrecognisedTokens( false )
+        {}
+        CommandLine( CommandLine const& other )
+        :   m_boundProcessName( other.m_boundProcessName ),
+            m_options ( other.m_options ),
+            m_positionalArgs( other.m_positionalArgs ),
+            m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
+            m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
+        {
+            if( other.m_floatingArg.get() )
+                m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
+        }
+
+        CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+            m_throwOnUnrecognisedTokens = shouldThrow;
+            return *this;
+        }
+
+        OptBuilder operator[]( std::string const& optName ) {
+            m_options.push_back( Arg() );
+            addOptName( m_options.back(), optName );
+            OptBuilder builder( &m_options.back() );
+            return builder;
+        }
+
+        ArgBuilder operator[]( int position ) {
+            m_positionalArgs.insert( std::make_pair( position, Arg() ) );
+            if( position > m_highestSpecifiedArgPosition )
+                m_highestSpecifiedArgPosition = position;
+            setPositionalArg( m_positionalArgs[position], position );
+            ArgBuilder builder( &m_positionalArgs[position] );
+            return builder;
+        }
+
+        // Invoke this with the _ instance
+        ArgBuilder operator[]( UnpositionalTag ) {
+            if( m_floatingArg.get() )
+                throw std::logic_error( "Only one unpositional argument can be added" );
+            m_floatingArg.reset( new Arg() );
+            ArgBuilder builder( m_floatingArg.get() );
+            return builder;
+        }
+
+        template<typename C, typename M>
+        void bindProcessName( M C::* field ) {
+            m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
+        }
+        template<typename C, typename M>
+        void bindProcessName( void (C::*_unaryMethod)( M ) ) {
+            m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
+        }
+
+        void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
+            typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
+            std::size_t maxWidth = 0;
+            for( it = itBegin; it != itEnd; ++it )
+                maxWidth = (std::max)( maxWidth, it->commands().size() );
+
+            for( it = itBegin; it != itEnd; ++it ) {
+                Detail::Text usage( it->commands(), Detail::TextAttributes()
+                                                        .setWidth( maxWidth+indent )
+                                                        .setIndent( indent ) );
+                Detail::Text desc( it->description, Detail::TextAttributes()
+                                                        .setWidth( width - maxWidth - 3 ) );
+
+                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+                    std::string usageCol = i < usage.size() ? usage[i] : "";
+                    os << usageCol;
+
+                    if( i < desc.size() && !desc[i].empty() )
+                        os  << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+                            << desc[i];
+                    os << "\n";
+                }
+            }
+        }
+        std::string optUsage() const {
+            std::ostringstream oss;
+            optUsage( oss );
+            return oss.str();
+        }
+
+        void argSynopsis( std::ostream& os ) const {
+            for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
+                if( i > 1 )
+                    os << " ";
+                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
+                if( it != m_positionalArgs.end() )
+                    os << "<" << it->second.placeholder << ">";
+                else if( m_floatingArg.get() )
+                    os << "<" << m_floatingArg->placeholder << ">";
+                else
+                    throw std::logic_error( "non consecutive positional arguments with no floating args" );
+            }
+            // !TBD No indication of mandatory args
+            if( m_floatingArg.get() ) {
+                if( m_highestSpecifiedArgPosition > 1 )
+                    os << " ";
+                os << "[<" << m_floatingArg->placeholder << "> ...]";
+            }
+        }
+        std::string argSynopsis() const {
+            std::ostringstream oss;
+            argSynopsis( oss );
+            return oss.str();
+        }
+
+        void usage( std::ostream& os, std::string const& procName ) const {
+            validate();
+            os << "usage:\n  " << procName << " ";
+            argSynopsis( os );
+            if( !m_options.empty() ) {
+                os << " [options]\n\nwhere options are: \n";
+                optUsage( os, 2 );
+            }
+            os << "\n";
+        }
+        std::string usage( std::string const& procName ) const {
+            std::ostringstream oss;
+            usage( oss, procName );
+            return oss.str();
+        }
+
+        ConfigT parse( std::vector<std::string> const& args ) const {
+            ConfigT config;
+            parseInto( args, config );
+            return config;
+        }
+
+        std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
+            std::string processName = args[0];
+            std::size_t lastSlash = processName.find_last_of( "/\\" );
+            if( lastSlash != std::string::npos )
+                processName = processName.substr( lastSlash+1 );
+            m_boundProcessName.set( config, processName );
+            std::vector<Parser::Token> tokens;
+            Parser parser;
+            parser.parseIntoTokens( args, tokens );
+            return populate( tokens, config );
+        }
+
+        std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            validate();
+            std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
+            unusedTokens = populateFixedArgs( unusedTokens, config );
+            unusedTokens = populateFloatingArgs( unusedTokens, config );
+            return unusedTokens;
+        }
+
+        std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            std::vector<Parser::Token> unusedTokens;
+            std::vector<std::string> errors;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
+                for(; it != itEnd; ++it ) {
+                    Arg const& arg = *it;
+
+                    try {
+                        if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
+                            ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
+                            if( arg.takesArg() ) {
+                                if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
+                                    errors.push_back( "Expected argument to option: " + token.data );
+                                else
+                                    arg.boundField.set( config, tokens[++i].data );
+                            }
+                            else {
+                                arg.boundField.set( config, "true" );
+                            }
+                            break;
+                        }
+                    }
+                    catch( std::exception& ex ) {
+                        errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+                    }
+                }
+                if( it == itEnd ) {
+                    if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
+                        unusedTokens.push_back( token );
+                    else if( errors.empty() && m_throwOnUnrecognisedTokens )
+                        errors.push_back( "unrecognised option: " + token.data );
+                }
+            }
+            if( !errors.empty() ) {
+                std::ostringstream oss;
+                for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
+                        it != itEnd;
+                        ++it ) {
+                    if( it != errors.begin() )
+                        oss << "\n";
+                    oss << *it;
+                }
+                throw std::runtime_error( oss.str() );
+            }
+            return unusedTokens;
+        }
+        std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            std::vector<Parser::Token> unusedTokens;
+            int position = 1;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
+                if( it != m_positionalArgs.end() )
+                    it->second.boundField.set( config, token.data );
+                else
+                    unusedTokens.push_back( token );
+                if( token.type == Parser::Token::Positional )
+                    position++;
+            }
+            return unusedTokens;
+        }
+        std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            if( !m_floatingArg.get() )
+                return tokens;
+            std::vector<Parser::Token> unusedTokens;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                if( token.type == Parser::Token::Positional )
+                    m_floatingArg->boundField.set( config, token.data );
+                else
+                    unusedTokens.push_back( token );
+            }
+            return unusedTokens;
+        }
+
+        void validate() const
+        {
+            if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
+                throw std::logic_error( "No options or arguments specified" );
+
+            for( typename std::vector<Arg>::const_iterator  it = m_options.begin(),
+                                                            itEnd = m_options.end();
+                    it != itEnd; ++it )
+                it->validate();
+        }
+
+    private:
+        Detail::BoundArgFunction<ConfigT> m_boundProcessName;
+        std::vector<Arg> m_options;
+        std::map<int, Arg> m_positionalArgs;
+        ArgAutoPtr m_floatingArg;
+        int m_highestSpecifiedArgPosition;
+        bool m_throwOnUnrecognisedTokens;
+    };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#include <fstream>
+#include <ctime>
+
+namespace Catch {
+
+    inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
+    inline void abortAfterX( ConfigData& config, int x ) {
+        if( x < 1 )
+            throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
+        config.abortAfter = x;
+    }
+    inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+    inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); }
+    inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
+
+    inline void addWarning( ConfigData& config, std::string const& _warning ) {
+        if( _warning == "NoAssertions" )
+            config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
+        else
+            throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' );
+    }
+    inline void setOrder( ConfigData& config, std::string const& order ) {
+        if( startsWith( "declared", order ) )
+            config.runOrder = RunTests::InDeclarationOrder;
+        else if( startsWith( "lexical", order ) )
+            config.runOrder = RunTests::InLexicographicalOrder;
+        else if( startsWith( "random", order ) )
+            config.runOrder = RunTests::InRandomOrder;
+        else
+            throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' );
+    }
+    inline void setRngSeed( ConfigData& config, std::string const& seed ) {
+        if( seed == "time" ) {
+            config.rngSeed = static_cast<unsigned int>( std::time(0) );
+        }
+        else {
+            std::stringstream ss;
+            ss << seed;
+            ss >> config.rngSeed;
+            if( ss.fail() )
+                throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" );
+        }
+    }
+    inline void setVerbosity( ConfigData& config, int level ) {
+        // !TBD: accept strings?
+        config.verbosity = static_cast<Verbosity::Level>( level );
+    }
+    inline void setShowDurations( ConfigData& config, bool _showDurations ) {
+        config.showDurations = _showDurations
+            ? ShowDurations::Always
+            : ShowDurations::Never;
+    }
+    inline void setUseColour( ConfigData& config, std::string const& value ) {
+        std::string mode = toLower( value );
+
+        if( mode == "yes" )
+            config.useColour = UseColour::Yes;
+        else if( mode == "no" )
+            config.useColour = UseColour::No;
+        else if( mode == "auto" )
+            config.useColour = UseColour::Auto;
+        else
+            throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
+    }
+    inline void forceColour( ConfigData& config ) {
+        config.useColour = UseColour::Yes;
+    }
+    inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
+        std::ifstream f( _filename.c_str() );
+        if( !f.is_open() )
+            throw std::domain_error( "Unable to load input file: " + _filename );
+
+        std::string line;
+        while( std::getline( f, line ) ) {
+            line = trim(line);
+            if( !line.empty() && !startsWith( line, '#' ) ) {
+                if( !startsWith( line, '"' ) )
+                    line = '"' + line + '"';
+                addTestOrTags( config, line + ',' );
+            }
+        }
+    }
+
+    inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+        using namespace Clara;
+        CommandLine<ConfigData> cli;
+
+        cli.bindProcessName( &ConfigData::processName );
+
+        cli["-?"]["-h"]["--help"]
+            .describe( "display usage information" )
+            .bind( &ConfigData::showHelp );
+
+        cli["-l"]["--list-tests"]
+            .describe( "list all/matching test cases" )
+            .bind( &ConfigData::listTests );
+
+        cli["-t"]["--list-tags"]
+            .describe( "list all/matching tags" )
+            .bind( &ConfigData::listTags );
+
+        cli["-s"]["--success"]
+            .describe( "include successful tests in output" )
+            .bind( &ConfigData::showSuccessfulTests );
+
+        cli["-b"]["--break"]
+            .describe( "break into debugger on failure" )
+            .bind( &ConfigData::shouldDebugBreak );
+
+        cli["-e"]["--nothrow"]
+            .describe( "skip exception tests" )
+            .bind( &ConfigData::noThrow );
+
+        cli["-i"]["--invisibles"]
+            .describe( "show invisibles (tabs, newlines)" )
+            .bind( &ConfigData::showInvisibles );
+
+        cli["-o"]["--out"]
+            .describe( "output filename" )
+            .bind( &ConfigData::outputFilename, "filename" );
+
+        cli["-r"]["--reporter"]
+//            .placeholder( "name[:filename]" )
+            .describe( "reporter to use (defaults to console)" )
+            .bind( &addReporterName, "name" );
+
+        cli["-n"]["--name"]
+            .describe( "suite name" )
+            .bind( &ConfigData::name, "name" );
+
+        cli["-a"]["--abort"]
+            .describe( "abort at first failure" )
+            .bind( &abortAfterFirst );
+
+        cli["-x"]["--abortx"]
+            .describe( "abort after x failures" )
+            .bind( &abortAfterX, "no. failures" );
+
+        cli["-w"]["--warn"]
+            .describe( "enable warnings" )
+            .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+//        cli.into( &setVerbosity )
+//            .describe( "level of verbosity (0=no output)" )
+//            .shortOpt( "v")
+//            .longOpt( "verbosity" )
+//            .placeholder( "level" );
+
+        cli[_]
+            .describe( "which test or tests to use" )
+            .bind( &addTestOrTags, "test name, pattern or tags" );
+
+        cli["-d"]["--durations"]
+            .describe( "show test durations" )
+            .bind( &setShowDurations, "yes|no" );
+
+        cli["-f"]["--input-file"]
+            .describe( "load test names to run from a file" )
+            .bind( &loadTestNamesFromFile, "filename" );
+
+        cli["-#"]["--filenames-as-tags"]
+            .describe( "adds a tag for the filename" )
+            .bind( &ConfigData::filenamesAsTags );
+
+        cli["-c"]["--section"]
+                .describe( "specify section to run" )
+                .bind( &addSectionToRun, "section name" );
+
+        // Less common commands which don't have a short form
+        cli["--list-test-names-only"]
+            .describe( "list all/matching test cases names only" )
+            .bind( &ConfigData::listTestNamesOnly );
+
+        cli["--list-reporters"]
+            .describe( "list all reporters" )
+            .bind( &ConfigData::listReporters );
+
+        cli["--order"]
+            .describe( "test case order (defaults to decl)" )
+            .bind( &setOrder, "decl|lex|rand" );
+
+        cli["--rng-seed"]
+            .describe( "set a specific seed for random numbers" )
+            .bind( &setRngSeed, "'time'|number" );
+
+        cli["--force-colour"]
+            .describe( "force colourised output (deprecated)" )
+            .bind( &forceColour );
+
+        cli["--use-colour"]
+            .describe( "should output be colourised" )
+            .bind( &setUseColour, "yes|no" );
+
+        return cli;
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
+// #included from: ../external/tbc_text_format.h
+// Only use header guard if we are not using an outer namespace
+#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+#  ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#   define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#  endif
+# else
+#  define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# endif
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+    struct TextAttributes {
+        TextAttributes()
+        :   initialIndent( std::string::npos ),
+            indent( 0 ),
+            width( consoleWidth-1 )
+        {}
+
+        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
+        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
+        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
+
+        std::size_t initialIndent;  // indent of first line, or npos
+        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
+        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
+    };
+
+    class Text {
+    public:
+        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+        : attr( _attr )
+        {
+            const std::string wrappableBeforeChars = "[({<\t";
+            const std::string wrappableAfterChars = "])}>-,./|\\";
+            const std::string wrappableInsteadOfChars = " \n\r";
+            std::string indent = _attr.initialIndent != std::string::npos
+                ? std::string( _attr.initialIndent, ' ' )
+                : std::string( _attr.indent, ' ' );
+
+            typedef std::string::const_iterator iterator;
+            iterator it = _str.begin();
+            const iterator strEnd = _str.end();
+
+            while( it != strEnd ) {
+
+                if( lines.size() >= 1000 ) {
+                    lines.push_back( "... message truncated due to excessive size" );
+                    return;
+                }
+
+                std::string suffix;
+                std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) );
+                iterator itEnd = it+width;
+                iterator itNext = _str.end();
+
+                iterator itNewLine = std::find( it, itEnd, '\n' );
+                if( itNewLine != itEnd )
+                    itEnd = itNewLine;
+
+                if( itEnd != strEnd  ) {
+                    bool foundWrapPoint = false;
+                    iterator findIt = itEnd;
+                    do {
+                        if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) {
+                            itEnd = findIt+1;
+                            itNext = findIt+1;
+                            foundWrapPoint = true;
+                        }
+                        else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) {
+                            itEnd = findIt;
+                            itNext = findIt;
+                            foundWrapPoint = true;
+                        }
+                        else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) {
+                            itNext = findIt+1;
+                            itEnd = findIt;
+                            foundWrapPoint = true;
+                        }
+                        if( findIt == it )
+                            break;
+                        else
+                            --findIt;
+                    }
+                    while( !foundWrapPoint );
+
+                    if( !foundWrapPoint ) {
+                        // No good wrap char, so we'll break mid word and add a hyphen
+                        --itEnd;
+                        itNext = itEnd;
+                        suffix = "-";
+                    }
+                    else {
+                        while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos )
+                            --itEnd;
+                    }
+                }
+                lines.push_back( indent + std::string( it, itEnd ) + suffix );
+
+                if( indent.size() != _attr.indent )
+                    indent = std::string( _attr.indent, ' ' );
+                it = itNext;
+            }
+        }
+
+        typedef std::vector<std::string>::const_iterator const_iterator;
+
+        const_iterator begin() const { return lines.begin(); }
+        const_iterator end() const { return lines.end(); }
+        std::string const& last() const { return lines.back(); }
+        std::size_t size() const { return lines.size(); }
+        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << *this;
+            return oss.str();
+        }
+
+        inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+                it != itEnd; ++it ) {
+                if( it != _text.begin() )
+                    _stream << "\n";
+                _stream << *it;
+            }
+            return _stream;
+        }
+
+    private:
+        std::string str;
+        TextAttributes attr;
+        std::vector<std::string> lines;
+    };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+    using Tbc::Text;
+    using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+    struct Colour {
+        enum Code {
+            None = 0,
+
+            White,
+            Red,
+            Green,
+            Blue,
+            Cyan,
+            Yellow,
+            Grey,
+
+            Bright = 0x10,
+
+            BrightRed = Bright | Red,
+            BrightGreen = Bright | Green,
+            LightGrey = Bright | Grey,
+            BrightWhite = Bright | White,
+
+            // By intention
+            FileName = LightGrey,
+            Warning = Yellow,
+            ResultError = BrightRed,
+            ResultSuccess = BrightGreen,
+            ResultExpectedFailure = Warning,
+
+            Error = BrightRed,
+            Success = Green,
+
+            OriginalExpression = Cyan,
+            ReconstructedExpression = Yellow,
+
+            SecondaryText = LightGrey,
+            Headers = White
+        };
+
+        // Use constructed object for RAII guard
+        Colour( Code _colourCode );
+        Colour( Colour const& other );
+        ~Colour();
+
+        // Use static method for one-shot changes
+        static void use( Code _colourCode );
+
+    private:
+        bool m_moved;
+    };
+
+    inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+
+namespace Catch
+{
+    struct ReporterConfig {
+        explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
+        :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+        ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
+        :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+        std::ostream& stream() const    { return *m_stream; }
+        Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
+
+    private:
+        std::ostream* m_stream;
+        Ptr<IConfig const> m_fullConfig;
+    };
+
+    struct ReporterPreferences {
+        ReporterPreferences()
+        : shouldRedirectStdOut( false )
+        {}
+
+        bool shouldRedirectStdOut;
+    };
+
+    template<typename T>
+    struct LazyStat : Option<T> {
+        LazyStat() : used( false ) {}
+        LazyStat& operator=( T const& _value ) {
+            Option<T>::operator=( _value );
+            used = false;
+            return *this;
+        }
+        void reset() {
+            Option<T>::reset();
+            used = false;
+        }
+        bool used;
+    };
+
+    struct TestRunInfo {
+        TestRunInfo( std::string const& _name ) : name( _name ) {}
+        std::string name;
+    };
+    struct GroupInfo {
+        GroupInfo(  std::string const& _name,
+                    std::size_t _groupIndex,
+                    std::size_t _groupsCount )
+        :   name( _name ),
+            groupIndex( _groupIndex ),
+            groupsCounts( _groupsCount )
+        {}
+
+        std::string name;
+        std::size_t groupIndex;
+        std::size_t groupsCounts;
+    };
+
+    struct AssertionStats {
+        AssertionStats( AssertionResult const& _assertionResult,
+                        std::vector<MessageInfo> const& _infoMessages,
+                        Totals const& _totals )
+        :   assertionResult( _assertionResult ),
+            infoMessages( _infoMessages ),
+            totals( _totals )
+        {
+            if( assertionResult.hasMessage() ) {
+                // Copy message into messages list.
+                // !TBD This should have been done earlier, somewhere
+                MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+                builder << assertionResult.getMessage();
+                builder.m_info.message = builder.m_stream.str();
+
+                infoMessages.push_back( builder.m_info );
+            }
+        }
+        virtual ~AssertionStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        AssertionStats( AssertionStats const& )              = default;
+        AssertionStats( AssertionStats && )                  = default;
+        AssertionStats& operator = ( AssertionStats const& ) = default;
+        AssertionStats& operator = ( AssertionStats && )     = default;
+#  endif
+
+        AssertionResult assertionResult;
+        std::vector<MessageInfo> infoMessages;
+        Totals totals;
+    };
+
+    struct SectionStats {
+        SectionStats(   SectionInfo const& _sectionInfo,
+                        Counts const& _assertions,
+                        double _durationInSeconds,
+                        bool _missingAssertions )
+        :   sectionInfo( _sectionInfo ),
+            assertions( _assertions ),
+            durationInSeconds( _durationInSeconds ),
+            missingAssertions( _missingAssertions )
+        {}
+        virtual ~SectionStats();
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        SectionStats( SectionStats const& )              = default;
+        SectionStats( SectionStats && )                  = default;
+        SectionStats& operator = ( SectionStats const& ) = default;
+        SectionStats& operator = ( SectionStats && )     = default;
+#  endif
+
+        SectionInfo sectionInfo;
+        Counts assertions;
+        double durationInSeconds;
+        bool missingAssertions;
+    };
+
+    struct TestCaseStats {
+        TestCaseStats(  TestCaseInfo const& _testInfo,
+                        Totals const& _totals,
+                        std::string const& _stdOut,
+                        std::string const& _stdErr,
+                        bool _aborting )
+        : testInfo( _testInfo ),
+            totals( _totals ),
+            stdOut( _stdOut ),
+            stdErr( _stdErr ),
+            aborting( _aborting )
+        {}
+        virtual ~TestCaseStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestCaseStats( TestCaseStats const& )              = default;
+        TestCaseStats( TestCaseStats && )                  = default;
+        TestCaseStats& operator = ( TestCaseStats const& ) = default;
+        TestCaseStats& operator = ( TestCaseStats && )     = default;
+#  endif
+
+        TestCaseInfo testInfo;
+        Totals totals;
+        std::string stdOut;
+        std::string stdErr;
+        bool aborting;
+    };
+
+    struct TestGroupStats {
+        TestGroupStats( GroupInfo const& _groupInfo,
+                        Totals const& _totals,
+                        bool _aborting )
+        :   groupInfo( _groupInfo ),
+            totals( _totals ),
+            aborting( _aborting )
+        {}
+        TestGroupStats( GroupInfo const& _groupInfo )
+        :   groupInfo( _groupInfo ),
+            aborting( false )
+        {}
+        virtual ~TestGroupStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestGroupStats( TestGroupStats const& )              = default;
+        TestGroupStats( TestGroupStats && )                  = default;
+        TestGroupStats& operator = ( TestGroupStats const& ) = default;
+        TestGroupStats& operator = ( TestGroupStats && )     = default;
+#  endif
+
+        GroupInfo groupInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    struct TestRunStats {
+        TestRunStats(   TestRunInfo const& _runInfo,
+                        Totals const& _totals,
+                        bool _aborting )
+        :   runInfo( _runInfo ),
+            totals( _totals ),
+            aborting( _aborting )
+        {}
+        virtual ~TestRunStats();
+
+#  ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestRunStats( TestRunStats const& _other )
+        :   runInfo( _other.runInfo ),
+            totals( _other.totals ),
+            aborting( _other.aborting )
+        {}
+#  else
+        TestRunStats( TestRunStats const& )              = default;
+        TestRunStats( TestRunStats && )                  = default;
+        TestRunStats& operator = ( TestRunStats const& ) = default;
+        TestRunStats& operator = ( TestRunStats && )     = default;
+#  endif
+
+        TestRunInfo runInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    class MultipleReporters;
+
+    struct IStreamingReporter : IShared {
+        virtual ~IStreamingReporter();
+
+        // Implementing class must also provide the following static method:
+        // static std::string getDescription();
+
+        virtual ReporterPreferences getPreferences() const = 0;
+
+        virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+        virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+        virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+        // The return value indicates if the messages buffer should be cleared:
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
+        virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+        virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+        virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+        virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; }
+    };
+
+    struct IReporterFactory : IShared {
+        virtual ~IReporterFactory();
+        virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+        virtual std::string getDescription() const = 0;
+    };
+
+    struct IReporterRegistry {
+        typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
+        typedef std::vector<Ptr<IReporterFactory> > Listeners;
+
+        virtual ~IReporterRegistry();
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
+        virtual FactoryMap const& getFactories() const = 0;
+        virtual Listeners const& getListeners() const = 0;
+    };
+
+    Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+    inline std::size_t listTests( Config const& config ) {
+
+        TestSpec testSpec = config.testSpec();
+        if( config.testSpec().hasFilters() )
+            Catch::cout() << "Matching test cases:\n";
+        else {
+            Catch::cout() << "All available test cases:\n";
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        }
+
+        std::size_t matchedTests = 0;
+        TextAttributes nameAttr, tagsAttr;
+        nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+        tagsAttr.setIndent( 6 );
+
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            matchedTests++;
+            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+            Colour::Code colour = testCaseInfo.isHidden()
+                ? Colour::SecondaryText
+                : Colour::None;
+            Colour colourGuard( colour );
+
+            Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
+            if( !testCaseInfo.tags.empty() )
+                Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+        }
+
+        if( !config.testSpec().hasFilters() )
+            Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl;
+        else
+            Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl;
+        return matchedTests;
+    }
+
+    inline std::size_t listTestsNamesOnly( Config const& config ) {
+        TestSpec testSpec = config.testSpec();
+        if( !config.testSpec().hasFilters() )
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        std::size_t matchedTests = 0;
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            matchedTests++;
+            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+            if( startsWith( testCaseInfo.name, '#' ) )
+               Catch::cout() << '"' << testCaseInfo.name << '"' << std::endl;
+            else
+               Catch::cout() << testCaseInfo.name << std::endl;
+        }
+        return matchedTests;
+    }
+
+    struct TagInfo {
+        TagInfo() : count ( 0 ) {}
+        void add( std::string const& spelling ) {
+            ++count;
+            spellings.insert( spelling );
+        }
+        std::string all() const {
+            std::string out;
+            for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
+                        it != itEnd;
+                        ++it )
+                out += "[" + *it + "]";
+            return out;
+        }
+        std::set<std::string> spellings;
+        std::size_t count;
+    };
+
+    inline std::size_t listTags( Config const& config ) {
+        TestSpec testSpec = config.testSpec();
+        if( config.testSpec().hasFilters() )
+            Catch::cout() << "Tags for matching test cases:\n";
+        else {
+            Catch::cout() << "All available tags:\n";
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        }
+
+        std::map<std::string, TagInfo> tagCounts;
+
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            for( std::set<std::string>::const_iterator  tagIt = it->getTestCaseInfo().tags.begin(),
+                                                        tagItEnd = it->getTestCaseInfo().tags.end();
+                    tagIt != tagItEnd;
+                    ++tagIt ) {
+                std::string tagName = *tagIt;
+                std::string lcaseTagName = toLower( tagName );
+                std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
+                if( countIt == tagCounts.end() )
+                    countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+                countIt->second.add( tagName );
+            }
+        }
+
+        for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
+                                                            countItEnd = tagCounts.end();
+                countIt != countItEnd;
+                ++countIt ) {
+            std::ostringstream oss;
+            oss << "  " << std::setw(2) << countIt->second.count << "  ";
+            Text wrapper( countIt->second.all(), TextAttributes()
+                                                    .setInitialIndent( 0 )
+                                                    .setIndent( oss.str().size() )
+                                                    .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
+            Catch::cout() << oss.str() << wrapper << '\n';
+        }
+        Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
+        return tagCounts.size();
+    }
+
+    inline std::size_t listReporters( Config const& /*config*/ ) {
+        Catch::cout() << "Available reporters:\n";
+        IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+        IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
+        std::size_t maxNameLen = 0;
+        for(it = itBegin; it != itEnd; ++it )
+            maxNameLen = (std::max)( maxNameLen, it->first.size() );
+
+        for(it = itBegin; it != itEnd; ++it ) {
+            Text wrapper( it->second->getDescription(), TextAttributes()
+                                                        .setInitialIndent( 0 )
+                                                        .setIndent( 7+maxNameLen )
+                                                        .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
+            Catch::cout() << "  "
+                    << it->first
+                    << ':'
+                    << std::string( maxNameLen - it->first.size() + 2, ' ' )
+                    << wrapper << '\n';
+        }
+        Catch::cout() << std::endl;
+        return factories.size();
+    }
+
+    inline Option<std::size_t> list( Config const& config ) {
+        Option<std::size_t> listedCount;
+        if( config.listTests() )
+            listedCount = listedCount.valueOr(0) + listTests( config );
+        if( config.listTestNamesOnly() )
+            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+        if( config.listTags() )
+            listedCount = listedCount.valueOr(0) + listTags( config );
+        if( config.listReporters() )
+            listedCount = listedCount.valueOr(0) + listReporters( config );
+        return listedCount;
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_run_context.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <map>
+#include <string>
+#include <assert.h>
+#include <vector>
+#include <iterator>
+#include <stdexcept>
+
+namespace Catch {
+namespace TestCaseTracking {
+
+    struct NameAndLocation {
+        std::string name;
+        SourceLineInfo location;
+
+        NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+        :   name( _name ),
+            location( _location )
+        {}
+    };
+
+    struct ITracker : SharedImpl<> {
+        virtual ~ITracker();
+
+        // static queries
+        virtual NameAndLocation const& nameAndLocation() const = 0;
+
+        // dynamic queries
+        virtual bool isComplete() const = 0; // Successfully completed or failed
+        virtual bool isSuccessfullyCompleted() const = 0;
+        virtual bool isOpen() const = 0; // Started but not complete
+        virtual bool hasChildren() const = 0;
+
+        virtual ITracker& parent() = 0;
+
+        // actions
+        virtual void close() = 0; // Successfully complete
+        virtual void fail() = 0;
+        virtual void markAsNeedingAnotherRun() = 0;
+
+        virtual void addChild( Ptr<ITracker> const& child ) = 0;
+        virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0;
+        virtual void openChild() = 0;
+
+        // Debug/ checking
+        virtual bool isSectionTracker() const = 0;
+        virtual bool isIndexTracker() const = 0;
+    };
+
+    class  TrackerContext {
+
+        enum RunState {
+            NotStarted,
+            Executing,
+            CompletedCycle
+        };
+
+        Ptr<ITracker> m_rootTracker;
+        ITracker* m_currentTracker;
+        RunState m_runState;
+
+    public:
+
+        static TrackerContext& instance() {
+            static TrackerContext s_instance;
+            return s_instance;
+        }
+
+        TrackerContext()
+        :   m_currentTracker( CATCH_NULL ),
+            m_runState( NotStarted )
+        {}
+
+        ITracker& startRun();
+
+        void endRun() {
+            m_rootTracker.reset();
+            m_currentTracker = CATCH_NULL;
+            m_runState = NotStarted;
+        }
+
+        void startCycle() {
+            m_currentTracker = m_rootTracker.get();
+            m_runState = Executing;
+        }
+        void completeCycle() {
+            m_runState = CompletedCycle;
+        }
+
+        bool completedCycle() const {
+            return m_runState == CompletedCycle;
+        }
+        ITracker& currentTracker() {
+            return *m_currentTracker;
+        }
+        void setCurrentTracker( ITracker* tracker ) {
+            m_currentTracker = tracker;
+        }
+    };
+
+    class TrackerBase : public ITracker {
+    protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
+        class TrackerHasName {
+            NameAndLocation m_nameAndLocation;
+        public:
+            TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
+            bool operator ()( Ptr<ITracker> const& tracker ) {
+                return
+                    tracker->nameAndLocation().name == m_nameAndLocation.name &&
+                    tracker->nameAndLocation().location == m_nameAndLocation.location;
+            }
+        };
+        typedef std::vector<Ptr<ITracker> > Children;
+        NameAndLocation m_nameAndLocation;
+        TrackerContext& m_ctx;
+        ITracker* m_parent;
+        Children m_children;
+        CycleState m_runState;
+    public:
+        TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+        :   m_nameAndLocation( nameAndLocation ),
+            m_ctx( ctx ),
+            m_parent( parent ),
+            m_runState( NotStarted )
+        {}
+        virtual ~TrackerBase();
+
+        virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE {
+            return m_nameAndLocation;
+        }
+        virtual bool isComplete() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully || m_runState == Failed;
+        }
+        virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully;
+        }
+        virtual bool isOpen() const CATCH_OVERRIDE {
+            return m_runState != NotStarted && !isComplete();
+        }
+        virtual bool hasChildren() const CATCH_OVERRIDE {
+            return !m_children.empty();
+        }
+
+        virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
+            m_children.push_back( child );
+        }
+
+        virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE {
+            Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
+            return( it != m_children.end() )
+                ? it->get()
+                : CATCH_NULL;
+        }
+        virtual ITracker& parent() CATCH_OVERRIDE {
+            assert( m_parent ); // Should always be non-null except for root
+            return *m_parent;
+        }
+
+        virtual void openChild() CATCH_OVERRIDE {
+            if( m_runState != ExecutingChildren ) {
+                m_runState = ExecutingChildren;
+                if( m_parent )
+                    m_parent->openChild();
+            }
+        }
+
+        virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; }
+        virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; }
+
+        void open() {
+            m_runState = Executing;
+            moveToThis();
+            if( m_parent )
+                m_parent->openChild();
+        }
+
+        virtual void close() CATCH_OVERRIDE {
+
+            // Close any still open children (e.g. generators)
+            while( &m_ctx.currentTracker() != this )
+                m_ctx.currentTracker().close();
+
+            switch( m_runState ) {
+                case NotStarted:
+                case CompletedSuccessfully:
+                case Failed:
+                    throw std::logic_error( "Illogical state" );
+
+                case NeedsAnotherRun:
+                    break;;
+
+                case Executing:
+                    m_runState = CompletedSuccessfully;
+                    break;
+                case ExecutingChildren:
+                    if( m_children.empty() || m_children.back()->isComplete() )
+                        m_runState = CompletedSuccessfully;
+                    break;
+
+                default:
+                    throw std::logic_error( "Unexpected state" );
+            }
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void fail() CATCH_OVERRIDE {
+            m_runState = Failed;
+            if( m_parent )
+                m_parent->markAsNeedingAnotherRun();
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
+            m_runState = NeedsAnotherRun;
+        }
+    private:
+        void moveToParent() {
+            assert( m_parent );
+            m_ctx.setCurrentTracker( m_parent );
+        }
+        void moveToThis() {
+            m_ctx.setCurrentTracker( this );
+        }
+    };
+
+    class SectionTracker : public TrackerBase {
+        std::vector<std::string> m_filters;
+    public:
+        SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+        :   TrackerBase( nameAndLocation, ctx, parent )
+        {
+            if( parent ) {
+                while( !parent->isSectionTracker() )
+                    parent = &parent->parent();
+
+                SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+                addNextFilters( parentSection.m_filters );
+            }
+        }
+        virtual ~SectionTracker();
+
+        virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
+
+        static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+            SectionTracker* section = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+                assert( childTracker );
+                assert( childTracker->isSectionTracker() );
+                section = static_cast<SectionTracker*>( childTracker );
+            }
+            else {
+                section = new SectionTracker( nameAndLocation, ctx, &currentTracker );
+                currentTracker.addChild( section );
+            }
+            if( !ctx.completedCycle() )
+                section->tryOpen();
+            return *section;
+        }
+
+        void tryOpen() {
+            if( !isComplete() && (m_filters.empty() || m_filters[0].empty() ||  m_filters[0] == m_nameAndLocation.name ) )
+                open();
+        }
+
+        void addInitialFilters( std::vector<std::string> const& filters ) {
+            if( !filters.empty() ) {
+                m_filters.push_back(""); // Root - should never be consulted
+                m_filters.push_back(""); // Test Case - not a section filter
+                std::copy( filters.begin(), filters.end(), std::back_inserter( m_filters ) );
+            }
+        }
+        void addNextFilters( std::vector<std::string> const& filters ) {
+            if( filters.size() > 1 )
+                std::copy( filters.begin()+1, filters.end(), std::back_inserter( m_filters ) );
+        }
+    };
+
+    class IndexTracker : public TrackerBase {
+        int m_size;
+        int m_index;
+    public:
+        IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
+        :   TrackerBase( nameAndLocation, ctx, parent ),
+            m_size( size ),
+            m_index( -1 )
+        {}
+        virtual ~IndexTracker();
+
+        virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
+
+        static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
+            IndexTracker* tracker = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+                assert( childTracker );
+                assert( childTracker->isIndexTracker() );
+                tracker = static_cast<IndexTracker*>( childTracker );
+            }
+            else {
+                tracker = new IndexTracker( nameAndLocation, ctx, &currentTracker, size );
+                currentTracker.addChild( tracker );
+            }
+
+            if( !ctx.completedCycle() && !tracker->isComplete() ) {
+                if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+                    tracker->moveNext();
+                tracker->open();
+            }
+
+            return *tracker;
+        }
+
+        int index() const { return m_index; }
+
+        void moveNext() {
+            m_index++;
+            m_children.clear();
+        }
+
+        virtual void close() CATCH_OVERRIDE {
+            TrackerBase::close();
+            if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+                m_runState = Executing;
+        }
+    };
+
+    inline ITracker& TrackerContext::startRun() {
+        m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL );
+        m_currentTracker = CATCH_NULL;
+        m_runState = Executing;
+        return *m_rootTracker;
+    }
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+using TestCaseTracking::IndexTracker;
+
+} // namespace Catch
+
+// #included from: catch_fatal_condition.hpp
+#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+
+namespace Catch {
+
+    // Report the error condition
+    inline void reportFatal( std::string const& message ) {
+        IContext& context = Catch::getCurrentContext();
+        IResultCapture* resultCapture = context.getResultCapture();
+        resultCapture->handleFatalErrorCondition( message );
+    }
+
+} // namespace Catch
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+// #included from: catch_windows_h_proxy.h
+
+#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
+
+#ifdef CATCH_DEFINES_NOMINMAX
+#  define NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+#  define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINES_NOMINMAX
+#  undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+#  undef WIN32_LEAN_AND_MEAN
+#endif
+
+
+#  if !defined ( CATCH_CONFIG_WINDOWS_SEH )
+
+namespace Catch {
+    struct FatalConditionHandler {
+        void reset() {}
+    };
+}
+
+#  else // CATCH_CONFIG_WINDOWS_SEH is defined
+
+namespace Catch {
+
+    struct SignalDefs { DWORD id; const char* name; };
+    extern SignalDefs signalDefs[];
+    // There is no 1-1 mapping between signals and windows exceptions.
+    // Windows can easily distinguish between SO and SigSegV,
+    // but SigInt, SigTerm, etc are handled differently.
+    SignalDefs signalDefs[] = {
+        { EXCEPTION_ILLEGAL_INSTRUCTION,  "SIGILL - Illegal instruction signal" },
+        { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
+        { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
+        { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
+    };
+
+    struct FatalConditionHandler {
+
+        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+            for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+                if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+                    reset();
+                    reportFatal(signalDefs[i].name);
+                }
+            }
+            // If its not an exception we care about, pass it along.
+            // This stops us from eating debugger breaks etc.
+            return EXCEPTION_CONTINUE_SEARCH;
+        }
+
+        FatalConditionHandler() {
+            isSet = true;
+            // 32k seems enough for Catch to handle stack overflow,
+            // but the value was found experimentally, so there is no strong guarantee
+            guaranteeSize = 32 * 1024;
+            exceptionHandlerHandle = CATCH_NULL;
+            // Register as first handler in current chain
+            exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+            // Pass in guarantee size to be filled
+            SetThreadStackGuarantee(&guaranteeSize);
+        }
+
+        static void reset() {
+            if (isSet) {
+                // Unregister handler and restore the old guarantee
+                RemoveVectoredExceptionHandler(exceptionHandlerHandle);
+                SetThreadStackGuarantee(&guaranteeSize);
+                exceptionHandlerHandle = CATCH_NULL;
+                isSet = false;
+            }
+        }
+
+        ~FatalConditionHandler() {
+            reset();
+        }
+    private:
+        static bool isSet;
+        static ULONG guaranteeSize;
+        static PVOID exceptionHandlerHandle;
+    };
+
+    bool FatalConditionHandler::isSet = false;
+    ULONG FatalConditionHandler::guaranteeSize = 0;
+    PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
+
+} // namespace Catch
+
+#  endif // CATCH_CONFIG_WINDOWS_SEH
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+#  if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+
+namespace Catch {
+    struct FatalConditionHandler {
+        void reset() {}
+    };
+}
+
+#  else // CATCH_CONFIG_POSIX_SIGNALS is defined
+
+#include <signal.h>
+
+namespace Catch {
+
+    struct SignalDefs {
+        int id;
+        const char* name;
+    };
+    extern SignalDefs signalDefs[];
+    SignalDefs signalDefs[] = {
+            { SIGINT,  "SIGINT - Terminal interrupt signal" },
+            { SIGILL,  "SIGILL - Illegal instruction signal" },
+            { SIGFPE,  "SIGFPE - Floating point error signal" },
+            { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+            { SIGTERM, "SIGTERM - Termination request signal" },
+            { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+    };
+
+    struct FatalConditionHandler {
+
+        static bool isSet;
+        static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
+        static stack_t oldSigStack;
+        static char altStackMem[SIGSTKSZ];
+
+        static void handleSignal( int sig ) {
+            std::string name = "<unknown signal>";
+            for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+                SignalDefs &def = signalDefs[i];
+                if (sig == def.id) {
+                    name = def.name;
+                    break;
+                }
+            }
+            reset();
+            reportFatal(name);
+            raise( sig );
+        }
+
+        FatalConditionHandler() {
+            isSet = true;
+            stack_t sigStack;
+            sigStack.ss_sp = altStackMem;
+            sigStack.ss_size = SIGSTKSZ;
+            sigStack.ss_flags = 0;
+            sigaltstack(&sigStack, &oldSigStack);
+            struct sigaction sa = { 0 };
+
+            sa.sa_handler = handleSignal;
+            sa.sa_flags = SA_ONSTACK;
+            for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+                sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+            }
+        }
+
+        ~FatalConditionHandler() {
+            reset();
+        }
+        static void reset() {
+            if( isSet ) {
+                // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
+                for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
+                    sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
+                }
+                // Return the old stack
+                sigaltstack(&oldSigStack, CATCH_NULL);
+                isSet = false;
+            }
+        }
+    };
+
+    bool FatalConditionHandler::isSet = false;
+    struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
+    stack_t FatalConditionHandler::oldSigStack = {};
+    char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+
+} // namespace Catch
+
+#  endif // CATCH_CONFIG_POSIX_SIGNALS
+
+#endif // not Windows
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+    class StreamRedirect {
+
+    public:
+        StreamRedirect( std::ostream& stream, std::string& targetString )
+        :   m_stream( stream ),
+            m_prevBuf( stream.rdbuf() ),
+            m_targetString( targetString )
+        {
+            stream.rdbuf( m_oss.rdbuf() );
+        }
+
+        ~StreamRedirect() {
+            m_targetString += m_oss.str();
+            m_stream.rdbuf( m_prevBuf );
+        }
+
+    private:
+        std::ostream& m_stream;
+        std::streambuf* m_prevBuf;
+        std::ostringstream m_oss;
+        std::string& m_targetString;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class RunContext : public IResultCapture, public IRunner {
+
+        RunContext( RunContext const& );
+        void operator =( RunContext const& );
+
+    public:
+
+        explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter )
+        :   m_runInfo( _config->name() ),
+            m_context( getCurrentMutableContext() ),
+            m_activeTestCase( CATCH_NULL ),
+            m_config( _config ),
+            m_reporter( reporter )
+        {
+            m_context.setRunner( this );
+            m_context.setConfig( m_config );
+            m_context.setResultCapture( this );
+            m_reporter->testRunStarting( m_runInfo );
+        }
+
+        virtual ~RunContext() {
+            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+        }
+
+        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
+            m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
+        }
+        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
+            m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
+        }
+
+        Totals runTest( TestCase const& testCase ) {
+            Totals prevTotals = m_totals;
+
+            std::string redirectedCout;
+            std::string redirectedCerr;
+
+            TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+            m_reporter->testCaseStarting( testInfo );
+
+            m_activeTestCase = &testCase;
+
+            do {
+                ITracker& rootTracker = m_trackerContext.startRun();
+                assert( rootTracker.isSectionTracker() );
+                static_cast<SectionTracker&>( rootTracker ).addInitialFilters( m_config->getSectionsToRun() );
+                do {
+                    m_trackerContext.startCycle();
+                    m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) );
+                    runCurrentTest( redirectedCout, redirectedCerr );
+                }
+                while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
+            }
+            // !TBD: deprecated - this will be replaced by indexed trackers
+            while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+            Totals deltaTotals = m_totals.delta( prevTotals );
+            if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
+                deltaTotals.assertions.failed++;
+                deltaTotals.testCases.passed--;
+                deltaTotals.testCases.failed++;
+            }
+            m_totals.testCases += deltaTotals.testCases;
+            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
+                                                        deltaTotals,
+                                                        redirectedCout,
+                                                        redirectedCerr,
+                                                        aborting() ) );
+
+            m_activeTestCase = CATCH_NULL;
+            m_testCaseTracker = CATCH_NULL;
+
+            return deltaTotals;
+        }
+
+        Ptr<IConfig const> config() const {
+            return m_config;
+        }
+
+    private: // IResultCapture
+
+        virtual void assertionEnded( AssertionResult const& result ) {
+            if( result.getResultType() == ResultWas::Ok ) {
+                m_totals.assertions.passed++;
+            }
+            else if( !result.isOk() ) {
+                m_totals.assertions.failed++;
+            }
+
+            if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
+                m_messages.clear();
+
+            // Reset working state
+            m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+            m_lastResult = result;
+        }
+
+        virtual bool sectionStarted (
+            SectionInfo const& sectionInfo,
+            Counts& assertions
+        )
+        {
+            ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) );
+            if( !sectionTracker.isOpen() )
+                return false;
+            m_activeSections.push_back( &sectionTracker );
+
+            m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+            m_reporter->sectionStarting( sectionInfo );
+
+            assertions = m_totals.assertions;
+
+            return true;
+        }
+        bool testForMissingAssertions( Counts& assertions ) {
+            if( assertions.total() != 0 )
+                return false;
+            if( !m_config->warnAboutMissingAssertions() )
+                return false;
+            if( m_trackerContext.currentTracker().hasChildren() )
+                return false;
+            m_totals.assertions.failed++;
+            assertions.failed++;
+            return true;
+        }
+
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) {
+            Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+            bool missingAssertions = testForMissingAssertions( assertions );
+
+            if( !m_activeSections.empty() ) {
+                m_activeSections.back()->close();
+                m_activeSections.pop_back();
+            }
+
+            m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
+            m_messages.clear();
+        }
+
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
+            if( m_unfinishedSections.empty() )
+                m_activeSections.back()->fail();
+            else
+                m_activeSections.back()->close();
+            m_activeSections.pop_back();
+
+            m_unfinishedSections.push_back( endInfo );
+        }
+
+        virtual void pushScopedMessage( MessageInfo const& message ) {
+            m_messages.push_back( message );
+        }
+
+        virtual void popScopedMessage( MessageInfo const& message ) {
+            m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+        }
+
+        virtual std::string getCurrentTestName() const {
+            return m_activeTestCase
+                ? m_activeTestCase->getTestCaseInfo().name
+                : std::string();
+        }
+
+        virtual const AssertionResult* getLastResult() const {
+            return &m_lastResult;
+        }
+
+        virtual void handleFatalErrorCondition( std::string const& message ) {
+            ResultBuilder resultBuilder = makeUnexpectedResultBuilder();
+            resultBuilder.setResultType( ResultWas::FatalErrorCondition );
+            resultBuilder << message;
+            resultBuilder.captureExpression();
+
+            handleUnfinishedSections();
+
+            // Recreate section for test case (as we will lose the one that was in scope)
+            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+
+            Counts assertions;
+            assertions.failed = 1;
+            SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
+            m_reporter->sectionEnded( testCaseSectionStats );
+
+            TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
+
+            Totals deltaTotals;
+            deltaTotals.testCases.failed = 1;
+            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
+                                                        deltaTotals,
+                                                        std::string(),
+                                                        std::string(),
+                                                        false ) );
+            m_totals.testCases.failed++;
+            testGroupEnded( std::string(), m_totals, 1, 1 );
+            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
+        }
+
+    public:
+        // !TBD We need to do this another way!
+        bool aborting() const {
+            return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
+        }
+
+    private:
+
+        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
+            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+            m_reporter->sectionStarting( testCaseSection );
+            Counts prevAssertions = m_totals.assertions;
+            double duration = 0;
+            try {
+                m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal );
+
+                seedRng( *m_config );
+
+                Timer timer;
+                timer.start();
+                if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+                    StreamRedirect coutRedir( Catch::cout(), redirectedCout );
+                    StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
+                    invokeActiveTestCase();
+                }
+                else {
+                    invokeActiveTestCase();
+                }
+                duration = timer.getElapsedSeconds();
+            }
+            catch( TestFailureException& ) {
+                // This just means the test was aborted due to failure
+            }
+            catch(...) {
+                makeUnexpectedResultBuilder().useActiveException();
+            }
+            m_testCaseTracker->close();
+            handleUnfinishedSections();
+            m_messages.clear();
+
+            Counts assertions = m_totals.assertions - prevAssertions;
+            bool missingAssertions = testForMissingAssertions( assertions );
+
+            if( testCaseInfo.okToFail() ) {
+                std::swap( assertions.failedButOk, assertions.failed );
+                m_totals.assertions.failed -= assertions.failedButOk;
+                m_totals.assertions.failedButOk += assertions.failedButOk;
+            }
+
+            SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+            m_reporter->sectionEnded( testCaseSectionStats );
+        }
+
+        void invokeActiveTestCase() {
+            FatalConditionHandler fatalConditionHandler; // Handle signals
+            m_activeTestCase->invoke();
+            fatalConditionHandler.reset();
+        }
+
+    private:
+
+        ResultBuilder makeUnexpectedResultBuilder() const {
+            return ResultBuilder(   m_lastAssertionInfo.macroName.c_str(),
+                                    m_lastAssertionInfo.lineInfo,
+                                    m_lastAssertionInfo.capturedExpression.c_str(),
+                                    m_lastAssertionInfo.resultDisposition );
+        }
+
+        void handleUnfinishedSections() {
+            // If sections ended prematurely due to an exception we stored their
+            // infos here so we can tear them down outside the unwind process.
+            for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+                        itEnd = m_unfinishedSections.rend();
+                    it != itEnd;
+                    ++it )
+                sectionEnded( *it );
+            m_unfinishedSections.clear();
+        }
+
+        TestRunInfo m_runInfo;
+        IMutableContext& m_context;
+        TestCase const* m_activeTestCase;
+        ITracker* m_testCaseTracker;
+        ITracker* m_currentSectionTracker;
+        AssertionResult m_lastResult;
+
+        Ptr<IConfig const> m_config;
+        Totals m_totals;
+        Ptr<IStreamingReporter> m_reporter;
+        std::vector<MessageInfo> m_messages;
+        AssertionInfo m_lastAssertionInfo;
+        std::vector<SectionEndInfo> m_unfinishedSections;
+        std::vector<ITracker*> m_activeSections;
+        TrackerContext m_trackerContext;
+    };
+
+    IResultCapture& getResultCapture() {
+        if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+            return *capture;
+        else
+            throw std::logic_error( "No result capture instance" );
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+    // Versioning information
+    struct Version {
+        Version(    unsigned int _majorVersion,
+                    unsigned int _minorVersion,
+                    unsigned int _patchNumber,
+                    std::string const& _branchName,
+                    unsigned int _buildNumber );
+
+        unsigned int const majorVersion;
+        unsigned int const minorVersion;
+        unsigned int const patchNumber;
+
+        // buildNumber is only used if branchName is not null
+        std::string const branchName;
+        unsigned int const buildNumber;
+
+        friend std::ostream& operator << ( std::ostream& os, Version const& version );
+
+    private:
+        void operator=( Version const& );
+    };
+
+    extern Version libraryVersion;
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+    Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
+        Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
+        if( !reporter ) {
+            std::ostringstream oss;
+            oss << "No reporter registered with name: '" << reporterName << "'";
+            throw std::domain_error( oss.str() );
+        }
+        return reporter;
+    }
+
+    Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
+        std::vector<std::string> reporters = config->getReporterNames();
+        if( reporters.empty() )
+            reporters.push_back( "console" );
+
+        Ptr<IStreamingReporter> reporter;
+        for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
+                it != itEnd;
+                ++it )
+            reporter = addReporter( reporter, createReporter( *it, config ) );
+        return reporter;
+    }
+    Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
+        IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
+        for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
+                it != itEnd;
+                ++it )
+            reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
+        return reporters;
+    }
+
+    Totals runTests( Ptr<Config> const& config ) {
+
+        Ptr<IConfig const> iconfig = config.get();
+
+        Ptr<IStreamingReporter> reporter = makeReporter( config );
+        reporter = addListeners( iconfig, reporter );
+
+        RunContext context( iconfig, reporter );
+
+        Totals totals;
+
+        context.testGroupStarting( config->name(), 1, 1 );
+
+        TestSpec testSpec = config->testSpec();
+        if( !testSpec.hasFilters() )
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+        std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
+        for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
+                it != itEnd;
+                ++it ) {
+            if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
+                totals += context.runTest( *it );
+            else
+                reporter->skipTest( *it );
+        }
+
+        context.testGroupEnded( iconfig->name(), totals, 1, 1 );
+        return totals;
+    }
+
+    void applyFilenamesAsTags( IConfig const& config ) {
+        std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
+        for(std::size_t i = 0; i < tests.size(); ++i ) {
+            TestCase& test = const_cast<TestCase&>( tests[i] );
+            std::set<std::string> tags = test.tags;
+
+            std::string filename = test.lineInfo.file;
+            std::string::size_type lastSlash = filename.find_last_of( "\\/" );
+            if( lastSlash != std::string::npos )
+                filename = filename.substr( lastSlash+1 );
+
+            std::string::size_type lastDot = filename.find_last_of( "." );
+            if( lastDot != std::string::npos )
+                filename = filename.substr( 0, lastDot );
+
+            tags.insert( "#" + filename );
+            setTags( test, tags );
+        }
+    }
+
+    class Session : NonCopyable {
+        static bool alreadyInstantiated;
+
+    public:
+
+        struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+        Session()
+        : m_cli( makeCommandLineParser() ) {
+            if( alreadyInstantiated ) {
+                std::string msg = "Only one instance of Catch::Session can ever be used";
+                Catch::cerr() << msg << std::endl;
+                throw std::logic_error( msg );
+            }
+            alreadyInstantiated = true;
+        }
+        ~Session() {
+            Catch::cleanUp();
+        }
+
+        void showHelp( std::string const& processName ) {
+            Catch::cout() << "\nCatch v" << libraryVersion << "\n";
+
+            m_cli.usage( Catch::cout(), processName );
+            Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
+        }
+
+        int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+            try {
+                m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+                m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
+                if( m_configData.showHelp )
+                    showHelp( m_configData.processName );
+                m_config.reset();
+            }
+            catch( std::exception& ex ) {
+                {
+                    Colour colourGuard( Colour::Red );
+                    Catch::cerr()
+                        << "\nError(s) in input:\n"
+                        << Text( ex.what(), TextAttributes().setIndent(2) )
+                        << "\n\n";
+                }
+                m_cli.usage( Catch::cout(), m_configData.processName );
+                return (std::numeric_limits<int>::max)();
+            }
+            return 0;
+        }
+
+        void useConfigData( ConfigData const& _configData ) {
+            m_configData = _configData;
+            m_config.reset();
+        }
+
+        int run( int argc, char const* const* const argv ) {
+
+            int returnCode = applyCommandLine( argc, argv );
+            if( returnCode == 0 )
+                returnCode = run();
+            return returnCode;
+        }
+
+        int run() {
+            if( m_configData.showHelp )
+                return 0;
+
+            try
+            {
+                config(); // Force config to be constructed
+
+                seedRng( *m_config );
+
+                if( m_configData.filenamesAsTags )
+                    applyFilenamesAsTags( *m_config );
+
+                // Handle list request
+                if( Option<std::size_t> listed = list( config() ) )
+                    return static_cast<int>( *listed );
+
+                return static_cast<int>( runTests( m_config ).assertions.failed );
+            }
+            catch( std::exception& ex ) {
+                Catch::cerr() << ex.what() << std::endl;
+                return (std::numeric_limits<int>::max)();
+            }
+        }
+
+        Clara::CommandLine<ConfigData> const& cli() const {
+            return m_cli;
+        }
+        std::vector<Clara::Parser::Token> const& unusedTokens() const {
+            return m_unusedTokens;
+        }
+        ConfigData& configData() {
+            return m_configData;
+        }
+        Config& config() {
+            if( !m_config )
+                m_config = new Config( m_configData );
+            return *m_config;
+        }
+    private:
+        Clara::CommandLine<ConfigData> m_cli;
+        std::vector<Clara::Parser::Token> m_unusedTokens;
+        ConfigData m_configData;
+        Ptr<Config> m_config;
+    };
+
+    bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+    struct RandomNumberGenerator {
+        typedef std::ptrdiff_t result_type;
+
+        result_type operator()( result_type n ) const { return std::rand() % n; }
+
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+        static constexpr result_type min() { return 0; }
+        static constexpr result_type max() { return 1000000; }
+        result_type operator()() const { return std::rand() % max(); }
+#endif
+        template<typename V>
+        static void shuffle( V& vector ) {
+            RandomNumberGenerator rng;
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+            std::shuffle( vector.begin(), vector.end(), rng );
+#else
+            std::random_shuffle( vector.begin(), vector.end(), rng );
+#endif
+        }
+    };
+
+    inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+
+        std::vector<TestCase> sorted = unsortedTestCases;
+
+        switch( config.runOrder() ) {
+            case RunTests::InLexicographicalOrder:
+                std::sort( sorted.begin(), sorted.end() );
+                break;
+            case RunTests::InRandomOrder:
+                {
+                    seedRng( config );
+                    RandomNumberGenerator::shuffle( sorted );
+                }
+                break;
+            case RunTests::InDeclarationOrder:
+                // already in declaration order
+                break;
+        }
+        return sorted;
+    }
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+    }
+
+    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+        std::set<TestCase> seenFunctions;
+        for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
+            it != itEnd;
+            ++it ) {
+            std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
+            if( !prev.second ) {
+                std::ostringstream ss;
+
+                ss  << Colour( Colour::Red )
+                    << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+                    << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
+                    << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+
+                throw std::runtime_error(ss.str());
+            }
+        }
+    }
+
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+        std::vector<TestCase> filtered;
+        filtered.reserve( testCases.size() );
+        for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+                it != itEnd;
+                ++it )
+            if( matchTest( *it, testSpec, config ) )
+                filtered.push_back( *it );
+        return filtered;
+    }
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+    }
+
+    class TestRegistry : public ITestCaseRegistry {
+    public:
+        TestRegistry()
+        :   m_currentSortOrder( RunTests::InDeclarationOrder ),
+            m_unnamedCount( 0 )
+        {}
+        virtual ~TestRegistry();
+
+        virtual void registerTest( TestCase const& testCase ) {
+            std::string name = testCase.getTestCaseInfo().name;
+            if( name.empty() ) {
+                std::ostringstream oss;
+                oss << "Anonymous test case " << ++m_unnamedCount;
+                return registerTest( testCase.withName( oss.str() ) );
+            }
+            m_functions.push_back( testCase );
+        }
+
+        virtual std::vector<TestCase> const& getAllTests() const {
+            return m_functions;
+        }
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
+            if( m_sortedFunctions.empty() )
+                enforceNoDuplicateTestCases( m_functions );
+
+            if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+                m_sortedFunctions = sortTests( config, m_functions );
+                m_currentSortOrder = config.runOrder();
+            }
+            return m_sortedFunctions;
+        }
+
+    private:
+        std::vector<TestCase> m_functions;
+        mutable RunTests::InWhatOrder m_currentSortOrder;
+        mutable std::vector<TestCase> m_sortedFunctions;
+        size_t m_unnamedCount;
+        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+    public:
+
+        FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+        virtual void invoke() const {
+            m_fun();
+        }
+
+    private:
+        virtual ~FreeFunctionTestCase();
+
+        TestFunction m_fun;
+    };
+
+    inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
+        std::string className = classOrQualifiedMethodName;
+        if( startsWith( className, '&' ) )
+        {
+            std::size_t lastColons = className.rfind( "::" );
+            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+            if( penultimateColons == std::string::npos )
+                penultimateColons = 1;
+            className = className.substr( penultimateColons, lastColons-penultimateColons );
+        }
+        return className;
+    }
+
+    void registerTestCase
+        (   ITestCase* testCase,
+            char const* classOrQualifiedMethodName,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
+
+        getMutableRegistryHub().registerTest
+            ( makeTestCase
+                (   testCase,
+                    extractClassName( classOrQualifiedMethodName ),
+                    nameAndDesc.name,
+                    nameAndDesc.description,
+                    lineInfo ) );
+    }
+    void registerTestCaseFunction
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
+        registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    AutoReg::AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
+        registerTestCaseFunction( function, lineInfo, nameAndDesc );
+    }
+
+    AutoReg::~AutoReg() {}
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+    class ReporterRegistry : public IReporterRegistry {
+
+    public:
+
+        virtual ~ReporterRegistry() CATCH_OVERRIDE {}
+
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
+            FactoryMap::const_iterator it =  m_factories.find( name );
+            if( it == m_factories.end() )
+                return CATCH_NULL;
+            return it->second->create( ReporterConfig( config ) );
+        }
+
+        void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
+            m_factories.insert( std::make_pair( name, factory ) );
+        }
+        void registerListener( Ptr<IReporterFactory> const& factory ) {
+            m_listeners.push_back( factory );
+        }
+
+        virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
+            return m_factories;
+        }
+        virtual Listeners const& getListeners() const CATCH_OVERRIDE {
+            return m_listeners;
+        }
+
+    private:
+        FactoryMap m_factories;
+        Listeners m_listeners;
+    };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+    public:
+        ~ExceptionTranslatorRegistry() {
+            deleteAll( m_translators );
+        }
+
+        virtual void registerTranslator( const IExceptionTranslator* translator ) {
+            m_translators.push_back( translator );
+        }
+
+        virtual std::string translateActiveException() const {
+            try {
+#ifdef __OBJC__
+                // In Objective-C try objective-c exceptions first
+                @try {
+                    return tryTranslators();
+                }
+                @catch (NSException *exception) {
+                    return Catch::toString( [exception description] );
+                }
+#else
+                return tryTranslators();
+#endif
+            }
+            catch( TestFailureException& ) {
+                throw;
+            }
+            catch( std::exception& ex ) {
+                return ex.what();
+            }
+            catch( std::string& msg ) {
+                return msg;
+            }
+            catch( const char* msg ) {
+                return msg;
+            }
+            catch(...) {
+                return "Unknown exception";
+            }
+        }
+
+        std::string tryTranslators() const {
+            if( m_translators.empty() )
+                throw;
+            else
+                return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
+        }
+
+    private:
+        std::vector<const IExceptionTranslator*> m_translators;
+    };
+}
+
+namespace Catch {
+
+    namespace {
+
+        class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+            RegistryHub( RegistryHub const& );
+            void operator=( RegistryHub const& );
+
+        public: // IRegistryHub
+            RegistryHub() {
+            }
+            virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
+                return m_reporterRegistry;
+            }
+            virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
+                return m_testCaseRegistry;
+            }
+            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
+                return m_exceptionTranslatorRegistry;
+            }
+
+        public: // IMutableRegistryHub
+            virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+                m_reporterRegistry.registerReporter( name, factory );
+            }
+            virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+                m_reporterRegistry.registerListener( factory );
+            }
+            virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
+                m_testCaseRegistry.registerTest( testInfo );
+            }
+            virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
+                m_exceptionTranslatorRegistry.registerTranslator( translator );
+            }
+
+        private:
+            TestRegistry m_testCaseRegistry;
+            ReporterRegistry m_reporterRegistry;
+            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+        };
+
+        // Single, global, instance
+        inline RegistryHub*& getTheRegistryHub() {
+            static RegistryHub* theRegistryHub = CATCH_NULL;
+            if( !theRegistryHub )
+                theRegistryHub = new RegistryHub();
+            return theRegistryHub;
+        }
+    }
+
+    IRegistryHub& getRegistryHub() {
+        return *getTheRegistryHub();
+    }
+    IMutableRegistryHub& getMutableRegistryHub() {
+        return *getTheRegistryHub();
+    }
+    void cleanUp() {
+        delete getTheRegistryHub();
+        getTheRegistryHub() = CATCH_NULL;
+        cleanUpContext();
+    }
+    std::string translateActiveException() {
+        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <sstream>
+
+namespace Catch {
+
+    NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+    :   m_lineInfo( lineInfo ) {
+        std::ostringstream oss;
+        oss << lineInfo << ": function ";
+        oss << "not implemented";
+        m_what = oss.str();
+    }
+
+    const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+        return m_what.c_str();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+#include <stdexcept>
+#include <cstdio>
+#include <iostream>
+
+namespace Catch {
+
+    template<typename WriterF, size_t bufferSize=256>
+    class StreamBufImpl : public StreamBufBase {
+        char data[bufferSize];
+        WriterF m_writer;
+
+    public:
+        StreamBufImpl() {
+            setp( data, data + sizeof(data) );
+        }
+
+        ~StreamBufImpl() CATCH_NOEXCEPT {
+            sync();
+        }
+
+    private:
+        int overflow( int c ) {
+            sync();
+
+            if( c != EOF ) {
+                if( pbase() == epptr() )
+                    m_writer( std::string( 1, static_cast<char>( c ) ) );
+                else
+                    sputc( static_cast<char>( c ) );
+            }
+            return 0;
+        }
+
+        int sync() {
+            if( pbase() != pptr() ) {
+                m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+                setp( pbase(), epptr() );
+            }
+            return 0;
+        }
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    FileStream::FileStream( std::string const& filename ) {
+        m_ofs.open( filename.c_str() );
+        if( m_ofs.fail() ) {
+            std::ostringstream oss;
+            oss << "Unable to open file: '" << filename << '\'';
+            throw std::domain_error( oss.str() );
+        }
+    }
+
+    std::ostream& FileStream::stream() const {
+        return m_ofs;
+    }
+
+    struct OutputDebugWriter {
+
+        void operator()( std::string const&str ) {
+            writeToDebugConsole( str );
+        }
+    };
+
+    DebugOutStream::DebugOutStream()
+    :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+        m_os( m_streamBuf.get() )
+    {}
+
+    std::ostream& DebugOutStream::stream() const {
+        return m_os;
+    }
+
+    // Store the streambuf from cout up-front because
+    // cout may get redirected when running tests
+    CoutStream::CoutStream()
+    :   m_os( Catch::cout().rdbuf() )
+    {}
+
+    std::ostream& CoutStream::stream() const {
+        return m_os;
+    }
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+    std::ostream& cout() {
+        return std::cout;
+    }
+    std::ostream& cerr() {
+        return std::cerr;
+    }
+#endif
+}
+
+namespace Catch {
+
+    class Context : public IMutableContext {
+
+        Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
+        Context( Context const& );
+        void operator=( Context const& );
+
+    public:
+        virtual ~Context() {
+            deleteAllValues( m_generatorsByTestName );
+        }
+
+    public: // IContext
+        virtual IResultCapture* getResultCapture() {
+            return m_resultCapture;
+        }
+        virtual IRunner* getRunner() {
+            return m_runner;
+        }
+        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
+            return getGeneratorsForCurrentTest()
+            .getGeneratorInfo( fileInfo, totalSize )
+            .getCurrentIndex();
+        }
+        virtual bool advanceGeneratorsForCurrentTest() {
+            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+            return generators && generators->moveNext();
+        }
+
+        virtual Ptr<IConfig const> getConfig() const {
+            return m_config;
+        }
+
+    public: // IMutableContext
+        virtual void setResultCapture( IResultCapture* resultCapture ) {
+            m_resultCapture = resultCapture;
+        }
+        virtual void setRunner( IRunner* runner ) {
+            m_runner = runner;
+        }
+        virtual void setConfig( Ptr<IConfig const> const& config ) {
+            m_config = config;
+        }
+
+        friend IMutableContext& getCurrentMutableContext();
+
+    private:
+        IGeneratorsForTest* findGeneratorsForCurrentTest() {
+            std::string testName = getResultCapture()->getCurrentTestName();
+
+            std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+                m_generatorsByTestName.find( testName );
+            return it != m_generatorsByTestName.end()
+                ? it->second
+                : CATCH_NULL;
+        }
+
+        IGeneratorsForTest& getGeneratorsForCurrentTest() {
+            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+            if( !generators ) {
+                std::string testName = getResultCapture()->getCurrentTestName();
+                generators = createGeneratorsForTest();
+                m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+            }
+            return *generators;
+        }
+
+    private:
+        Ptr<IConfig const> m_config;
+        IRunner* m_runner;
+        IResultCapture* m_resultCapture;
+        std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+    };
+
+    namespace {
+        Context* currentContext = CATCH_NULL;
+    }
+    IMutableContext& getCurrentMutableContext() {
+        if( !currentContext )
+            currentContext = new Context();
+        return *currentContext;
+    }
+    IContext& getCurrentContext() {
+        return getCurrentMutableContext();
+    }
+
+    void cleanUpContext() {
+        delete currentContext;
+        currentContext = CATCH_NULL;
+    }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+namespace Catch {
+    namespace {
+
+        struct IColourImpl {
+            virtual ~IColourImpl() {}
+            virtual void use( Colour::Code _colourCode ) = 0;
+        };
+
+        struct NoColourImpl : IColourImpl {
+            void use( Colour::Code ) {}
+
+            static IColourImpl* instance() {
+                static NoColourImpl s_instance;
+                return &s_instance;
+            }
+        };
+
+    } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+#   ifdef CATCH_PLATFORM_WINDOWS
+#       define CATCH_CONFIG_COLOUR_WINDOWS
+#   else
+#       define CATCH_CONFIG_COLOUR_ANSI
+#   endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+    class Win32ColourImpl : public IColourImpl {
+    public:
+        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+        {
+            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+        }
+
+        virtual void use( Colour::Code _colourCode ) {
+            switch( _colourCode ) {
+                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
+                case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
+                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
+                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
+                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+                case Colour::Grey:      return setTextAttribute( 0 );
+
+                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
+                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+
+                case Colour::Bright: throw std::logic_error( "not a colour" );
+            }
+        }
+
+    private:
+        void setTextAttribute( WORD _textAttribute ) {
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
+        }
+        HANDLE stdoutHandle;
+        WORD originalForegroundAttributes;
+        WORD originalBackgroundAttributes;
+    };
+
+    IColourImpl* platformColourInstance() {
+        static Win32ColourImpl s_instance;
+
+        Ptr<IConfig const> config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = !isDebuggerActive()
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
+            ? &s_instance
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+    // use POSIX/ ANSI console terminal codes
+    // Thanks to Adam Strzelecki for original contribution
+    // (http://github.com/nanoant)
+    // https://github.com/philsquared/Catch/pull/131
+    class PosixColourImpl : public IColourImpl {
+    public:
+        virtual void use( Colour::Code _colourCode ) {
+            switch( _colourCode ) {
+                case Colour::None:
+                case Colour::White:     return setColour( "[0m" );
+                case Colour::Red:       return setColour( "[0;31m" );
+                case Colour::Green:     return setColour( "[0;32m" );
+                case Colour::Blue:      return setColour( "[0;34m" );
+                case Colour::Cyan:      return setColour( "[0;36m" );
+                case Colour::Yellow:    return setColour( "[0;33m" );
+                case Colour::Grey:      return setColour( "[1;30m" );
+
+                case Colour::LightGrey:     return setColour( "[0;37m" );
+                case Colour::BrightRed:     return setColour( "[1;31m" );
+                case Colour::BrightGreen:   return setColour( "[1;32m" );
+                case Colour::BrightWhite:   return setColour( "[1;37m" );
+
+                case Colour::Bright: throw std::logic_error( "not a colour" );
+            }
+        }
+        static IColourImpl* instance() {
+            static PosixColourImpl s_instance;
+            return &s_instance;
+        }
+
+    private:
+        void setColour( const char* _escapeCode ) {
+            Catch::cout() << '\033' << _escapeCode;
+        }
+    };
+
+    IColourImpl* platformColourInstance() {
+        Ptr<IConfig const> config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
+            ? PosixColourImpl::instance()
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else  // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+    static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+    Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
+    Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
+    Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+    void Colour::use( Code _colourCode ) {
+        static IColourImpl* impl = platformColourInstance();
+        impl->use( _colourCode );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+    struct GeneratorInfo : IGeneratorInfo {
+
+        GeneratorInfo( std::size_t size )
+        :   m_size( size ),
+            m_currentIndex( 0 )
+        {}
+
+        bool moveNext() {
+            if( ++m_currentIndex == m_size ) {
+                m_currentIndex = 0;
+                return false;
+            }
+            return true;
+        }
+
+        std::size_t getCurrentIndex() const {
+            return m_currentIndex;
+        }
+
+        std::size_t m_size;
+        std::size_t m_currentIndex;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class GeneratorsForTest : public IGeneratorsForTest {
+
+    public:
+        ~GeneratorsForTest() {
+            deleteAll( m_generatorsInOrder );
+        }
+
+        IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
+            std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
+            if( it == m_generatorsByName.end() ) {
+                IGeneratorInfo* info = new GeneratorInfo( size );
+                m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
+                m_generatorsInOrder.push_back( info );
+                return *info;
+            }
+            return *it->second;
+        }
+
+        bool moveNext() {
+            std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
+            std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
+            for(; it != itEnd; ++it ) {
+                if( (*it)->moveNext() )
+                    return true;
+            }
+            return false;
+        }
+
+    private:
+        std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+        std::vector<IGeneratorInfo*> m_generatorsInOrder;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest()
+    {
+        return new GeneratorsForTest();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+    AssertionInfo::AssertionInfo(   std::string const& _macroName,
+                                    SourceLineInfo const& _lineInfo,
+                                    std::string const& _capturedExpression,
+                                    ResultDisposition::Flags _resultDisposition )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        capturedExpression( _capturedExpression ),
+        resultDisposition( _resultDisposition )
+    {}
+
+    AssertionResult::AssertionResult() {}
+
+    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+    :   m_info( info ),
+        m_resultData( data )
+    {}
+
+    AssertionResult::~AssertionResult() {}
+
+    // Result was a success
+    bool AssertionResult::succeeded() const {
+        return Catch::isOk( m_resultData.resultType );
+    }
+
+    // Result was a success, or failure is suppressed
+    bool AssertionResult::isOk() const {
+        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+    }
+
+    ResultWas::OfType AssertionResult::getResultType() const {
+        return m_resultData.resultType;
+    }
+
+    bool AssertionResult::hasExpression() const {
+        return !m_info.capturedExpression.empty();
+    }
+
+    bool AssertionResult::hasMessage() const {
+        return !m_resultData.message.empty();
+    }
+
+    std::string AssertionResult::getExpression() const {
+        if( isFalseTest( m_info.resultDisposition ) )
+            return '!' + m_info.capturedExpression;
+        else
+            return m_info.capturedExpression;
+    }
+    std::string AssertionResult::getExpressionInMacro() const {
+        if( m_info.macroName.empty() )
+            return m_info.capturedExpression;
+        else
+            return m_info.macroName + "( " + m_info.capturedExpression + " )";
+    }
+
+    bool AssertionResult::hasExpandedExpression() const {
+        return hasExpression() && getExpandedExpression() != getExpression();
+    }
+
+    std::string AssertionResult::getExpandedExpression() const {
+        return m_resultData.reconstructExpression();
+    }
+
+    std::string AssertionResult::getMessage() const {
+        return m_resultData.message;
+    }
+    SourceLineInfo AssertionResult::getSourceInfo() const {
+        return m_info.lineInfo;
+    }
+
+    std::string AssertionResult::getTestMacroName() const {
+        return m_info.macroName;
+    }
+
+    void AssertionResult::discardDecomposedExpression() const {
+        m_resultData.decomposedExpression = CATCH_NULL;
+    }
+
+    void AssertionResult::expandDecomposedExpression() const {
+        m_resultData.reconstructExpression();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+#include <cctype>
+
+namespace Catch {
+
+    inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+        if( startsWith( tag, '.' ) ||
+            tag == "hide" ||
+            tag == "!hide" )
+            return TestCaseInfo::IsHidden;
+        else if( tag == "!throws" )
+            return TestCaseInfo::Throws;
+        else if( tag == "!shouldfail" )
+            return TestCaseInfo::ShouldFail;
+        else if( tag == "!mayfail" )
+            return TestCaseInfo::MayFail;
+        else if( tag == "!nonportable" )
+            return TestCaseInfo::NonPortable;
+        else
+            return TestCaseInfo::None;
+    }
+    inline bool isReservedTag( std::string const& tag ) {
+        return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] );
+    }
+    inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+        if( isReservedTag( tag ) ) {
+            {
+                Colour colourGuard( Colour::Red );
+                Catch::cerr()
+                    << "Tag name [" << tag << "] not allowed.\n"
+                    << "Tag names starting with non alpha-numeric characters are reserved\n";
+            }
+            {
+                Colour colourGuard( Colour::FileName );
+                Catch::cerr() << _lineInfo << std::endl;
+            }
+            exit(1);
+        }
+    }
+
+    TestCase makeTestCase(  ITestCase* _testCase,
+                            std::string const& _className,
+                            std::string const& _name,
+                            std::string const& _descOrTags,
+                            SourceLineInfo const& _lineInfo )
+    {
+        bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+        // Parse out tags
+        std::set<std::string> tags;
+        std::string desc, tag;
+        bool inTag = false;
+        for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+            char c = _descOrTags[i];
+            if( !inTag ) {
+                if( c == '[' )
+                    inTag = true;
+                else
+                    desc += c;
+            }
+            else {
+                if( c == ']' ) {
+                    TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+                    if( prop == TestCaseInfo::IsHidden )
+                        isHidden = true;
+                    else if( prop == TestCaseInfo::None )
+                        enforceNotReservedTag( tag, _lineInfo );
+
+                    tags.insert( tag );
+                    tag.clear();
+                    inTag = false;
+                }
+                else
+                    tag += c;
+            }
+        }
+        if( isHidden ) {
+            tags.insert( "hide" );
+            tags.insert( "." );
+        }
+
+        TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+        return TestCase( _testCase, info );
+    }
+
+    void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
+    {
+        testCaseInfo.tags = tags;
+        testCaseInfo.lcaseTags.clear();
+
+        std::ostringstream oss;
+        for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
+            oss << '[' << *it << ']';
+            std::string lcaseTag = toLower( *it );
+            testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+            testCaseInfo.lcaseTags.insert( lcaseTag );
+        }
+        testCaseInfo.tagsAsString = oss.str();
+    }
+
+    TestCaseInfo::TestCaseInfo( std::string const& _name,
+                                std::string const& _className,
+                                std::string const& _description,
+                                std::set<std::string> const& _tags,
+                                SourceLineInfo const& _lineInfo )
+    :   name( _name ),
+        className( _className ),
+        description( _description ),
+        lineInfo( _lineInfo ),
+        properties( None )
+    {
+        setTags( *this, _tags );
+    }
+
+    TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
+    :   name( other.name ),
+        className( other.className ),
+        description( other.description ),
+        tags( other.tags ),
+        lcaseTags( other.lcaseTags ),
+        tagsAsString( other.tagsAsString ),
+        lineInfo( other.lineInfo ),
+        properties( other.properties )
+    {}
+
+    bool TestCaseInfo::isHidden() const {
+        return ( properties & IsHidden ) != 0;
+    }
+    bool TestCaseInfo::throws() const {
+        return ( properties & Throws ) != 0;
+    }
+    bool TestCaseInfo::okToFail() const {
+        return ( properties & (ShouldFail | MayFail ) ) != 0;
+    }
+    bool TestCaseInfo::expectedToFail() const {
+        return ( properties & (ShouldFail ) ) != 0;
+    }
+
+    TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+    TestCase::TestCase( TestCase const& other )
+    :   TestCaseInfo( other ),
+        test( other.test )
+    {}
+
+    TestCase TestCase::withName( std::string const& _newName ) const {
+        TestCase other( *this );
+        other.name = _newName;
+        return other;
+    }
+
+    void TestCase::swap( TestCase& other ) {
+        test.swap( other.test );
+        name.swap( other.name );
+        className.swap( other.className );
+        description.swap( other.description );
+        tags.swap( other.tags );
+        lcaseTags.swap( other.lcaseTags );
+        tagsAsString.swap( other.tagsAsString );
+        std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
+        std::swap( lineInfo, other.lineInfo );
+    }
+
+    void TestCase::invoke() const {
+        test->invoke();
+    }
+
+    bool TestCase::operator == ( TestCase const& other ) const {
+        return  test.get() == other.test.get() &&
+                name == other.name &&
+                className == other.className;
+    }
+
+    bool TestCase::operator < ( TestCase const& other ) const {
+        return name < other.name;
+    }
+    TestCase& TestCase::operator = ( TestCase const& other ) {
+        TestCase temp( other );
+        swap( temp );
+        return *this;
+    }
+
+    TestCaseInfo const& TestCase::getTestCaseInfo() const
+    {
+        return *this;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+    Version::Version
+        (   unsigned int _majorVersion,
+            unsigned int _minorVersion,
+            unsigned int _patchNumber,
+            std::string const& _branchName,
+            unsigned int _buildNumber )
+    :   majorVersion( _majorVersion ),
+        minorVersion( _minorVersion ),
+        patchNumber( _patchNumber ),
+        branchName( _branchName ),
+        buildNumber( _buildNumber )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, Version const& version ) {
+        os  << version.majorVersion << '.'
+            << version.minorVersion << '.'
+            << version.patchNumber;
+
+        if( !version.branchName.empty() ) {
+            os  << '-' << version.branchName
+                << '.' << version.buildNumber;
+        }
+        return os;
+    }
+
+    Version libraryVersion( 1, 8, 1, "", 0 );
+
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+namespace Catch {
+
+    MessageInfo::MessageInfo(   std::string const& _macroName,
+                                SourceLineInfo const& _lineInfo,
+                                ResultWas::OfType _type )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        type( _type ),
+        sequence( ++globalCount )
+    {}
+
+    // This may need protecting if threading support is added
+    unsigned int MessageInfo::globalCount = 0;
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+    : m_info( builder.m_info )
+    {
+        m_info.message = builder.m_stream.str();
+        getResultCapture().pushScopedMessage( m_info );
+    }
+    ScopedMessage::ScopedMessage( ScopedMessage const& other )
+    : m_info( other.m_info )
+    {}
+
+    ScopedMessage::~ScopedMessage() {
+        getResultCapture().popScopedMessage( m_info );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_legacy_reporter_adapter.hpp
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
+
+// #included from: catch_legacy_reporter_adapter.h
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
+
+namespace Catch
+{
+    // Deprecated
+    struct IReporter : IShared {
+        virtual ~IReporter();
+
+        virtual bool shouldRedirectStdout() const = 0;
+
+        virtual void StartTesting() = 0;
+        virtual void EndTesting( Totals const& totals ) = 0;
+        virtual void StartGroup( std::string const& groupName ) = 0;
+        virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
+        virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
+        virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
+        virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
+        virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
+        virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
+        virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
+        virtual void Aborted() = 0;
+        virtual void Result( AssertionResult const& result ) = 0;
+    };
+
+    class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
+    {
+    public:
+        LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
+        virtual ~LegacyReporterAdapter();
+
+        virtual ReporterPreferences getPreferences() const;
+        virtual void noMatchingTestCases( std::string const& );
+        virtual void testRunStarting( TestRunInfo const& );
+        virtual void testGroupStarting( GroupInfo const& groupInfo );
+        virtual void testCaseStarting( TestCaseInfo const& testInfo );
+        virtual void sectionStarting( SectionInfo const& sectionInfo );
+        virtual void assertionStarting( AssertionInfo const& );
+        virtual bool assertionEnded( AssertionStats const& assertionStats );
+        virtual void sectionEnded( SectionStats const& sectionStats );
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats );
+        virtual void testRunEnded( TestRunStats const& testRunStats );
+        virtual void skipTest( TestCaseInfo const& );
+
+    private:
+        Ptr<IReporter> m_legacyReporter;
+    };
+}
+
+namespace Catch
+{
+    LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
+    :   m_legacyReporter( legacyReporter )
+    {}
+    LegacyReporterAdapter::~LegacyReporterAdapter() {}
+
+    ReporterPreferences LegacyReporterAdapter::getPreferences() const {
+        ReporterPreferences prefs;
+        prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
+        return prefs;
+    }
+
+    void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
+    void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
+        m_legacyReporter->StartTesting();
+    }
+    void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
+        m_legacyReporter->StartGroup( groupInfo.name );
+    }
+    void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        m_legacyReporter->StartTestCase( testInfo );
+    }
+    void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
+        m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
+    }
+    void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
+        // Not on legacy interface
+    }
+
+    bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
+        if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+            for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+                    it != itEnd;
+                    ++it ) {
+                if( it->type == ResultWas::Info ) {
+                    ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
+                    rb << it->message;
+                    rb.setResultType( ResultWas::Info );
+                    AssertionResult result = rb.build();
+                    m_legacyReporter->Result( result );
+                }
+            }
+        }
+        m_legacyReporter->Result( assertionStats.assertionResult );
+        return true;
+    }
+    void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
+        if( sectionStats.missingAssertions )
+            m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
+        m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
+    }
+    void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        m_legacyReporter->EndTestCase
+            (   testCaseStats.testInfo,
+                testCaseStats.totals,
+                testCaseStats.stdOut,
+                testCaseStats.stdErr );
+    }
+    void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        if( testGroupStats.aborting )
+            m_legacyReporter->Aborted();
+        m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
+    }
+    void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
+        m_legacyReporter->EndTesting( testRunStats.totals );
+    }
+    void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
+    }
+}
+
+// #included from: catch_timer.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif
+
+#ifdef CATCH_PLATFORM_WINDOWS
+
+#else
+
+#include <sys/time.h>
+
+#endif
+
+namespace Catch {
+
+    namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+        uint64_t getCurrentTicks() {
+            static uint64_t hz=0, hzo=0;
+            if (!hz) {
+                QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
+                QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
+            }
+            uint64_t t;
+            QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
+            return ((t-hzo)*1000000)/hz;
+        }
+#else
+        uint64_t getCurrentTicks() {
+            timeval t;
+            gettimeofday(&t,CATCH_NULL);
+            return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
+        }
+#endif
+    }
+
+    void Timer::start() {
+        m_ticks = getCurrentTicks();
+    }
+    unsigned int Timer::getElapsedMicroseconds() const {
+        return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+    }
+    unsigned int Timer::getElapsedMilliseconds() const {
+        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+    }
+    double Timer::getElapsedSeconds() const {
+        return getElapsedMicroseconds()/1000000.0;
+    }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+// #included from: catch_common.hpp
+#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+
+#include <cstring>
+#include <cctype>
+
+namespace Catch {
+
+    bool startsWith( std::string const& s, std::string const& prefix ) {
+        return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+    }
+    bool startsWith( std::string const& s, char prefix ) {
+        return !s.empty() && s[0] == prefix;
+    }
+    bool endsWith( std::string const& s, std::string const& suffix ) {
+        return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+    }
+    bool endsWith( std::string const& s, char suffix ) {
+        return !s.empty() && s[s.size()-1] == suffix;
+    }
+    bool contains( std::string const& s, std::string const& infix ) {
+        return s.find( infix ) != std::string::npos;
+    }
+    char toLowerCh(char c) {
+        return static_cast<char>( std::tolower( c ) );
+    }
+    void toLowerInPlace( std::string& s ) {
+        std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+    }
+    std::string toLower( std::string const& s ) {
+        std::string lc = s;
+        toLowerInPlace( lc );
+        return lc;
+    }
+    std::string trim( std::string const& str ) {
+        static char const* whitespaceChars = "\n\r\t ";
+        std::string::size_type start = str.find_first_not_of( whitespaceChars );
+        std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+        return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+    }
+
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+        bool replaced = false;
+        std::size_t i = str.find( replaceThis );
+        while( i != std::string::npos ) {
+            replaced = true;
+            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+            if( i < str.size()-withThis.size() )
+                i = str.find( replaceThis, i+withThis.size() );
+            else
+                i = std::string::npos;
+        }
+        return replaced;
+    }
+
+    pluralise::pluralise( std::size_t count, std::string const& label )
+    :   m_count( count ),
+        m_label( label )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+        os << pluraliser.m_count << ' ' << pluraliser.m_label;
+        if( pluraliser.m_count != 1 )
+            os << 's';
+        return os;
+    }
+
+    SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){}
+    SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+    :   file( _file ),
+        line( _line )
+    {}
+    bool SourceLineInfo::empty() const {
+        return file[0] == '\0';
+    }
+    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+        return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+    }
+    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
+        return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0));
+    }
+
+    void seedRng( IConfig const& config ) {
+        if( config.rngSeed() != 0 )
+            std::srand( config.rngSeed() );
+    }
+    unsigned int rngSeed() {
+        return getCurrentContext().getConfig()->rngSeed();
+    }
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+        os << info.file << '(' << info.line << ')';
+#else
+        os << info.file << ':' << info.line;
+#endif
+        return os;
+    }
+
+    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
+        std::ostringstream oss;
+        oss << locationInfo << ": Internal Catch error: '" << message << '\'';
+        if( alwaysTrue() )
+            throw std::logic_error( oss.str() );
+    }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+    SectionInfo::SectionInfo
+        (   SourceLineInfo const& _lineInfo,
+            std::string const& _name,
+            std::string const& _description )
+    :   name( _name ),
+        description( _description ),
+        lineInfo( _lineInfo )
+    {}
+
+    Section::Section( SectionInfo const& info )
+    :   m_info( info ),
+        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+    {
+        m_timer.start();
+    }
+
+    Section::~Section() {
+        if( m_sectionIncluded ) {
+            SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
+            if( std::uncaught_exception() )
+                getResultCapture().sectionEndedEarly( endInfo );
+            else
+                getResultCapture().sectionEnded( endInfo );
+        }
+    }
+
+    // This indicates whether the section should be executed or not
+    Section::operator bool() const {
+        return m_sectionIncluded;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#ifdef CATCH_PLATFORM_MAC
+
+    #include <assert.h>
+    #include <stdbool.h>
+    #include <sys/types.h>
+    #include <unistd.h>
+    #include <sys/sysctl.h>
+
+    namespace Catch{
+
+        // The following function is taken directly from the following technical note:
+        // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+
+        // Returns true if the current process is being debugged (either
+        // running under the debugger or has a debugger attached post facto).
+        bool isDebuggerActive(){
+
+            int                 mib[4];
+            struct kinfo_proc   info;
+            size_t              size;
+
+            // Initialize the flags so that, if sysctl fails for some bizarre
+            // reason, we get a predictable result.
+
+            info.kp_proc.p_flag = 0;
+
+            // Initialize mib, which tells sysctl the info we want, in this case
+            // we're looking for information about a specific process ID.
+
+            mib[0] = CTL_KERN;
+            mib[1] = KERN_PROC;
+            mib[2] = KERN_PROC_PID;
+            mib[3] = getpid();
+
+            // Call sysctl.
+
+            size = sizeof(info);
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) {
+                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+                return false;
+            }
+
+            // We're being debugged if the P_TRACED flag is set.
+
+            return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+        }
+    } // namespace Catch
+
+#elif defined(CATCH_PLATFORM_LINUX)
+    #include <fstream>
+    #include <string>
+
+    namespace Catch{
+        // The standard POSIX way of detecting a debugger is to attempt to
+        // ptrace() the process, but this needs to be done from a child and not
+        // this process itself to still allow attaching to this process later
+        // if wanted, so is rather heavy. Under Linux we have the PID of the
+        // "debugger" (which doesn't need to be gdb, of course, it could also
+        // be strace, for example) in /proc/$PID/status, so just get it from
+        // there instead.
+        bool isDebuggerActive(){
+            std::ifstream in("/proc/self/status");
+            for( std::string line; std::getline(in, line); ) {
+                static const int PREFIX_LEN = 11;
+                if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+                    // We're traced if the PID is not 0 and no other PID starts
+                    // with 0 digit, so it's enough to check for just a single
+                    // character.
+                    return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+                }
+            }
+
+            return false;
+        }
+    } // namespace Catch
+#elif defined(_MSC_VER)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#else
+    namespace Catch {
+       inline bool isDebuggerActive() { return false; }
+    }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            ::OutputDebugStringA( text.c_str() );
+        }
+    }
+#else
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            // !TBD: Need a version for Mac/ XCode and other IDEs
+            Catch::cout() << text;
+        }
+    }
+#endif // Platform
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+    const std::string unprintableString = "{?}";
+
+    namespace {
+        const int hexThreshold = 255;
+
+        struct Endianness {
+            enum Arch { Big, Little };
+
+            static Arch which() {
+                union _{
+                    int asInt;
+                    char asChar[sizeof (int)];
+                } u;
+
+                u.asInt = 1;
+                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+            }
+        };
+    }
+
+    std::string rawMemoryToString( const void *object, std::size_t size )
+    {
+        // Reverse order for little endian architectures
+        int i = 0, end = static_cast<int>( size ), inc = 1;
+        if( Endianness::which() == Endianness::Little ) {
+            i = end-1;
+            end = inc = -1;
+        }
+
+        unsigned char const *bytes = static_cast<unsigned char const *>(object);
+        std::ostringstream os;
+        os << "0x" << std::setfill('0') << std::hex;
+        for( ; i != end; i += inc )
+             os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+       return os.str();
+    }
+}
+
+std::string toString( std::string const& value ) {
+    std::string s = value;
+    if( getCurrentContext().getConfig()->showInvisibles() ) {
+        for(size_t i = 0; i < s.size(); ++i ) {
+            std::string subs;
+            switch( s[i] ) {
+            case '\n': subs = "\\n"; break;
+            case '\t': subs = "\\t"; break;
+            default: break;
+            }
+            if( !subs.empty() ) {
+                s = s.substr( 0, i ) + subs + s.substr( i+1 );
+                ++i;
+            }
+        }
+    }
+    return '"' + s + '"';
+}
+std::string toString( std::wstring const& value ) {
+
+    std::string s;
+    s.reserve( value.size() );
+    for(size_t i = 0; i < value.size(); ++i )
+        s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
+    return Catch::toString( s );
+}
+
+std::string toString( const char* const value ) {
+    return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+    return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+    return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+    return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ')';
+    return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ')';
+    return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+    return Catch::toString( static_cast<unsigned long>( value ) );
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+    std::ostringstream oss;
+    oss << std::setprecision( precision )
+        << std::fixed
+        << value;
+    std::string d = oss.str();
+    std::size_t i = d.find_last_not_of( '0' );
+    if( i != std::string::npos && i != d.size()-1 ) {
+        if( d[i] == '.' )
+            i++;
+        d = d.substr( 0, i+1 );
+    }
+    return d;
+}
+
+std::string toString( const double value ) {
+    return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+    return fpToString( value, 5 ) + 'f';
+}
+
+std::string toString( bool value ) {
+    return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+    if ( value == '\r' )
+        return "'\\r'";
+    if ( value == '\f' )
+        return "'\\f'";
+    if ( value == '\n' )
+        return "'\\n'";
+    if ( value == '\t' )
+        return "'\\t'";
+    if ( '\0' <= value && value < ' ' )
+        return toString( static_cast<unsigned int>( value ) );
+    char chstr[] = "' '";
+    chstr[1] = value;
+    return chstr;
+}
+
+std::string toString( signed char value ) {
+    return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+    return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ')';
+    return oss.str();
+}
+std::string toString( unsigned long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ')';
+    return oss.str();
+}
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+    return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring ) {
+        if( !nsstring )
+            return "nil";
+        return "@" + toString([nsstring UTF8String]);
+    }
+    std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
+        if( !nsstring )
+            return "nil";
+        return "@" + toString([nsstring UTF8String]);
+    }
+    std::string toString( NSObject* const& nsObject ) {
+        return toString( [nsObject description] );
+    }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+    std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) {
+        return secondArg.empty() || secondArg == "\"\""
+            ? capturedExpression
+            : capturedExpression + ", " + secondArg;
+    }
+    ResultBuilder::ResultBuilder(   char const* macroName,
+                                    SourceLineInfo const& lineInfo,
+                                    char const* capturedExpression,
+                                    ResultDisposition::Flags resultDisposition,
+                                    char const* secondArg )
+    :   m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ),
+        m_shouldDebugBreak( false ),
+        m_shouldThrow( false )
+    {}
+
+    ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+        m_data.resultType = result;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setResultType( bool result ) {
+        m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+        return *this;
+    }
+
+    void ResultBuilder::endExpression( DecomposedExpression const& expr ) {
+        AssertionResult result = build( expr );
+        handleResult( result );
+    }
+
+    void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+        m_assertionInfo.resultDisposition = resultDisposition;
+        m_stream.oss << Catch::translateActiveException();
+        captureResult( ResultWas::ThrewException );
+    }
+
+    void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+        setResultType( resultType );
+        captureExpression();
+    }
+
+    void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
+        if( expectedMessage.empty() )
+            captureExpectedException( Matchers::Impl::MatchAllOf<std::string>() );
+        else
+            captureExpectedException( Matchers::Equals( expectedMessage ) );
+    }
+
+    void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher ) {
+
+        assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
+        AssertionResultData data = m_data;
+        data.resultType = ResultWas::Ok;
+        data.reconstructedExpression = m_assertionInfo.capturedExpression;
+
+        std::string actualMessage = Catch::translateActiveException();
+        if( !matcher.match( actualMessage ) ) {
+            data.resultType = ResultWas::ExpressionFailed;
+            data.reconstructedExpression = actualMessage;
+        }
+        AssertionResult result( m_assertionInfo, data );
+        handleResult( result );
+    }
+
+    void ResultBuilder::captureExpression() {
+        AssertionResult result = build();
+        handleResult( result );
+    }
+
+    void ResultBuilder::handleResult( AssertionResult const& result )
+    {
+        getResultCapture().assertionEnded( result );
+
+        if( !result.isOk() ) {
+            if( getCurrentContext().getConfig()->shouldDebugBreak() )
+                m_shouldDebugBreak = true;
+            if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
+                m_shouldThrow = true;
+        }
+    }
+
+    void ResultBuilder::react() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+        if (m_shouldDebugBreak) {
+            ///////////////////////////////////////////////////////////////////
+            // To inspect the state during test, you need to go one level up the callstack
+            // To go back to the test and change execution, jump over the throw statement
+            ///////////////////////////////////////////////////////////////////
+            CATCH_BREAK_INTO_DEBUGGER();
+        }
+#endif
+        if( m_shouldThrow )
+            throw Catch::TestFailureException();
+    }
+
+    bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+    bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+    AssertionResult ResultBuilder::build() const
+    {
+        return build( *this );
+    }
+
+    // CAVEAT: The returned AssertionResult stores a pointer to the argument expr,
+    //         a temporary DecomposedExpression, which in turn holds references to
+    //         operands, possibly temporary as well.
+    //         It should immediately be passed to handleResult; if the expression
+    //         needs to be reported, its string expansion must be composed before
+    //         the temporaries are destroyed.
+    AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const
+    {
+        assert( m_data.resultType != ResultWas::Unknown );
+        AssertionResultData data = m_data;
+
+        // Flip bool results if FalseTest flag is set
+        if( isFalseTest( m_assertionInfo.resultDisposition ) ) {
+            data.negate( expr.isBinaryExpression() );
+        }
+
+        data.message = m_stream.oss.str();
+        data.decomposedExpression = &expr; // for lazy reconstruction
+        return AssertionResult( m_assertionInfo, data );
+    }
+
+    void ResultBuilder::reconstructExpression( std::string& dest ) const {
+        dest = m_assertionInfo.capturedExpression;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+    class TagAliasRegistry : public ITagAliasRegistry {
+    public:
+        virtual ~TagAliasRegistry();
+        virtual Option<TagAlias> find( std::string const& alias ) const;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
+        void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+        static TagAliasRegistry& get();
+
+    private:
+        std::map<std::string, TagAlias> m_registry;
+    };
+
+} // end namespace Catch
+
+namespace Catch {
+
+    TagAliasRegistry::~TagAliasRegistry() {}
+
+    Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
+        std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
+        if( it != m_registry.end() )
+            return it->second;
+        else
+            return Option<TagAlias>();
+    }
+
+    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+        std::string expandedTestSpec = unexpandedTestSpec;
+        for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
+                it != itEnd;
+                ++it ) {
+            std::size_t pos = expandedTestSpec.find( it->first );
+            if( pos != std::string::npos ) {
+                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
+                                    it->second.tag +
+                                    expandedTestSpec.substr( pos + it->first.size() );
+            }
+        }
+        return expandedTestSpec;
+    }
+
+    void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+
+        if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) {
+            std::ostringstream oss;
+            oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
+            throw std::domain_error( oss.str().c_str() );
+        }
+        if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+            std::ostringstream oss;
+            oss << "error: tag alias, \"" << alias << "\" already registered.\n"
+                << "\tFirst seen at " << find(alias)->lineInfo << '\n'
+                << "\tRedefined at " << lineInfo;
+            throw std::domain_error( oss.str().c_str() );
+        }
+    }
+
+    TagAliasRegistry& TagAliasRegistry::get() {
+        static TagAliasRegistry instance;
+        return instance;
+
+    }
+
+    ITagAliasRegistry::~ITagAliasRegistry() {}
+    ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
+
+    RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+        try {
+            TagAliasRegistry::get().add( alias, tag, lineInfo );
+        }
+        catch( std::exception& ex ) {
+            Colour colourGuard( Colour::Red );
+            Catch::cerr() << ex.what() << std::endl;
+            exit(1);
+        }
+    }
+
+} // end namespace Catch
+
+// #included from: catch_matchers_string.hpp
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_str( adjustString( str ) )
+        {}
+        std::string CasedString::adjustString( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? toLower( str )
+                   : str;
+        }
+        std::string CasedString::caseSensitivitySuffix() const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? " (case insensitive)"
+                   : std::string();
+        }
+
+        StringMatcherBase::StringMatcherBase( std::string operation, CasedString const& comparator )
+        : m_comparator( comparator ),
+          m_operation( operation ) {
+        }
+
+        std::string StringMatcherBase::describe() const {
+            std::string description;
+            description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+                                        m_comparator.caseSensitivitySuffix().size());
+            description += m_operation;
+            description += ": \"";
+            description += m_comparator.m_str;
+            description += "\"";
+            description += m_comparator.caseSensitivitySuffix();
+            return description;
+        }
+
+        EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+        bool EqualsMatcher::match( std::string const& source ) const {
+            return m_comparator.adjustString( source ) == m_comparator.m_str;
+        }
+
+        ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+        bool ContainsMatcher::match( std::string const& source ) const {
+            return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+        bool StartsWithMatcher::match( std::string const& source ) const {
+            return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+        bool EndsWithMatcher::match( std::string const& source ) const {
+            return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+    } // namespace StdString
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+
+} // namespace Matchers
+} // namespace Catch
+// #included from: ../reporters/catch_reporter_multi.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+namespace Catch {
+
+class MultipleReporters : public SharedImpl<IStreamingReporter> {
+    typedef std::vector<Ptr<IStreamingReporter> > Reporters;
+    Reporters m_reporters;
+
+public:
+    void add( Ptr<IStreamingReporter> const& reporter ) {
+        m_reporters.push_back( reporter );
+    }
+
+public: // IStreamingReporter
+
+    virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+        return m_reporters[0]->getPreferences();
+    }
+
+    virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->noMatchingTestCases( spec );
+    }
+
+    virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunStarting( testRunInfo );
+    }
+
+    virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupStarting( groupInfo );
+    }
+
+    virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseStarting( testInfo );
+    }
+
+    virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionStarting( sectionInfo );
+    }
+
+    virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->assertionStarting( assertionInfo );
+    }
+
+    // The return value indicates if the messages buffer should be cleared:
+    virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+        bool clearBuffer = false;
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            clearBuffer |= (*it)->assertionEnded( assertionStats );
+        return clearBuffer;
+    }
+
+    virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionEnded( sectionStats );
+    }
+
+    virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseEnded( testCaseStats );
+    }
+
+    virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupEnded( testGroupStats );
+    }
+
+    virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunEnded( testRunStats );
+    }
+
+    virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->skipTest( testInfo );
+    }
+
+    virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE {
+        return this;
+    }
+
+};
+
+Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
+    Ptr<IStreamingReporter> resultingReporter;
+
+    if( existingReporter ) {
+        MultipleReporters* multi = existingReporter->tryAsMulti();
+        if( !multi ) {
+            multi = new MultipleReporters;
+            resultingReporter = Ptr<IStreamingReporter>( multi );
+            if( existingReporter )
+                multi->add( existingReporter );
+        }
+        else
+            resultingReporter = existingReporter;
+        multi->add( additionalReporter );
+    }
+    else
+        resultingReporter = additionalReporter;
+
+    return resultingReporter;
+}
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+#include <cstring>
+#include <assert.h>
+
+namespace Catch {
+
+    struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+        StreamingReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
+
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
+
+        virtual ~StreamingReporterBase() CATCH_OVERRIDE;
+
+        virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
+
+        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
+            currentTestRunInfo = _testRunInfo;
+        }
+        virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
+            currentGroupInfo = _groupInfo;
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
+            currentTestCaseInfo = _testInfo;
+        }
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+            m_sectionStack.push_back( _sectionInfo );
+        }
+
+        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
+            m_sectionStack.pop_back();
+        }
+        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
+            currentTestCaseInfo.reset();
+        }
+        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
+            currentGroupInfo.reset();
+        }
+        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
+            currentTestCaseInfo.reset();
+            currentGroupInfo.reset();
+            currentTestRunInfo.reset();
+        }
+
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
+            // Don't do anything with this by default.
+            // It can optionally be overridden in the derived class.
+        }
+
+        Ptr<IConfig const> m_config;
+        std::ostream& stream;
+
+        LazyStat<TestRunInfo> currentTestRunInfo;
+        LazyStat<GroupInfo> currentGroupInfo;
+        LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+        std::vector<SectionInfo> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
+    };
+
+    struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+        template<typename T, typename ChildNodeT>
+        struct Node : SharedImpl<> {
+            explicit Node( T const& _value ) : value( _value ) {}
+            virtual ~Node() {}
+
+            typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+            T value;
+            ChildNodes children;
+        };
+        struct SectionNode : SharedImpl<> {
+            explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+            virtual ~SectionNode();
+
+            bool operator == ( SectionNode const& other ) const {
+                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+            }
+            bool operator == ( Ptr<SectionNode> const& other ) const {
+                return operator==( *other );
+            }
+
+            SectionStats stats;
+            typedef std::vector<Ptr<SectionNode> > ChildSections;
+            typedef std::vector<AssertionStats> Assertions;
+            ChildSections childSections;
+            Assertions assertions;
+            std::string stdOut;
+            std::string stdErr;
+        };
+
+        struct BySectionInfo {
+            BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+            BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+            bool operator() ( Ptr<SectionNode> const& node ) const {
+                return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
+            }
+        private:
+            void operator=( BySectionInfo const& );
+            SectionInfo const& m_other;
+        };
+
+        typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+        typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+        typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+        CumulativeReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
+        ~CumulativeReporterBase();
+
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
+
+        virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
+        virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
+
+        virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+            SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+            Ptr<SectionNode> node;
+            if( m_sectionStack.empty() ) {
+                if( !m_rootSection )
+                    m_rootSection = new SectionNode( incompleteStats );
+                node = m_rootSection;
+            }
+            else {
+                SectionNode& parentNode = *m_sectionStack.back();
+                SectionNode::ChildSections::const_iterator it =
+                    std::find_if(   parentNode.childSections.begin(),
+                                    parentNode.childSections.end(),
+                                    BySectionInfo( sectionInfo ) );
+                if( it == parentNode.childSections.end() ) {
+                    node = new SectionNode( incompleteStats );
+                    parentNode.childSections.push_back( node );
+                }
+                else
+                    node = *it;
+            }
+            m_sectionStack.push_back( node );
+            m_deepestSection = node;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+            assert( !m_sectionStack.empty() );
+            SectionNode& sectionNode = *m_sectionStack.back();
+            sectionNode.assertions.push_back( assertionStats );
+            // AssertionResult holds a pointer to a temporary DecomposedExpression,
+            // which getExpandedExpression() calls to build the expression string.
+            // Our section stack copy of the assertionResult will likely outlive the
+            // temporary, so it must be expanded or discarded now to avoid calling
+            // a destroyed object later.
+            prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
+            return true;
+        }
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+            assert( !m_sectionStack.empty() );
+            SectionNode& node = *m_sectionStack.back();
+            node.stats = sectionStats;
+            m_sectionStack.pop_back();
+        }
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+            Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
+            assert( m_sectionStack.size() == 0 );
+            node->children.push_back( m_rootSection );
+            m_testCases.push_back( node );
+            m_rootSection.reset();
+
+            assert( m_deepestSection );
+            m_deepestSection->stdOut = testCaseStats.stdOut;
+            m_deepestSection->stdErr = testCaseStats.stdErr;
+        }
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+            Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+            node->children.swap( m_testCases );
+            m_testGroups.push_back( node );
+        }
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+            Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+            node->children.swap( m_testGroups );
+            m_testRuns.push_back( node );
+            testRunEndedCumulative();
+        }
+        virtual void testRunEndedCumulative() = 0;
+
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+        virtual void prepareExpandedExpression( AssertionResult& result ) const {
+            if( result.isOk() )
+                result.discardDecomposedExpression();
+            else
+                result.expandDecomposedExpression();
+        }
+
+        Ptr<IConfig const> m_config;
+        std::ostream& stream;
+        std::vector<AssertionStats> m_assertions;
+        std::vector<std::vector<Ptr<SectionNode> > > m_sections;
+        std::vector<Ptr<TestCaseNode> > m_testCases;
+        std::vector<Ptr<TestGroupNode> > m_testGroups;
+
+        std::vector<Ptr<TestRunNode> > m_testRuns;
+
+        Ptr<SectionNode> m_rootSection;
+        Ptr<SectionNode> m_deepestSection;
+        std::vector<Ptr<SectionNode> > m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
+
+    };
+
+    template<char C>
+    char const* getLineOfChars() {
+        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+        if( !*line ) {
+            std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+        }
+        return line;
+    }
+
+    struct TestEventListenerBase : StreamingReporterBase {
+        TestEventListenerBase( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config )
+        {}
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+        virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
+            return false;
+        }
+    };
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+    template<typename T>
+    class LegacyReporterRegistrar {
+
+        class ReporterFactory : public IReporterFactory {
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new LegacyReporterAdapter( new T( config ) );
+            }
+
+            virtual std::string getDescription() const {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        LegacyReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+        }
+    };
+
+    template<typename T>
+    class ReporterRegistrar {
+
+        class ReporterFactory : public SharedImpl<IReporterFactory> {
+
+            // *** Please Note ***:
+            // - If you end up here looking at a compiler error because it's trying to register
+            // your custom reporter class be aware that the native reporter interface has changed
+            // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
+            // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
+            // However please consider updating to the new interface as the old one is now
+            // deprecated and will probably be removed quite soon!
+            // Please contact me via github if you have any questions at all about this.
+            // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
+            // no idea who is actually using custom reporters at all (possibly no-one!).
+            // The new interface is designed to minimise exposure to interface changes in the future.
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new T( config );
+            }
+
+            virtual std::string getDescription() const {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        ReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+        }
+    };
+
+    template<typename T>
+    class ListenerRegistrar {
+
+        class ListenerFactory : public SharedImpl<IReporterFactory> {
+
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new T( config );
+            }
+            virtual std::string getDescription() const {
+                return std::string();
+            }
+        };
+
+    public:
+
+        ListenerRegistrar() {
+            getMutableRegistryHub().registerListener( new ListenerFactory() );
+        }
+    };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+    namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+    namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
+    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iomanip>
+
+namespace Catch {
+
+    class XmlEncode {
+    public:
+        enum ForWhat { ForTextNodes, ForAttributes };
+
+        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
+        :   m_str( str ),
+            m_forWhat( forWhat )
+        {}
+
+        void encodeTo( std::ostream& os ) const {
+
+            // Apostrophe escaping not necessary if we always use " to write attributes
+            // (see: http://www.w3.org/TR/xml/#syntax)
+
+            for( std::size_t i = 0; i < m_str.size(); ++ i ) {
+                char c = m_str[i];
+                switch( c ) {
+                    case '<':   os << "&lt;"; break;
+                    case '&':   os << "&amp;"; break;
+
+                    case '>':
+                        // See: http://www.w3.org/TR/xml/#syntax
+                        if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
+                            os << "&gt;";
+                        else
+                            os << c;
+                        break;
+
+                    case '\"':
+                        if( m_forWhat == ForAttributes )
+                            os << "&quot;";
+                        else
+                            os << c;
+                        break;
+
+                    default:
+                        // Escape control chars - based on contribution by @espenalb in PR #465 and
+                        // by @mrpi PR #588
+                        if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
+                            // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+                            os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+                               << static_cast<int>( c );
+                        }
+                        else
+                            os << c;
+                }
+            }
+        }
+
+        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+            xmlEncode.encodeTo( os );
+            return os;
+        }
+
+    private:
+        std::string m_str;
+        ForWhat m_forWhat;
+    };
+
+    class XmlWriter {
+    public:
+
+        class ScopedElement {
+        public:
+            ScopedElement( XmlWriter* writer )
+            :   m_writer( writer )
+            {}
+
+            ScopedElement( ScopedElement const& other )
+            :   m_writer( other.m_writer ){
+                other.m_writer = CATCH_NULL;
+            }
+
+            ~ScopedElement() {
+                if( m_writer )
+                    m_writer->endElement();
+            }
+
+            ScopedElement& writeText( std::string const& text, bool indent = true ) {
+                m_writer->writeText( text, indent );
+                return *this;
+            }
+
+            template<typename T>
+            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+                m_writer->writeAttribute( name, attribute );
+                return *this;
+            }
+
+        private:
+            mutable XmlWriter* m_writer;
+        };
+
+        XmlWriter()
+        :   m_tagIsOpen( false ),
+            m_needsNewline( false ),
+            m_os( Catch::cout() )
+        {
+            writeDeclaration();
+        }
+
+        XmlWriter( std::ostream& os )
+        :   m_tagIsOpen( false ),
+            m_needsNewline( false ),
+            m_os( os )
+        {
+            writeDeclaration();
+        }
+
+        ~XmlWriter() {
+            while( !m_tags.empty() )
+                endElement();
+        }
+
+        XmlWriter& startElement( std::string const& name ) {
+            ensureTagClosed();
+            newlineIfNecessary();
+            m_os << m_indent << '<' << name;
+            m_tags.push_back( name );
+            m_indent += "  ";
+            m_tagIsOpen = true;
+            return *this;
+        }
+
+        ScopedElement scopedElement( std::string const& name ) {
+            ScopedElement scoped( this );
+            startElement( name );
+            return scoped;
+        }
+
+        XmlWriter& endElement() {
+            newlineIfNecessary();
+            m_indent = m_indent.substr( 0, m_indent.size()-2 );
+            if( m_tagIsOpen ) {
+                m_os << "/>";
+                m_tagIsOpen = false;
+            }
+            else {
+                m_os << m_indent << "</" << m_tags.back() << ">";
+            }
+            m_os << std::endl;
+            m_tags.pop_back();
+            return *this;
+        }
+
+        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+            if( !name.empty() && !attribute.empty() )
+                m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+            return *this;
+        }
+
+        XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+            m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+            return *this;
+        }
+
+        template<typename T>
+        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+            std::ostringstream oss;
+            oss << attribute;
+            return writeAttribute( name, oss.str() );
+        }
+
+        XmlWriter& writeText( std::string const& text, bool indent = true ) {
+            if( !text.empty() ){
+                bool tagWasOpen = m_tagIsOpen;
+                ensureTagClosed();
+                if( tagWasOpen && indent )
+                    m_os << m_indent;
+                m_os << XmlEncode( text );
+                m_needsNewline = true;
+            }
+            return *this;
+        }
+
+        XmlWriter& writeComment( std::string const& text ) {
+            ensureTagClosed();
+            m_os << m_indent << "<!--" << text << "-->";
+            m_needsNewline = true;
+            return *this;
+        }
+
+        void writeStylesheetRef( std::string const& url ) {
+            m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+        }
+
+        XmlWriter& writeBlankLine() {
+            ensureTagClosed();
+            m_os << '\n';
+            return *this;
+        }
+
+        void ensureTagClosed() {
+            if( m_tagIsOpen ) {
+                m_os << ">" << std::endl;
+                m_tagIsOpen = false;
+            }
+        }
+
+    private:
+        XmlWriter( XmlWriter const& );
+        void operator=( XmlWriter const& );
+
+        void writeDeclaration() {
+            m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+        }
+
+        void newlineIfNecessary() {
+            if( m_needsNewline ) {
+                m_os << std::endl;
+                m_needsNewline = false;
+            }
+        }
+
+        bool m_tagIsOpen;
+        bool m_needsNewline;
+        std::vector<std::string> m_tags;
+        std::string m_indent;
+        std::ostream& m_os;
+    };
+
+}
+// #included from: catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#    ifdef __ICC // icpc defines the __clang__ macro
+#        pragma warning(pop)
+#    else
+#        pragma clang diagnostic pop
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic pop
+#endif
+
+
+namespace Catch {
+    class XmlReporter : public StreamingReporterBase {
+    public:
+        XmlReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config ),
+            m_xml(_config.stream()),
+            m_sectionDepth( 0 )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
+
+        virtual ~XmlReporter() CATCH_OVERRIDE;
+
+        static std::string getDescription() {
+            return "Reports test results as an XML document";
+        }
+
+        virtual std::string getStylesheetRef() const {
+            return std::string();
+        }
+
+        void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+            m_xml
+                .writeAttribute( "filename", sourceInfo.file )
+                .writeAttribute( "line", sourceInfo.line );
+        }
+
+    public: // StreamingReporterBase
+
+        virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
+            StreamingReporterBase::noMatchingTestCases( s );
+        }
+
+        virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
+            StreamingReporterBase::testRunStarting( testInfo );
+            std::string stylesheetRef = getStylesheetRef();
+            if( !stylesheetRef.empty() )
+                m_xml.writeStylesheetRef( stylesheetRef );
+            m_xml.startElement( "Catch" );
+            if( !m_config->name().empty() )
+                m_xml.writeAttribute( "name", m_config->name() );
+        }
+
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+            StreamingReporterBase::testGroupStarting( groupInfo );
+            m_xml.startElement( "Group" )
+                .writeAttribute( "name", groupInfo.name );
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+            StreamingReporterBase::testCaseStarting(testInfo);
+            m_xml.startElement( "TestCase" )
+                .writeAttribute( "name", trim( testInfo.name ) )
+                .writeAttribute( "description", testInfo.description )
+                .writeAttribute( "tags", testInfo.tagsAsString );
+
+            writeSourceInfo( testInfo.lineInfo );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                m_testCaseTimer.start();
+            m_xml.ensureTagClosed();
+        }
+
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+            StreamingReporterBase::sectionStarting( sectionInfo );
+            if( m_sectionDepth++ > 0 ) {
+                m_xml.startElement( "Section" )
+                    .writeAttribute( "name", trim( sectionInfo.name ) )
+                    .writeAttribute( "description", sectionInfo.description );
+                writeSourceInfo( sectionInfo.lineInfo );
+                m_xml.ensureTagClosed();
+            }
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+            const AssertionResult& assertionResult = assertionStats.assertionResult;
+
+            // Print any info messages in <Info> tags.
+            if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+                for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+                        it != itEnd;
+                        ++it ) {
+                    if( it->type == ResultWas::Info ) {
+                        m_xml.scopedElement( "Info" )
+                            .writeText( it->message );
+                    } else if ( it->type == ResultWas::Warning ) {
+                        m_xml.scopedElement( "Warning" )
+                            .writeText( it->message );
+                    }
+                }
+            }
+
+            // Drop out if result was successful but we're not printing them.
+            if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) )
+                return true;
+
+            // Print the expression if there is one.
+            if( assertionResult.hasExpression() ) {
+                m_xml.startElement( "Expression" )
+                    .writeAttribute( "success", assertionResult.succeeded() )
+                    .writeAttribute( "type", assertionResult.getTestMacroName() );
+
+                writeSourceInfo( assertionResult.getSourceInfo() );
+
+                m_xml.scopedElement( "Original" )
+                    .writeText( assertionResult.getExpression() );
+                m_xml.scopedElement( "Expanded" )
+                    .writeText( assertionResult.getExpandedExpression() );
+            }
+
+            // And... Print a result applicable to each result type.
+            switch( assertionResult.getResultType() ) {
+                case ResultWas::ThrewException:
+                    m_xml.startElement( "Exception" );
+                    writeSourceInfo( assertionResult.getSourceInfo() );
+                    m_xml.writeText( assertionResult.getMessage() );
+                    m_xml.endElement();
+                    break;
+                case ResultWas::FatalErrorCondition:
+                    m_xml.startElement( "FatalErrorCondition" );
+                    writeSourceInfo( assertionResult.getSourceInfo() );
+                    m_xml.writeText( assertionResult.getMessage() );
+                    m_xml.endElement();
+                    break;
+                case ResultWas::Info:
+                    m_xml.scopedElement( "Info" )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                case ResultWas::Warning:
+                    // Warning will already have been written
+                    break;
+                case ResultWas::ExplicitFailure:
+                    m_xml.startElement( "Failure" );
+                    writeSourceInfo( assertionResult.getSourceInfo() );
+                    m_xml.writeText( assertionResult.getMessage() );
+                    m_xml.endElement();
+                    break;
+                default:
+                    break;
+            }
+
+            if( assertionResult.hasExpression() )
+                m_xml.endElement();
+
+            return true;
+        }
+
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::sectionEnded( sectionStats );
+            if( --m_sectionDepth > 0 ) {
+                XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+                e.writeAttribute( "successes", sectionStats.assertions.passed );
+                e.writeAttribute( "failures", sectionStats.assertions.failed );
+                e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+
+                if ( m_config->showDurations() == ShowDurations::Always )
+                    e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+                m_xml.endElement();
+            }
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::testCaseEnded( testCaseStats );
+            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+            e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+            if( !testCaseStats.stdOut.empty() )
+                m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
+            if( !testCaseStats.stdErr.empty() )
+                m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
+
+            m_xml.endElement();
+        }
+
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::testGroupEnded( testGroupStats );
+            // TODO: Check testGroupStats.aborting and act accordingly.
+            m_xml.scopedElement( "OverallResults" )
+                .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+                .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+                .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+            m_xml.endElement();
+        }
+
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::testRunEnded( testRunStats );
+            m_xml.scopedElement( "OverallResults" )
+                .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+                .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+                .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+            m_xml.endElement();
+        }
+
+    private:
+        Timer m_testCaseTimer;
+        XmlWriter m_xml;
+        int m_sectionDepth;
+    };
+
+     INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+    namespace {
+        std::string getCurrentTimestamp() {
+            // Beware, this is not reentrant because of backward compatibility issues
+            // Also, UTC only, again because of backward compatibility (%z is C++11)
+            time_t rawtime;
+            std::time(&rawtime);
+            const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+#ifdef _MSC_VER
+            std::tm timeInfo = {};
+            gmtime_s(&timeInfo, &rawtime);
+#else
+            std::tm* timeInfo;
+            timeInfo = std::gmtime(&rawtime);
+#endif
+
+            char timeStamp[timeStampSize];
+            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+            std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+            return std::string(timeStamp);
+        }
+
+    }
+
+    class JunitReporter : public CumulativeReporterBase {
+    public:
+        JunitReporter( ReporterConfig const& _config )
+        :   CumulativeReporterBase( _config ),
+            xml( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
+
+        virtual ~JunitReporter() CATCH_OVERRIDE;
+
+        static std::string getDescription() {
+            return "Reports test results in an XML format that looks like Ant's junitreport target";
+        }
+
+        virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {}
+
+        virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
+            CumulativeReporterBase::testRunStarting( runInfo );
+            xml.startElement( "testsuites" );
+        }
+
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+            suiteTimer.start();
+            stdOutForSuite.str("");
+            stdErrForSuite.str("");
+            unexpectedExceptions = 0;
+            CumulativeReporterBase::testGroupStarting( groupInfo );
+        }
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+            if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
+                unexpectedExceptions++;
+            return CumulativeReporterBase::assertionEnded( assertionStats );
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+            stdOutForSuite << testCaseStats.stdOut;
+            stdErrForSuite << testCaseStats.stdErr;
+            CumulativeReporterBase::testCaseEnded( testCaseStats );
+        }
+
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+            double suiteTime = suiteTimer.getElapsedSeconds();
+            CumulativeReporterBase::testGroupEnded( testGroupStats );
+            writeGroup( *m_testGroups.back(), suiteTime );
+        }
+
+        virtual void testRunEndedCumulative() CATCH_OVERRIDE {
+            xml.endElement();
+        }
+
+        void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+            XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+            TestGroupStats const& stats = groupNode.value;
+            xml.writeAttribute( "name", stats.groupInfo.name );
+            xml.writeAttribute( "errors", unexpectedExceptions );
+            xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+            xml.writeAttribute( "tests", stats.totals.assertions.total() );
+            xml.writeAttribute( "hostname", "tbd" ); // !TBD
+            if( m_config->showDurations() == ShowDurations::Never )
+                xml.writeAttribute( "time", "" );
+            else
+                xml.writeAttribute( "time", suiteTime );
+            xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+
+            // Write test cases
+            for( TestGroupNode::ChildNodes::const_iterator
+                    it = groupNode.children.begin(), itEnd = groupNode.children.end();
+                    it != itEnd;
+                    ++it )
+                writeTestCase( **it );
+
+            xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+            xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+        }
+
+        void writeTestCase( TestCaseNode const& testCaseNode ) {
+            TestCaseStats const& stats = testCaseNode.value;
+
+            // All test cases have exactly one section - which represents the
+            // test case itself. That section may have 0-n nested sections
+            assert( testCaseNode.children.size() == 1 );
+            SectionNode const& rootSection = *testCaseNode.children.front();
+
+            std::string className = stats.testInfo.className;
+
+            if( className.empty() ) {
+                if( rootSection.childSections.empty() )
+                    className = "global";
+            }
+            writeSection( className, "", rootSection );
+        }
+
+        void writeSection(  std::string const& className,
+                            std::string const& rootName,
+                            SectionNode const& sectionNode ) {
+            std::string name = trim( sectionNode.stats.sectionInfo.name );
+            if( !rootName.empty() )
+                name = rootName + '/' + name;
+
+            if( !sectionNode.assertions.empty() ||
+                !sectionNode.stdOut.empty() ||
+                !sectionNode.stdErr.empty() ) {
+                XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+                if( className.empty() ) {
+                    xml.writeAttribute( "classname", name );
+                    xml.writeAttribute( "name", "root" );
+                }
+                else {
+                    xml.writeAttribute( "classname", className );
+                    xml.writeAttribute( "name", name );
+                }
+                xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
+
+                writeAssertions( sectionNode );
+
+                if( !sectionNode.stdOut.empty() )
+                    xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+                if( !sectionNode.stdErr.empty() )
+                    xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+            }
+            for( SectionNode::ChildSections::const_iterator
+                    it = sectionNode.childSections.begin(),
+                    itEnd = sectionNode.childSections.end();
+                    it != itEnd;
+                    ++it )
+                if( className.empty() )
+                    writeSection( name, "", **it );
+                else
+                    writeSection( className, name, **it );
+        }
+
+        void writeAssertions( SectionNode const& sectionNode ) {
+            for( SectionNode::Assertions::const_iterator
+                    it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
+                    it != itEnd;
+                    ++it )
+                writeAssertion( *it );
+        }
+        void writeAssertion( AssertionStats const& stats ) {
+            AssertionResult const& result = stats.assertionResult;
+            if( !result.isOk() ) {
+                std::string elementName;
+                switch( result.getResultType() ) {
+                    case ResultWas::ThrewException:
+                    case ResultWas::FatalErrorCondition:
+                        elementName = "error";
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        elementName = "failure";
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        elementName = "failure";
+                        break;
+                    case ResultWas::DidntThrowException:
+                        elementName = "failure";
+                        break;
+
+                    // We should never see these here:
+                    case ResultWas::Info:
+                    case ResultWas::Warning:
+                    case ResultWas::Ok:
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        elementName = "internalError";
+                        break;
+                }
+
+                XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+                xml.writeAttribute( "message", result.getExpandedExpression() );
+                xml.writeAttribute( "type", result.getTestMacroName() );
+
+                std::ostringstream oss;
+                if( !result.getMessage().empty() )
+                    oss << result.getMessage() << '\n';
+                for( std::vector<MessageInfo>::const_iterator
+                        it = stats.infoMessages.begin(),
+                        itEnd = stats.infoMessages.end();
+                            it != itEnd;
+                            ++it )
+                    if( it->type == ResultWas::Info )
+                        oss << it->message << '\n';
+
+                oss << "at " << result.getSourceInfo();
+                xml.writeText( oss.str(), false );
+            }
+        }
+
+        XmlWriter xml;
+        Timer suiteTimer;
+        std::ostringstream stdOutForSuite;
+        std::ostringstream stdErrForSuite;
+        unsigned int unexpectedExceptions;
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+#include <cfloat>
+#include <cstdio>
+
+namespace Catch {
+
+    namespace {
+        // Because formatting using c++ streams is stateful, drop down to C is required
+        // Alternatively we could use stringstream, but its performance is... not good.
+        std::string getFormattedDuration( double duration ) {
+            // Max exponent + 1 is required to represent the whole part
+            // + 1 for decimal point
+            // + 3 for the 3 decimal places
+            // + 1 for null terminator
+            const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+            char buffer[maxDoubleSize];
+#ifdef _MSC_VER
+            sprintf_s(buffer, "%.3f", duration);
+#else
+            sprintf(buffer, "%.3f", duration);
+#endif
+            return std::string(buffer);
+        }
+    }
+
+    struct ConsoleReporter : StreamingReporterBase {
+        ConsoleReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config ),
+            m_headerPrinted( false )
+        {}
+
+        virtual ~ConsoleReporter() CATCH_OVERRIDE;
+        static std::string getDescription() {
+            return "Reports test results as plain lines of text";
+        }
+
+        virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+            stream << "No test cases matched '" << spec << '\'' << std::endl;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
+        }
+
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // Drop out if result was successful and we're not printing those
+            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+                if( result.getResultType() != ResultWas::Warning )
+                    return false;
+                printInfoMessages = false;
+            }
+
+            lazyPrint();
+
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
+            stream << std::endl;
+            return true;
+        }
+
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+            m_headerPrinted = false;
+            StreamingReporterBase::sectionStarting( _sectionInfo );
+        }
+        virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
+            if( _sectionStats.missingAssertions ) {
+                lazyPrint();
+                Colour colour( Colour::ResultError );
+                if( m_sectionStack.size() > 1 )
+                    stream << "\nNo assertions in section";
+                else
+                    stream << "\nNo assertions in test case";
+                stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+            }
+            if( m_config->showDurations() == ShowDurations::Always ) {
+                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+            }
+            if( m_headerPrinted ) {
+                m_headerPrinted = false;
+            }
+            StreamingReporterBase::sectionEnded( _sectionStats );
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::testCaseEnded( _testCaseStats );
+            m_headerPrinted = false;
+        }
+        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
+            if( currentGroupInfo.used ) {
+                printSummaryDivider();
+                stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+                printTotals( _testGroupStats.totals );
+                stream << '\n' << std::endl;
+            }
+            StreamingReporterBase::testGroupEnded( _testGroupStats );
+        }
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE {
+            printTotalsDivider( _testRunStats.totals );
+            printTotals( _testRunStats.totals );
+            stream << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+    private:
+
+        class AssertionPrinter {
+            void operator= ( AssertionPrinter const& );
+        public:
+            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+            :   stream( _stream ),
+                stats( _stats ),
+                result( _stats.assertionResult ),
+                colour( Colour::None ),
+                message( result.getMessage() ),
+                messages( _stats.infoMessages ),
+                printInfoMessages( _printInfoMessages )
+            {
+                switch( result.getResultType() ) {
+                    case ResultWas::Ok:
+                        colour = Colour::Success;
+                        passOrFail = "PASSED";
+                        //if( result.hasMessage() )
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "with messages";
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        if( result.isOk() ) {
+                            colour = Colour::Success;
+                            passOrFail = "FAILED - but was ok";
+                        }
+                        else {
+                            colour = Colour::Error;
+                            passOrFail = "FAILED";
+                        }
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "with messages";
+                        break;
+                    case ResultWas::ThrewException:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "due to unexpected exception with message";
+                        break;
+                    case ResultWas::FatalErrorCondition:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "due to a fatal error condition";
+                        break;
+                    case ResultWas::DidntThrowException:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "because no exception was thrown where one was expected";
+                        break;
+                    case ResultWas::Info:
+                        messageLabel = "info";
+                        break;
+                    case ResultWas::Warning:
+                        messageLabel = "warning";
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        passOrFail = "FAILED";
+                        colour = Colour::Error;
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "explicitly with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "explicitly with messages";
+                        break;
+                    // These cases are here to prevent compiler warnings
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        passOrFail = "** internal error **";
+                        colour = Colour::Error;
+                        break;
+                }
+            }
+
+            void print() const {
+                printSourceInfo();
+                if( stats.totals.assertions.total() > 0 ) {
+                    if( result.isOk() )
+                        stream << '\n';
+                    printResultType();
+                    printOriginalExpression();
+                    printReconstructedExpression();
+                }
+                else {
+                    stream << '\n';
+                }
+                printMessage();
+            }
+
+        private:
+            void printResultType() const {
+                if( !passOrFail.empty() ) {
+                    Colour colourGuard( colour );
+                    stream << passOrFail << ":\n";
+                }
+            }
+            void printOriginalExpression() const {
+                if( result.hasExpression() ) {
+                    Colour colourGuard( Colour::OriginalExpression );
+                    stream  << "  ";
+                    stream << result.getExpressionInMacro();
+                    stream << '\n';
+                }
+            }
+            void printReconstructedExpression() const {
+                if( result.hasExpandedExpression() ) {
+                    stream << "with expansion:\n";
+                    Colour colourGuard( Colour::ReconstructedExpression );
+                    stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n';
+                }
+            }
+            void printMessage() const {
+                if( !messageLabel.empty() )
+                    stream << messageLabel << ':' << '\n';
+                for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
+                        it != itEnd;
+                        ++it ) {
+                    // If this assertion is a warning ignore any INFO messages
+                    if( printInfoMessages || it->type != ResultWas::Info )
+                        stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n';
+                }
+            }
+            void printSourceInfo() const {
+                Colour colourGuard( Colour::FileName );
+                stream << result.getSourceInfo() << ": ";
+            }
+
+            std::ostream& stream;
+            AssertionStats const& stats;
+            AssertionResult const& result;
+            Colour::Code colour;
+            std::string passOrFail;
+            std::string messageLabel;
+            std::string message;
+            std::vector<MessageInfo> messages;
+            bool printInfoMessages;
+        };
+
+        void lazyPrint() {
+
+            if( !currentTestRunInfo.used )
+                lazyPrintRunInfo();
+            if( !currentGroupInfo.used )
+                lazyPrintGroupInfo();
+
+            if( !m_headerPrinted ) {
+                printTestCaseAndSectionHeader();
+                m_headerPrinted = true;
+            }
+        }
+        void lazyPrintRunInfo() {
+            stream  << '\n' << getLineOfChars<'~'>() << '\n';
+            Colour colour( Colour::SecondaryText );
+            stream  << currentTestRunInfo->name
+                    << " is a Catch v"  << libraryVersion << " host application.\n"
+                    << "Run with -? for options\n\n";
+
+            if( m_config->rngSeed() != 0 )
+                stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+            currentTestRunInfo.used = true;
+        }
+        void lazyPrintGroupInfo() {
+            if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
+                printClosedHeader( "Group: " + currentGroupInfo->name );
+                currentGroupInfo.used = true;
+            }
+        }
+        void printTestCaseAndSectionHeader() {
+            assert( !m_sectionStack.empty() );
+            printOpenHeader( currentTestCaseInfo->name );
+
+            if( m_sectionStack.size() > 1 ) {
+                Colour colourGuard( Colour::Headers );
+
+                std::vector<SectionInfo>::const_iterator
+                    it = m_sectionStack.begin()+1, // Skip first section (test case)
+                    itEnd = m_sectionStack.end();
+                for( ; it != itEnd; ++it )
+                    printHeaderString( it->name, 2 );
+            }
+
+            SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+
+            if( !lineInfo.empty() ){
+                stream << getLineOfChars<'-'>() << '\n';
+                Colour colourGuard( Colour::FileName );
+                stream << lineInfo << '\n';
+            }
+            stream << getLineOfChars<'.'>() << '\n' << std::endl;
+        }
+
+        void printClosedHeader( std::string const& _name ) {
+            printOpenHeader( _name );
+            stream << getLineOfChars<'.'>() << '\n';
+        }
+        void printOpenHeader( std::string const& _name ) {
+            stream  << getLineOfChars<'-'>() << '\n';
+            {
+                Colour colourGuard( Colour::Headers );
+                printHeaderString( _name );
+            }
+        }
+
+        // if string has a : in first line will set indent to follow it on
+        // subsequent lines
+        void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
+            std::size_t i = _string.find( ": " );
+            if( i != std::string::npos )
+                i+=2;
+            else
+                i = 0;
+            stream << Text( _string, TextAttributes()
+                                        .setIndent( indent+i)
+                                        .setInitialIndent( indent ) ) << '\n';
+        }
+
+        struct SummaryColumn {
+
+            SummaryColumn( std::string const& _label, Colour::Code _colour )
+            :   label( _label ),
+                colour( _colour )
+            {}
+            SummaryColumn addRow( std::size_t count ) {
+                std::ostringstream oss;
+                oss << count;
+                std::string row = oss.str();
+                for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
+                    while( it->size() < row.size() )
+                        *it = ' ' + *it;
+                    while( it->size() > row.size() )
+                        row = ' ' + row;
+                }
+                rows.push_back( row );
+                return *this;
+            }
+
+            std::string label;
+            Colour::Code colour;
+            std::vector<std::string> rows;
+
+        };
+
+        void printTotals( Totals const& totals ) {
+            if( totals.testCases.total() == 0 ) {
+                stream << Colour( Colour::Warning ) << "No tests ran\n";
+            }
+            else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
+                stream << Colour( Colour::ResultSuccess ) << "All tests passed";
+                stream << " ("
+                        << pluralise( totals.assertions.passed, "assertion" ) << " in "
+                        << pluralise( totals.testCases.passed, "test case" ) << ')'
+                        << '\n';
+            }
+            else {
+
+                std::vector<SummaryColumn> columns;
+                columns.push_back( SummaryColumn( "", Colour::None )
+                                        .addRow( totals.testCases.total() )
+                                        .addRow( totals.assertions.total() ) );
+                columns.push_back( SummaryColumn( "passed", Colour::Success )
+                                        .addRow( totals.testCases.passed )
+                                        .addRow( totals.assertions.passed ) );
+                columns.push_back( SummaryColumn( "failed", Colour::ResultError )
+                                        .addRow( totals.testCases.failed )
+                                        .addRow( totals.assertions.failed ) );
+                columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
+                                        .addRow( totals.testCases.failedButOk )
+                                        .addRow( totals.assertions.failedButOk ) );
+
+                printSummaryRow( "test cases", columns, 0 );
+                printSummaryRow( "assertions", columns, 1 );
+            }
+        }
+        void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
+            for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
+                std::string value = it->rows[row];
+                if( it->label.empty() ) {
+                    stream << label << ": ";
+                    if( value != "0" )
+                        stream << value;
+                    else
+                        stream << Colour( Colour::Warning ) << "- none -";
+                }
+                else if( value != "0" ) {
+                    stream  << Colour( Colour::LightGrey ) << " | ";
+                    stream  << Colour( it->colour )
+                            << value << ' ' << it->label;
+                }
+            }
+            stream << '\n';
+        }
+
+        static std::size_t makeRatio( std::size_t number, std::size_t total ) {
+            std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
+            return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+        }
+        static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+            if( i > j && i > k )
+                return i;
+            else if( j > k )
+                return j;
+            else
+                return k;
+        }
+
+        void printTotalsDivider( Totals const& totals ) {
+            if( totals.testCases.total() > 0 ) {
+                std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
+                std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
+                std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
+                while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
+                    findMax( failedRatio, failedButOkRatio, passedRatio )++;
+                while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
+                    findMax( failedRatio, failedButOkRatio, passedRatio )--;
+
+                stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
+                stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
+                if( totals.testCases.allPassed() )
+                    stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
+                else
+                    stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
+            }
+            else {
+                stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
+            }
+            stream << '\n';
+        }
+        void printSummaryDivider() {
+            stream << getLineOfChars<'-'>() << '\n';
+        }
+
+    private:
+        bool m_headerPrinted;
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+    struct CompactReporter : StreamingReporterBase {
+
+        CompactReporter( ReporterConfig const& _config )
+        : StreamingReporterBase( _config )
+        {}
+
+        virtual ~CompactReporter();
+
+        static std::string getDescription() {
+            return "Reports test results on a single line, suitable for IDEs";
+        }
+
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = false;
+            return prefs;
+        }
+
+        virtual void noMatchingTestCases( std::string const& spec ) {
+            stream << "No test cases matched '" << spec << '\'' << std::endl;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) {
+        }
+
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // Drop out if result was successful and we're not printing those
+            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+                if( result.getResultType() != ResultWas::Warning )
+                    return false;
+                printInfoMessages = false;
+            }
+
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
+
+            stream << std::endl;
+            return true;
+        }
+
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotals( _testRunStats.totals );
+            stream << '\n' << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+    private:
+        class AssertionPrinter {
+            void operator= ( AssertionPrinter const& );
+        public:
+            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+            : stream( _stream )
+            , stats( _stats )
+            , result( _stats.assertionResult )
+            , messages( _stats.infoMessages )
+            , itMessage( _stats.infoMessages.begin() )
+            , printInfoMessages( _printInfoMessages )
+            {}
+
+            void print() {
+                printSourceInfo();
+
+                itMessage = messages.begin();
+
+                switch( result.getResultType() ) {
+                    case ResultWas::Ok:
+                        printResultType( Colour::ResultSuccess, passedString() );
+                        printOriginalExpression();
+                        printReconstructedExpression();
+                        if ( ! result.hasExpression() )
+                            printRemainingMessages( Colour::None );
+                        else
+                            printRemainingMessages();
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        if( result.isOk() )
+                            printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
+                        else
+                            printResultType( Colour::Error, failedString() );
+                        printOriginalExpression();
+                        printReconstructedExpression();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::ThrewException:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "unexpected exception with message:" );
+                        printMessage();
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::FatalErrorCondition:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "fatal error condition with message:" );
+                        printMessage();
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::DidntThrowException:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "expected exception, got none" );
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::Info:
+                        printResultType( Colour::None, "info" );
+                        printMessage();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::Warning:
+                        printResultType( Colour::None, "warning" );
+                        printMessage();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "explicitly" );
+                        printRemainingMessages( Colour::None );
+                        break;
+                    // These cases are here to prevent compiler warnings
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        printResultType( Colour::Error, "** internal error **" );
+                        break;
+                }
+            }
+
+        private:
+            // Colour::LightGrey
+
+            static Colour::Code dimColour() { return Colour::FileName; }
+
+#ifdef CATCH_PLATFORM_MAC
+            static const char* failedString() { return "FAILED"; }
+            static const char* passedString() { return "PASSED"; }
+#else
+            static const char* failedString() { return "failed"; }
+            static const char* passedString() { return "passed"; }
+#endif
+
+            void printSourceInfo() const {
+                Colour colourGuard( Colour::FileName );
+                stream << result.getSourceInfo() << ':';
+            }
+
+            void printResultType( Colour::Code colour, std::string passOrFail ) const {
+                if( !passOrFail.empty() ) {
+                    {
+                        Colour colourGuard( colour );
+                        stream << ' ' << passOrFail;
+                    }
+                    stream << ':';
+                }
+            }
+
+            void printIssue( std::string issue ) const {
+                stream << ' ' << issue;
+            }
+
+            void printExpressionWas() {
+                if( result.hasExpression() ) {
+                    stream << ';';
+                    {
+                        Colour colour( dimColour() );
+                        stream << " expression was:";
+                    }
+                    printOriginalExpression();
+                }
+            }
+
+            void printOriginalExpression() const {
+                if( result.hasExpression() ) {
+                    stream << ' ' << result.getExpression();
+                }
+            }
+
+            void printReconstructedExpression() const {
+                if( result.hasExpandedExpression() ) {
+                    {
+                        Colour colour( dimColour() );
+                        stream << " for: ";
+                    }
+                    stream << result.getExpandedExpression();
+                }
+            }
+
+            void printMessage() {
+                if ( itMessage != messages.end() ) {
+                    stream << " '" << itMessage->message << '\'';
+                    ++itMessage;
+                }
+            }
+
+            void printRemainingMessages( Colour::Code colour = dimColour() ) {
+                if ( itMessage == messages.end() )
+                    return;
+
+                // using messages.end() directly yields compilation error:
+                std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+                const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+
+                {
+                    Colour colourGuard( colour );
+                    stream << " with " << pluralise( N, "message" ) << ':';
+                }
+
+                for(; itMessage != itEnd; ) {
+                    // If this assertion is a warning ignore any INFO messages
+                    if( printInfoMessages || itMessage->type != ResultWas::Info ) {
+                        stream << " '" << itMessage->message << '\'';
+                        if ( ++itMessage != itEnd ) {
+                            Colour colourGuard( dimColour() );
+                            stream << " and";
+                        }
+                    }
+                }
+            }
+
+        private:
+            std::ostream& stream;
+            AssertionStats const& stats;
+            AssertionResult const& result;
+            std::vector<MessageInfo> messages;
+            std::vector<MessageInfo>::const_iterator itMessage;
+            bool printInfoMessages;
+        };
+
+        // Colour, message variants:
+        // - white: No tests ran.
+        // -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
+        // - white: Passed [both/all] N test cases (no assertions).
+        // -   red: Failed N tests cases, failed M assertions.
+        // - green: Passed [both/all] N tests cases with M assertions.
+
+        std::string bothOrAll( std::size_t count ) const {
+            return count == 1 ? std::string() : count == 2 ? "both " : "all " ;
+        }
+
+        void printTotals( const Totals& totals ) const {
+            if( totals.testCases.total() == 0 ) {
+                stream << "No tests ran.";
+            }
+            else if( totals.testCases.failed == totals.testCases.total() ) {
+                Colour colour( Colour::ResultError );
+                const std::string qualify_assertions_failed =
+                    totals.assertions.failed == totals.assertions.total() ?
+                        bothOrAll( totals.assertions.failed ) : std::string();
+                stream <<
+                    "Failed " << bothOrAll( totals.testCases.failed )
+                              << pluralise( totals.testCases.failed, "test case"  ) << ", "
+                    "failed " << qualify_assertions_failed <<
+                                 pluralise( totals.assertions.failed, "assertion" ) << '.';
+            }
+            else if( totals.assertions.total() == 0 ) {
+                stream <<
+                    "Passed " << bothOrAll( totals.testCases.total() )
+                              << pluralise( totals.testCases.total(), "test case" )
+                              << " (no assertions).";
+            }
+            else if( totals.assertions.failed ) {
+                Colour colour( Colour::ResultError );
+                stream <<
+                    "Failed " << pluralise( totals.testCases.failed, "test case"  ) << ", "
+                    "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.';
+            }
+            else {
+                Colour colour( Colour::ResultSuccess );
+                stream <<
+                    "Passed " << bothOrAll( totals.testCases.passed )
+                              << pluralise( totals.testCases.passed, "test case"  ) <<
+                    " with "  << pluralise( totals.assertions.passed, "assertion" ) << '.';
+            }
+        }
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+    // These are all here to avoid warnings about not having any out of line
+    // virtual methods
+    NonCopyable::~NonCopyable() {}
+    IShared::~IShared() {}
+    IStream::~IStream() CATCH_NOEXCEPT {}
+    FileStream::~FileStream() CATCH_NOEXCEPT {}
+    CoutStream::~CoutStream() CATCH_NOEXCEPT {}
+    DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
+    StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
+    IContext::~IContext() {}
+    IResultCapture::~IResultCapture() {}
+    ITestCase::~ITestCase() {}
+    ITestCaseRegistry::~ITestCaseRegistry() {}
+    IRegistryHub::~IRegistryHub() {}
+    IMutableRegistryHub::~IMutableRegistryHub() {}
+    IExceptionTranslator::~IExceptionTranslator() {}
+    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
+    IReporter::~IReporter() {}
+    IReporterFactory::~IReporterFactory() {}
+    IReporterRegistry::~IReporterRegistry() {}
+    IStreamingReporter::~IStreamingReporter() {}
+    AssertionStats::~AssertionStats() {}
+    SectionStats::~SectionStats() {}
+    TestCaseStats::~TestCaseStats() {}
+    TestGroupStats::~TestGroupStats() {}
+    TestRunStats::~TestRunStats() {}
+    CumulativeReporterBase::SectionNode::~SectionNode() {}
+    CumulativeReporterBase::~CumulativeReporterBase() {}
+
+    StreamingReporterBase::~StreamingReporterBase() {}
+    ConsoleReporter::~ConsoleReporter() {}
+    CompactReporter::~CompactReporter() {}
+    IRunner::~IRunner() {}
+    IMutableContext::~IMutableContext() {}
+    IConfig::~IConfig() {}
+    XmlReporter::~XmlReporter() {}
+    JunitReporter::~JunitReporter() {}
+    TestRegistry::~TestRegistry() {}
+    FreeFunctionTestCase::~FreeFunctionTestCase() {}
+    IGeneratorInfo::~IGeneratorInfo() {}
+    IGeneratorsForTest::~IGeneratorsForTest() {}
+    WildcardPattern::~WildcardPattern() {}
+    TestSpec::Pattern::~Pattern() {}
+    TestSpec::NamePattern::~NamePattern() {}
+    TestSpec::TagPattern::~TagPattern() {}
+    TestSpec::ExcludedPattern::~ExcludedPattern() {}
+
+    void Config::dummy() {}
+
+    namespace TestCaseTracking {
+        ITracker::~ITracker() {}
+        TrackerBase::~TrackerBase() {}
+        SectionTracker::~SectionTracker() {}
+        IndexTracker::~IndexTracker() {}
+    }
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+// Standard C/C++ main entry point
+int main (int argc, char * argv[]) {
+	int result = Catch::Session().run( argc, argv );
+    return ( result < 0xff ? result : 0xff );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+    Catch::registerTestMethods();
+    int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+    [pool drain];
+#endif
+
+    return ( result < 0xff ? result : 0xff );
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+#  undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
+
+#define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CATCH_CHECK_THROWS" )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
+
+#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+    #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+    #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+    #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+    #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
+    #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
+#else
+    #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+    #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+    #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
+    #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+    #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
+    #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
+#endif
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
+#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define CATCH_GIVEN( desc )    CATCH_SECTION( std::string( "Given: ") + desc, "" )
+#define CATCH_WHEN( desc )     CATCH_SECTION( std::string( " When: ") + desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
+#define CATCH_THEN( desc )     CATCH_SECTION( std::string( " Then: ") + desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
+
+#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+    #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+    #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+    #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+    #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
+    #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
+#else
+    #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+    #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+    #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
+    #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+    #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
+    #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
+#endif
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
+#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define GIVEN( desc )    SECTION( std::string("   Given: ") + desc, "" )
+#define WHEN( desc )     SECTION( std::string("    When: ") + desc, "" )
+#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
+#define THEN( desc )     SECTION( std::string("    Then: ") + desc, "" )
+#define AND_THEN( desc ) SECTION( std::string("     And: ") + desc, "" )
+
+using Catch::Detail::Approx;
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/tests/main.cpp b/tests/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c7c351f437f5f43f3bb62beb254a9f1ecbec5a0
--- /dev/null
+++ b/tests/main.cpp
@@ -0,0 +1,2 @@
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"