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 ¢er, + double radius, double north, QPalette::ColorGroup ) const; + + virtual void drawScaleContents( QPainter *, + const QPointF ¢er, 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 ¢er, 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 ¢er, double radius, + double north, QPalette::ColorGroup = QPalette::Active ) const; + + static void drawRose( QPainter *, const QPalette &, + const QPointF ¢er, 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 ¢er, double radius ) const; + + virtual void drawScaleContents( QPainter *painter, + const QPointF ¢er, 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 ¢er, + 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 ¤t_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 "slice by slice" 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 "slice by slice" 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 "slice by slice" 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 "slice by slice" 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 "slice by slice" 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 ¢er, + 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 ¢er, 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 ¢er, 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 ¢er, 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 ¢er, + 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 ¢er, + 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 ¢er, 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 ¢er, + 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 ¢er, 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 ¢er, 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 ¢er, 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 ®ion ) +{ + 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 ¢er ) +{ + 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, ¤tTracker ); + 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, ¤tTracker, 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( §ionTracker ); + + 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 << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; + + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + 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"